fixed 优化商品sku(未完成,计划端午完成)

This commit is contained in:
lwh 2023-06-21 18:30:49 +08:00
parent 3170e8b9dd
commit 515b6afe62
6 changed files with 743 additions and 1180 deletions

View File

@ -156,69 +156,7 @@
<el-row v-if="formData.goodsSpecType == 2">
<el-col :lg="24">
<el-form-item :label-width="labelWidth" label="商品规格">
<div style="display: block;width: 100%;">
<!-- <el-input-number v-model.number="formData.goodsSpec" controls-position="right" :min="0.01"
:precision="2" /> -->
<div class="input-intro">最多添加3个商品规格组生成的SKU数量不能超出50个</div>
<!-- 规格样式 -->
<div class="sepc-big-box" v-for="(item, index) in goodsSpecList">
<div class="sepc-title-box">
<el-input v-model="item.specName" @change="handleChangeSepc(item, index)" class="spec-input"
placeholder="规格组名称" clearable />
<el-link type="primary" @click="handleDelSpec(item)">删除规格组</el-link>
</div>
<div class="spec-value-box">
<div class="spec-value-input-big-box">
<div class="spec-value-input-box" v-for="specValue in item.props">
<div class="spec-value-input-del" @click="handleDelSpecValue(item,specValue)"><el-icon>
<Close />
</el-icon></div>
<el-input v-model="specValue.specValueName" @change="handleChangeSepcValue(item,specValue)"
class="spec-value-input" placeholder="规格值名称" clearable />
</div>
</div>
<el-link type="primary" v-if="isAddSpecValueMax" @click="handleAddSpecValue(item)">添加规格值</el-link>
</div>
</div>
<el-button v-if="isAddSpecMax" class="mt-5" @click="handleAddSpec">
<el-icon class="avatar-uploader-icon">
<plus />
</el-icon>
添加规格组
</el-button>
</div>
</el-form-item>
<!-- Sku列表 -->
<el-form-item :label-width="labelWidth" label="Sku列表">
<!-- sku列表 -->
<el-table :data="skuList" border style="width: 100%" :span-method="objectSpanMethod">
<!-- 动态列 -->
<el-table-column v-for="column in skuColumns" :prop="column.prop" :label="column.label"
:key="column.key" :width="column.width">
</el-table-column>
<!-- <el-table-column prop="goodsSkuPrice" label="预览图">
<template #default="scope">
<UploadImage ref="uploadRef" v-model="scope.row.goodsSkuImg" :data=imgData :limit="1"
:fileSize="5" :drag="true" :isShowTip="false" />
</template>
</el-table-column> -->
<el-table-column prop="goodsSkuPrice" label="商品价格">
<template #default="scope">
<el-input-number v-model.number="scope.row.goodsSkuPrice" controls-position="right" :min="0"
:precision="2" />
</template>
</el-table-column>
</el-table>
</el-form-item>
<MultiSpec></MultiSpec>
</el-col>
</el-row>
@ -388,6 +326,7 @@ import { shopGoodsCategoryTreeList } from '@/api/business/GoodsManager/ShopGoods
import { deliveryList } from '@/api/business/LogisticsManage/Deliverys/delivery.js'
import useUserStore from '@/store/modules/user'
import ChooseShopDialog from './ChooseShopDialog.vue';
import MultiSpec from './MultiSpec/MultiSpec.vue';
//

View File

@ -1,286 +0,0 @@
<template>
<el-form-item :label-width="labelWidth" label="商品规格">
<div style="display: block;width: 100%;">
<div class="input-intro">最多添加3个商品规格组生成的SKU数量不能超出50个</div>
<!-- 规格样式 -->
<div class="sepc-big-box" v-for="(item, index) in multiSpecData.specList">
<div class="sepc-title-box">
<el-input v-model="item.spec_name" @change="onChangeSpecGroupIpt" class="spec-input" placeholder="规格组名称"
clearable />
<el-link type="primary" @click="handleDeleteSpecGroup(index)">删除规格组</el-link>
</div>
<div class="spec-value-box">
<div class="spec-value-input-big-box">
<div class="spec-value-input-box" v-for="(itm, idx) in item.valueList" :key="idx">
<div class="spec-value-input-del" @click="handleDeleteSpecValue(index, idx)"><el-icon>
<Close />
</el-icon></div>
<el-input v-model="specValue.specValueName" @change="onChangeSpecValueIpt"
class="spec-value-input" placeholder="规格值名称" clearable />
</div>
</div>
<el-link type="primary" v-if="!isSpecLocked" @click="handleAddSpecValue(index)">添加规格值</el-link>
</div>
</div>
<el-button v-if="!isSpecLocked && multiSpecData.specList.length < 3" class="mt-5" @click="handleAddSpecGroup">
<el-icon class="avatar-uploader-icon">
<plus />
</el-icon>
添加规格组
</el-button>
</div>
</el-form-item>
<!-- Sku列表 -->
<el-form-item :label-width="labelWidth" label="Sku列表">
<!-- sku列表 -->
<el-table :data="skuList" border style="width: 100%" :span-method="objectSpanMethod">
<!-- 动态列 -->
<el-table-column v-for="column in skuColumns" :prop="column.prop" :label="column.label" :key="column.key"
:width="column.width">
</el-table-column>
<!-- <el-table-column prop="goodsSkuPrice" label="预览图">
<template #default="scope">
<UploadImage ref="uploadRef" v-model="scope.row.goodsSkuImg" :data=imgData :limit="1"
:fileSize="5" :drag="true" :isShowTip="false" />
</template>
</el-table-column> -->
<el-table-column prop="goodsSkuPrice" label="商品价格">
<template #default="scope">
<el-input-number v-model.number="scope.row.goodsSkuPrice" controls-position="right" :min="0" :precision="2" />
</template>
</el-table-column>
</el-table>
</el-form-item>
</template>
<script>
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import MultiSpecModel from '@/common/model/goods/MultiSpec'
import { SelectImage } from '@/components'
export default {
components: {
SelectImage
},
props: {
//
defaultSpecList: PropTypes.array.def([]),
// SKU
defaultSkuList: PropTypes.array.def([]),
// ()
isSpecLocked: PropTypes.bool.def(false)
},
data() {
return {
//
labelCol: { span: 3 },
//
wrapperCol: { span: 21 },
//
MultiSpecModel: new MultiSpecModel(),
// MultiSpecModel: Object,
multiSpecData: {
//
specList: [],
// SKU
skuList: []
}
}
},
watch: {
defaultSpecList(val) {
if (val.length && this.MultiSpecModel.isEmpty()) {
this.getData()
}
}
},
//
created() {
// SKU
this.getData()
},
methods: {
// SKU()
getData() {
const { defaultSpecList, defaultSkuList } = this
this.multiSpecData = this.MultiSpecModel.getData(defaultSpecList, defaultSkuList)
},
// SKU()
getFromSpecData() {
return this.MultiSpecModel.getFromSpecData()
},
//
handleAddSpecGroup() {
if (this.checkSkuMaxNum()) {
this.MultiSpecModel.handleAddSpecGroup()
}
},
//
handleDeleteSpecGroup(groupIndex) {
const app = this
const modal = this.$confirm({
title: '您确定要删除该规格组吗?',
content: '删除后不可恢复',
onOk() {
//
app.MultiSpecModel.handleDeleteSpecGroup(groupIndex)
//
modal.destroy()
}
})
},
//
handleAddSpecValue(groupIndex) {
if (this.checkSkuMaxNum()) {
this.MultiSpecModel.handleAddSpecValue(groupIndex)
}
},
//
handleDeleteSpecValue(groupIndex, valueIndex) {
const app = this
const modal = this.$confirm({
title: '您确定要删除该规格值吗?',
content: '删除后不可恢复',
onOk() {
//
app.MultiSpecModel.handleDeleteSpecValue(groupIndex, valueIndex)
//
modal.destroy()
}
})
},
// change
onChangeSpecGroupIpt() {
// skuList
this.MultiSpecModel.onUpdate(true)
},
// change
onChangeSpecValueIpt(event, itm) {
// skuList
this.MultiSpecModel.onUpdate(true)
},
// sku
checkSkuMaxNum() {
const skuList = this.multiSpecData.skuList
if (skuList.length >= 50) {
this.$message.error(`生成的sku列表数量不能大于50个当前数量${skuList.length}`, 2.5)
return false
}
return true
},
// sku
handleSkuBatch() {
this.MultiSpecModel.handleSkuBatch()
},
//
verifyForm() {
if (!this.MultiSpecModel.verifyForm()) {
this.$message.error(this.MultiSpecModel.getError(), 2)
return false
}
return true
}
}
}
</script>
<style lang="less" scoped>
.divider-title {
font-size: 18px;
}
.input-intro {
color: #8c8c8c;
font-size: 12px;
}
.el-divider {
margin: 50px 0;
}
.sepc-big-box {
width: 100%;
margin-bottom: 20px;
}
.sepc-title-box {
width: 100%;
padding: 10px;
background-color: #f4f4f5;
display: flex;
justify-content: space-between;
}
.spec-value-box {
width: 100%;
padding: 20px 20px 20px 40px;
background-color: #fbfbfb;
display: flex;
flex-wrap: wrap;
}
.spec-value-input-big-box {
display: flex;
flex-wrap: wrap;
}
.spec-value-input-box {
width: 250px;
position: relative;
margin-right: 30px;
margin-bottom: 15px;
}
.spec-value-input-del {
position: absolute;
top: -5px;
right: -5px;
z-index: 99;
width: 18px;
height: 18px;
background-color: grey;
color: white;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
cursor: pointer;
}
.spec-input {
width: 250px;
margin-right: 30px;
}
.mt-5 {
margin-top: 10px;
}
.el-input-number {
width: 140px;
}
</style>

View File

@ -1,446 +0,0 @@
import _ from 'lodash'
import { debounce, isEmpty } from '@/utils/util'
// 默认的sku字段属性
const defaultColumns = [
{
title: '预览图',
dataIndex: 'image',
width: 90,
scopedSlots: { customRender: 'image' }
},
{
title: '商品价格',
dataIndex: 'goods_price',
width: 120,
scopedSlots: { customRender: 'goods_price' }
},
{
title: '划线价格',
dataIndex: 'line_price',
width: 120,
scopedSlots: { customRender: 'line_price' }
},
{
title: '库存数量',
dataIndex: 'stock_num',
width: 120,
scopedSlots: { customRender: 'stock_num' }
},
{
title: '商品重量 (KG)',
dataIndex: 'goods_weight',
width: 120,
scopedSlots: { customRender: 'goods_weight' }
},
{
title: 'SKU编码',
dataIndex: 'goods_sku_no',
width: 140,
scopedSlots: { customRender: 'goods_sku_no' }
}
]
// 默认的sku记录值
const defaultSkuItemData = {
image_id: 0,
image: {},
// imageList: [],
goods_price: '',
line_price: '',
stock_num: '',
goods_weight: '',
goods_sku_no: ''
}
// const demoSpecList = [
// {
// key: 0,
// spec_name: '颜色',
// valueList: [
// { key: 0, groupKey: 0, /* spec_value_id: 10001, */ spec_value: '红色' },
// { key: 1, groupKey: 0, spec_value: '白色' },
// { key: 2, groupKey: 0, spec_value: '蓝色' }
// ]
// },
// {
// key: 1,
// spec_name: '尺码',
// valueList: [
// { key: 0, groupKey: 1, spec_value: 'XXL' },
// { key: 1, groupKey: 1, spec_value: 'XL' }
// ]
// }
// ]
/**
* 商品 model类
* GoodsModel
*/
export default class MultiSpec {
// 商品多规格数据
multiSpecData = {}
// 错误信息
error = '';
/**
* 构造方法
* @param {array} specList 规格列表
* @param {array} skuList SKU列表
*/
constructor () {
this.multiSpecData = {
// 规格列表
specList: [],
// SKU列表
skuList: [],
// SKU字段
skuColumns: _.cloneDeep(defaultColumns),
// 批量设置sku
skuBatchForm: _.cloneDeep(defaultSkuItemData)
}
}
// 生成并获取多规格数据
getData (specList = [], skuList = []) {
if (specList.length) {
this.multiSpecData.specList = _.cloneDeep(specList)
this.multiSpecData.skuList = _.cloneDeep(skuList)
}
// 整理所有的规格组
const specGroupArr = this.specGroupArr()
// sku记录的规格属性集(生成笛卡尔积)
const cartesianList = cartesianProductOf(specGroupArr)
// 合并单元格
const rowSpanArr = this.rowSpanArr(specGroupArr, cartesianList)
// 生成sku字段名
this.buildSkuColumns(rowSpanArr)
// 生成sku列表数据
this.buildSkuList(cartesianList)
// 返回多规格数据
return this.multiSpecData
}
// 数据是否为空
isEmpty () {
return this.multiSpecData.specList.length === 0
}
// 返回错误信息
getError () {
return this.error
}
// 整理所有的规格
specGroupArr () {
const specGroupArr = []
this.multiSpecData.specList.forEach(specGroup => {
const itemArr = []
specGroup.valueList.forEach(value => {
itemArr.push(value)
})
specGroupArr.push(itemArr)
})
return specGroupArr
}
// 合并单元格
rowSpanArr (specGroupArr, cartesianList) {
const rowSpanArr = []
var rowSpan = cartesianList.length
for (let i = 0; i < specGroupArr.length; i++) {
rowSpanArr[i] = parseInt(rowSpan / specGroupArr[i].length)
rowSpan = rowSpanArr[i]
}
return rowSpanArr
}
// 生成skuList
buildSkuList (cartesianList) {
// 生成新的skuList
const newSkuList = []
for (let i = 0; i < cartesianList.length; i++) {
const newSkuItem = {
...defaultSkuItemData,
key: i,
// skuKey用于合并旧记录
skuKey: cartesianList[i].map(item => item.key).join('_'),
// skuKeys用于传参给后端
skuKeys: cartesianList[i].map(item => {
return {
groupKey: item.groupKey,
valueKey: item.key
}
})
}
cartesianList[i].forEach((val, idx) => {
newSkuItem[`spec_value_${idx}`] = val.spec_value
})
newSkuList.push(newSkuItem)
}
// 兼容旧的sku数据
this.multiSpecData.skuList = this.oldSkuList(newSkuList)
}
// 合并已存在的sku数据
oldSkuList (newSkuList) {
// const oldSkuList = _.cloneDeep(this.multiSpecData.skuList)
const oldSkuList = this.multiSpecData.skuList.concat()
if (!oldSkuList.length || !newSkuList.length) {
return newSkuList
}
for (const index in newSkuList) {
// 查找符合的旧记录
let oldSkuItem = {}
if (oldSkuList.length === newSkuList.length) {
oldSkuItem = _.cloneDeep(oldSkuList[index])
} else {
oldSkuItem = oldSkuList.find(item => {
return item.skuKey === newSkuList[index].skuKey
})
}
// 写入新纪录
if (oldSkuItem) {
newSkuList[index] = {
...newSkuList[index],
..._.pick(oldSkuItem, Object.keys(defaultSkuItemData))
}
// console.log(newSkuList[index].image)
}
}
return newSkuList
}
// 生成sku表格字段名
buildSkuColumns (rowSpanArr) {
const specList = this.multiSpecData.specList
const newColumns = defaultColumns.concat()
// 渲染字段的rowSpan
const customRender = (specIndex, value, row, index) => {
const obj = {
children: value,
attrs: {}
}
const rowSpan = rowSpanArr[specIndex - 1]
if ((index % rowSpan) === 0) {
obj.attrs.rowSpan = rowSpan
} else {
obj.attrs.rowSpan = 0
}
return obj
}
// 遍历规格组整理字段
for (let specIndex = specList.length; specIndex > 0; specIndex--) {
const specGroupItem = specList[specIndex - 1]
newColumns.unshift({
title: specGroupItem.spec_name,
dataIndex: `spec_value_${specIndex - 1}`,
customRender: (value, row, index) => customRender(specIndex, value, row, index)
})
}
this.multiSpecData.skuColumns = newColumns
}
// 添加规格组
handleAddSpecGroup () {
const specList = this.multiSpecData.specList
specList.push({
key: specList.length || 0,
spec_name: '',
valueList: []
})
// 默认规格值
const groupIndex = specList.length - 1
this.handleAddSpecValue(groupIndex)
}
// 添加规格值
handleAddSpecValue (groupIndex) {
const specGroupItem = this.multiSpecData.specList[groupIndex]
const specValueList = specGroupItem.valueList
specValueList.push({
key: specValueList.length || 0,
groupKey: specGroupItem.key,
spec_value: ''
})
// 刷新规格值的key
this.onRefreshSpecValueKey(groupIndex)
}
// 删除规格组
handleDeleteSpecGroup (groupIndex) {
this.multiSpecData.specList.splice(groupIndex, 1)
this.onUpdate(false)
}
// 删除规格值
handleDeleteSpecValue (groupIndex, valueIndex) {
// 将规格值移出
this.multiSpecData.specList[groupIndex].valueList.splice(valueIndex, 1)
// 刷新规格值的key
this.onRefreshSpecValueKey(groupIndex)
this.onUpdate(false)
}
// 刷新规格值的key
onRefreshSpecValueKey (groupIndex) {
const specGroupItem = this.multiSpecData.specList[groupIndex]
const specValueList = specGroupItem.valueList
specValueList.forEach((item, index) => {
specValueList[index].key = index
})
}
// 批量设置sku事件
handleSkuBatch () {
const skuBatchForm = this.getFilterObject(this.multiSpecData.skuBatchForm)
const skuList = this.multiSpecData.skuList
// if (!skuBatchForm.image_id) {
// delete skuBatchForm.image
// }
console.log('skuBatchForm', skuBatchForm)
for (const index in skuList) {
skuList[index] = { ...skuList[index], ...skuBatchForm }
}
this.onUpdate(false)
}
/**
* 过滤对象的空元素
* (仅支持一维对象)
* @param {object} object 源对象
* @returns {object}
*/
getFilterObject (object) {
const newObj = {}
for (const key in object) {
const value = object[key]
// value === 0 可以不过滤image_id为0的情况
// if (!isEmpty(value) || value === 0) {
// newObj[key] = value
// }
if (!isEmpty(value)) {
newObj[key] = value
}
}
return newObj
}
// 表单验证
verifyForm () {
// 验证规格
if (!this.verifySpec()) {
return false
}
// 验证sku
if (!this.verifySkuList()) {
return false
}
return true
}
// 验证sku
verifySkuList () {
const columns = [
{ field: 'goods_price', name: '商品价格' },
{ field: 'stock_num', name: '库存数量' },
{ field: 'goods_weight', name: '商品重量' }
]
const skuList = this.multiSpecData.skuList
for (const skuIndex in skuList) {
const skuItem = skuList[skuIndex]
for (const colIndex in columns) {
const value = skuItem[columns[colIndex].field]
if (value === '' || value === null) {
this.error = `${columns[colIndex].name}不能为空`
return false
}
}
}
return true
}
// 验证规格
verifySpec () {
const specList = this.multiSpecData.specList
if (!specList.length) {
this.error = '亲,还没有添加规格组~'
return false
}
for (const index in specList) {
// 验证规格组
const specGroup = specList[index]
if (isEmpty(specGroup.spec_name)) {
this.error = '规格组名称不能为空~'
return false
}
// 验证规格值
const valueList = specGroup.valueList
if (!valueList.length) {
this.error = '还没有添加规格值~'
return false
}
for (const i in valueList) {
if (isEmpty(valueList[i].spec_value)) {
this.error = '规格值不能为空~'
return false
}
}
}
return true
}
// 获取规格及SKU信息(表单提交)
getFromSpecData () {
const { multiSpecData: { specList, skuList } } = this
const specData = {
specList: _.cloneDeep(specList),
skuList: _.cloneDeep(skuList)
}
for (const skuIndex in specData.skuList) {
const skuItem = specData.skuList[skuIndex]
delete skuItem.image
// delete skuItem.imageList
delete skuItem.key
}
return specData
}
/**
* 使用防抖节流方式刷新sku列表
* @param {boolean} isDebounce 如果true则使用防抖函数
*/
onUpdate (isDebounce = true) {
if (isDebounce) {
debounce(getDataForDebounce, 200)(this)
} else {
getDataForDebounce(this)
}
}
}
// onUpdate调用的逻辑方法
const getDataForDebounce = MultiSpecModel => {
return MultiSpecModel.getData()
}
/**
* 生成笛卡尔积数据
* cartesianProductOf([arr1, arr2, arr3 ...])
*/
const cartesianProductOf = arrays => {
if (!arrays.length) {
return []
}
return Array.prototype.reduce.call(arrays, (arr1, arr2) => {
var ret = []
arr1.forEach(v1 => {
arr2.forEach(v2 => {
ret.push(v1.concat([v2]))
})
})
return ret
}, [[]])
}

View File

@ -1,407 +1,518 @@
<template>
<div>
<a-form-item label="商品规格" :labelCol="labelCol" :wrapperCol="wrapperCol">
<div v-if="true" class="form-item-help" style="line-height: 36px">
<small>最多添加3个商品规格组生成的SKU数量不能超出50个</small>
</div>
<!-- 规格组 -->
<div class="spec-group" v-for="(item, index) in multiSpecData.specList" :key="index">
<div class="spec-group-item clearfix">
<a-input
class="group-item-input"
v-model="item.spec_name"
:readOnly="isSpecLocked"
placeholder="请输入规格名称"
@change="onChangeSpecGroupIpt"
/>
<a
v-if="!isSpecLocked"
class="group-item-delete"
href="javascript:;"
@click="handleDeleteSpecGroup(index)"
>删除规格组</a>
<el-form-item :label-width="labelWidth" label="商品规格">
<div style="display: block;width: 100%;">
<!-- <el-input-number v-model.number="formData.goodsSpec" controls-position="right" :min="0.01"
:precision="2" /> -->
<div class="input-intro">最多添加3个商品规格组生成的SKU数量不能超出50个</div>
<!-- 规格样式 -->
<div class="sepc-big-box" v-for="(item, index) in goodsSpecList">
<div class="sepc-title-box">
<el-input v-model="item.specName" @change="handleChangeSepc(item, index)" class="spec-input" placeholder="规格组名称"
clearable />
<el-link type="primary" @click="handleDelSpec(item)">删除规格组</el-link>
</div>
<div class="spec-value clearfix">
<div class="spec-value-item" v-for="(itm, idx) in item.valueList" :key="idx">
<a-input
class="value-item-input"
v-model="itm.spec_value"
:readOnly="isSpecLocked"
placeholder="请输入规格值"
@change="onChangeSpecValueIpt"
/>
<a-icon
v-if="!isSpecLocked"
class="icon-close"
theme="filled"
type="close-circle"
@click="handleDeleteSpecValue(index, idx)"
/>
</div>
<div v-if="!isSpecLocked" class="spec-value-add">
<a
class="group-item-delete"
href="javascript:;"
@click="handleAddSpecValue(index)"
>新增规格值</a>
<div class="spec-value-box">
<div class="spec-value-input-big-box">
<div class="spec-value-input-box" v-for="specValue in item.props">
<div class="spec-value-input-del" @click="handleDelSpecValue(item, specValue)"><el-icon>
<Close />
</el-icon></div>
<el-input v-model="specValue.specValueName" @change="handleChangeSepcValue(item, specValue)"
class="spec-value-input" placeholder="规格值名称" clearable />
</div>
</div>
<el-link type="primary" v-if="isAddSpecValueMax" @click="handleAddSpecValue(item)">添加规格值</el-link>
</div>
</div>
<!-- 添加规格按钮 -->
<a-button
v-if="!isSpecLocked && multiSpecData.specList.length < 3"
class="spec-group-add-btn"
icon="plus"
@click="handleAddSpecGroup"
>添加规格组</a-button>
</a-form-item>
<a-form-item
v-show="multiSpecData.skuList.length"
label="SKU列表"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
>
<!-- 批量设置 -->
<div v-if="multiSpecData.skuList.length > 1" class="sku-batch">
<span class="title">批量设置:</span>
<a-input-number
v-model="multiSpecData.skuBatchForm.goods_price"
placeholder="商品价格"
:min="0.01"
:precision="2"
/>
<a-input-number
v-model="multiSpecData.skuBatchForm.line_price"
placeholder="划线价格"
:min="0"
:precision="2"
/>
<a-input-number
v-model="multiSpecData.skuBatchForm.stock_num"
placeholder="库存数量"
:min="0"
:precision="0"
/>
<a-input-number
v-model="multiSpecData.skuBatchForm.goods_weight"
placeholder="商品重量"
:min="0"
:precision="2"
/>
<a-input v-model="multiSpecData.skuBatchForm.goods_sku_no" placeholder="sku编码" />
<a-button @click="handleSkuBatch">立即设置</a-button>
</div>
<!-- sku列表table -->
<a-table
class="sku-list"
:columns="multiSpecData.skuColumns"
:dataSource="multiSpecData.skuList"
:scroll="{ x: true }"
:pagination="false"
bordered
>
<!-- 预览图 -->
<template slot="image" slot-scope="text, item">
<SelectImage
v-model="item.image_id"
:defaultList="(item.image_id > 0 && item.image) ? [item.image] : []"
:width="50"
/>
<el-button v-if="isAddSpecMax" class="mt-5" @click="handleAddSpec">
<el-icon class="avatar-uploader-icon">
<plus />
</el-icon>
添加规格组
</el-button>
</div>
</el-form-item>
<!-- Sku列表 -->
<el-form-item :label-width="labelWidth" label="Sku列表">
<!-- sku列表 -->
<el-table :data="skuList" border style="width: 100%" :span-method="objectSpanMethod">
<!-- 动态列 -->
<el-table-column v-for="column in skuColumns" :prop="column.prop" :label="column.label" :key="column.key"
:width="column.width">
</el-table-column>
<!-- <el-table-column prop="goodsSkuPrice" label="预览图">
<template #default="scope">
<UploadImage ref="uploadRef" v-model="scope.row.goodsSkuImg" :data=imgData :limit="1"
:fileSize="5" :drag="true" :isShowTip="false" />
</template>
</el-table-column> -->
<el-table-column prop="goodsSkuPrice" label="商品价格">
<template #default="scope">
<el-input-number v-model.number="scope.row.goodsSkuPrice" controls-position="right" :min="0" :precision="2" />
</template>
<!-- 商品价格 -->
<template slot="goods_price" slot-scope="text, item">
<a-input-number v-model="item.goods_price" size="small" :min="0.01" :precision="2" />
</template>
<!-- 划线价格 -->
<template slot="line_price" slot-scope="text, item">
<a-input-number v-model="item.line_price" size="small" :min="0" :precision="2" />
</template>
<!-- 库存数量 -->
<template slot="stock_num" slot-scope="text, item">
<a-input-number v-model="item.stock_num" size="small" :min="0" :precision="0" />
</template>
<!-- 商品重量 -->
<template slot="goods_weight" slot-scope="text, item">
<a-input-number v-model="item.goods_weight" size="small" :min="0" :precision="2" />
</template>
<!-- sku编码 -->
<template slot="goods_sku_no" slot-scope="text, item">
<a-input v-model="item.goods_sku_no" size="small" />
</template>
</a-table>
</a-form-item>
</div>
</el-table-column>
</el-table>
</el-form-item>
</template>
<script>
import PropTypes from 'ant-design-vue/es/_util/vue-types'
import MultiSpecModel from '@/common/model/goods/MultiSpec'
import { SelectImage } from '@/components'
<script setup>
import descartes from "./dikaerji";
export default {
components: {
SelectImage
},
props: {
//
defaultSpecList: PropTypes.array.def([]),
// SKU
defaultSkuList: PropTypes.array.def([]),
// ()
isSpecLocked: PropTypes.bool.def(false)
},
data () {
return {
//
labelCol: { span: 3 },
//
wrapperCol: { span: 21 },
//
MultiSpecModel: new MultiSpecModel(),
// MultiSpecModel: Object,
multiSpecData: {
//
specList: [],
// SKU
skuList: []
}
}
},
watch: {
defaultSpecList (val) {
if (val.length && this.MultiSpecModel.isEmpty()) {
this.getData()
}
}
},
//
created () {
// SKU
this.getData()
},
methods: {
// SKU()
getData () {
const { defaultSpecList, defaultSkuList } = this
this.multiSpecData = this.MultiSpecModel.getData(defaultSpecList, defaultSkuList)
},
/** 商品规格 业务参数 */
// SKU()
getFromSpecData () {
return this.MultiSpecModel.getFromSpecData()
},
//
const isAddSpecMax = ref(true)
const isAddSpecValueMax = ref(true)
//
handleAddSpecGroup () {
if (this.checkSkuMaxNum()) {
this.MultiSpecModel.handleAddSpecGroup()
}
},
//
const skuColumns = ref([
// {
// prop: 'specValue',
// label: '',
// width: '100'
// },
// {
// prop: 'specSecondValue',
// label: '',
// width: '100'
// },
// {
// prop: 'specThirdValue',
// label: '',
// width: '100'
// },
])
//
handleDeleteSpecGroup (groupIndex) {
const app = this
const modal = this.$confirm({
title: '您确定要删除该规格组吗?',
content: '删除后不可恢复',
onOk () {
//
app.MultiSpecModel.handleDeleteSpecGroup(groupIndex)
//
modal.destroy()
}
})
},
//
handleAddSpecValue (groupIndex) {
if (this.checkSkuMaxNum()) {
this.MultiSpecModel.handleAddSpecValue(groupIndex)
}
},
//
handleDeleteSpecValue (groupIndex, valueIndex) {
const app = this
const modal = this.$confirm({
title: '您确定要删除该规格值吗?',
content: '删除后不可恢复',
onOk () {
//
app.MultiSpecModel.handleDeleteSpecValue(groupIndex, valueIndex)
//
modal.destroy()
}
})
},
// change
onChangeSpecGroupIpt () {
// skuList
this.MultiSpecModel.onUpdate(true)
},
// change
onChangeSpecValueIpt (event, itm) {
// skuList
this.MultiSpecModel.onUpdate(true)
},
// sku
checkSkuMaxNum () {
const skuList = this.multiSpecData.skuList
if (skuList.length >= 50) {
this.$message.error(`生成的sku列表数量不能大于50个当前数量${skuList.length}`, 2.5)
return false
}
return true
},
// sku
handleSkuBatch () {
this.MultiSpecModel.handleSkuBatch()
},
//
verifyForm () {
if (!this.MultiSpecModel.verifyForm()) {
this.$message.error(this.MultiSpecModel.getError(), 2)
return false
}
return true
}
}
}
</script>
<style lang="less" scoped>
//
.spec-group {
width: 895px;
margin-bottom: 15px;
line-height: normal;
.ant-input {
height: 28px;
padding: 4px 11px;
font-size: @font-size-base;
line-height: 1.5;
&::placeholder {
font-size: @font-size-base;
}
}
.form-item-help {
line-height: 36px;
}
.spec-group-item {
background: #f4f5f9;
padding: 7px 12px;
line-height: 28px;
.group-item-input {
float: left;
width: 180px;
}
.group-item-delete {
font-size: 12px;
float: right;
}
}
.spec-value {
background: #fbfbfb;
padding: 8px 35px;
.spec-value-add,
.spec-value-item {
float: left;
margin-bottom: 6px;
margin-top: 6px;
}
.spec-value-add {
height: 28px;
line-height: 28px;
a {
font-size: 12px;
}
}
.spec-value-item {
position: relative;
margin-right: 20px;
.value-item-input {
width: 186px;
}
&:hover {
.icon-close {
display: block;
}
}
.icon-close {
display: none;
position: absolute;
top: -8px;
right: -8px;
cursor: pointer;
font-size: 16px;
color: #7d7d7d;
}
}
}
}
//
.spec-group-add-btn {
font-size: @font-size-base;
}
// sku
.sku-batch {
line-height: 40px;
margin-bottom: 12px;
.title {
line-height: 28px;
margin-right: 15px;
font-size: @font-size-base;
}
/deep/.ant-input,
/deep/.ant-input-number {
width: 120px;
margin-right: 15px;
}
/deep/.ant-input {
width: 140px;
}
}
//
const goodsSpecList = ref([
// Json
// {
// specId: 0,
// specName: "",
// props: [
// {
// specValueId: 0,
// specValueName: "",
// specValue: "",
// },
// ],
// }
])
// sku
.sku-list {
width: 895px;
/deep/.ant-table-thead > tr > th,
/deep/.ant-table-tbody > tr > td {
white-space: nowrap;
const skuList = ref([
])
/** sku列表参数 */
const newList = ref([])
const newData = ref([])
const tableData7 = ref([])
const spanArr = ref([])
const spanArr1 = ref([])
const pos = ref("")
const pos1 = ref("")
/** sku列表方法 */
//
const processing = () => {
newData.value = [];
for (let i = 0; i < goodsSpecList.value.length; i++) {
var newlist = [];
for (
let index = 0;
index < goodsSpecList.value[i].props.length;
index++
) {
newlist.push(goodsSpecList.value[i].props[index].specValueName);
}
this.newData.push(newlist);
}
/deep/.ant-table-tbody > tr > td {
padding: 12px 18px;
console.log(this.newData, '各规格组中的规格值');
getList();
}
//
const getList = () => {
tableData7.value = [];
newList.value = this.descartes(newData.value);
console.log(newList.value, '笛卡尔积处理过后的数据');
for (let index = 0; index < newList.value.length; index++) {
this.tableData7.push({
specValue: newList.value[index][0],
specSecondValue: newList.value[index][1],
specThirdValue: newList.value[index][2],
goodsGuid: 0,
specValueName: "",
goodsSkuImg: "",
goodsSkuSkuCode: 0,
goodsSkuPrice: 0,
goodsSkuLinePrice: 0,
goodsSkuStockNum: 0,
goodsSkuWeight: 0,
goodsSkuProps: ""
});
}
/deep/.ant-input-sm,
/deep/.ant-input-number-sm {
height: 28px;
getSpanArr(this.tableData7);
console.log(this.tableData7, '表格中的数据');
}
//
const getSpanArr = (data) => {
spanArr.value = [];
pos.value = "";
spanArr1.value = [];
pos1.value = "";
for (var i = 0; i < data.length; i++) {
if (i === 0) {
spanArr.value.push(1);
pos.value = 0;
} else {
//
if (data[i].specValue === data[i - 1].specValue) {
spanArr.value[pos.value] += 1;
spanArr.value.push(0);
} else {
spanArr.value.push(1);
pos.value = i;
}
}
}
/deep/.ant-input-number-sm input {
height: 26px;
for (var i = 0; i < data.length; i++) {
if (i === 0) {
spanArr1.value.push(1);
pos1.value = 0;
} else {
// for
if (data[i].specSecondValue === data[i - 1].specSecondValue) {
spanArr1.value[pos1.value] += 1;
spanArr1.value.push(0);
} else {
spanArr1.value.push(1);
pos1.value = i;
}
}
}
console.log(this.spanArr, '排序Arr');
}
//
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }) => {
// columnIndex === 0
if (columnIndex === 0) {
const _row = spanArr.value[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
};
// columnIndex === 1
} else if (columnIndex === 1) {
const _row = spanArr1.value[rowIndex];
const _col = _row > 0 ? 1 : 0;
return {
rowspan: _row,
colspan: _col
};
}
}
//
// const objectSpanMethod = ({
// row,
// column,
// rowIndex,
// columnIndex,
// }) => {
// // if (columnIndex === 0) {
// // if (rowIndex % firstLen === 0) {
// // return {
// // rowspan: firstLen,
// // colspan: 1,
// // }
// // } else {
// // return {
// // rowspan: 0,
// // colspan: 0,
// // }
// // }
// // }
// // if (columnIndex === 1) {
// // if (rowIndex % secondLen === 0) {
// // return {
// // rowspan: secondLen,
// // colspan: 1,
// // }
// // } else {
// // return {
// // rowspan: 0,
// // colspan: 0,
// // }
// // }
// // }
// }
// let firstLen = 0
// let secondLen = 0
// skuList.value.map((item, idx) => {
// let firstVal = skuList?.value[0].specValue
// if (item.specValue === firstVal) {
// firstLen += 1
// firstVal = item.specValue
// }
// // let secondVal = skuList?.value[0].specSecondValue
// // if (item.specSecondValue === secondVal) {
// // secondLen += 1
// // secondVal = item.specSecondValue
// // }
// })
// console.log(secondLen, '');
/** 商品规格 业务方法 */
//
function handleAddSpec() {
let data = {
specId: 0,
specName: "",
key: 0,
props: [
]
}
goodsSpecList.value.push(data)
if (goodsSpecList.value.length == 3) {
isAddSpecMax.value = false
}
}
//
function handleChangeSepc(item, idx) {
item.key = idx
let foundMatch = false;
skuColumns.value.map((i, index) => {
if (i.key == idx) {
i.label = item.specName
foundMatch = true
return true
}
})
if (foundMatch) {
return; //
}
//
let column = {
prop: 'specValue',
label: item.specName,
width: '100',
key: idx
}
skuColumns.value.push(column)
}
//
function handleDelSpec(item) {
const index = goodsSpecList.value.indexOf(item);
if (index > -1) {
goodsSpecList.value.splice(index, 1);
}
if (goodsSpecList.value.length < 3) {
isAddSpecMax.value = true
}
//
skuColumns.value = skuColumns.value.filter(column => column.label !== item.specName);
// sku
// skuColumns.value = skuColumns.value.filter(column => column.specName !== item.specName);
}
//
function handleAddSpecValue(item) {
let data = {
specId: 0,
specValueId: 0,
specValueName: "",
}
item.props.push(data)
if (item.props.length == 50) {
isAddSpecValueMax.value = false
}
}
//
function handleChangeSepcValue(item, specValue) {
let firstSpecValue = ""
let secondSpecValue = ""
let thirdSpecValue = ""
console.log(item, 'item喵喵喵喵喵喵');
// goodsSpecList.value.map((item,index) => {
// if(item.key === 0){
// firstSpecValue = specValue.specValueName
// }
// if(item.key === 1){
// secondSpecValue = specValue.specValueName
// }
// if(item.key === 2){
// thirdSpecValue = specValue.specValueName
// }
// })
// sku
let sku = {
specValue: firstSpecValue,
specSecondValue: secondSpecValue,
specThirdValue: thirdSpecValue,
goodsGuid: 0,
specValueName: "",
goodsSkuImg: "",
goodsSkuSkuCode: 0,
goodsSkuPrice: 0,
goodsSkuLinePrice: 0,
goodsSkuStockNum: 0,
goodsSkuWeight: 0,
goodsSkuProps: ""
}
skuList.value.push(sku)
console.log(skuList.value, 'asdasd-------------');
}
//
function handleDelSpecValue(item, specValue) {
const index = item.props.indexOf(specValue);
if (index > -1) {
item.props.splice(index, 1);
}
if (item.props.length < 50) {
isAddSpecValueMax.value = true
}
}
</script>
<style lang="scss" scoped>
.divider-title {
font-size: 18px;
}
.input-intro {
color: #8c8c8c;
font-size: 12px;
}
.el-divider {
margin: 50px 0;
}
.sepc-big-box {
width: 100%;
margin-bottom: 20px;
}
.sepc-title-box {
width: 100%;
padding: 10px;
background-color: #f4f4f5;
display: flex;
justify-content: space-between;
}
.spec-value-box {
width: 100%;
padding: 20px 20px 20px 40px;
background-color: #fbfbfb;
display: flex;
flex-wrap: wrap;
}
.spec-value-input-big-box {
display: flex;
flex-wrap: wrap;
}
.spec-value-input-box {
width: 250px;
position: relative;
margin-right: 30px;
margin-bottom: 15px;
}
.spec-value-input-del {
position: absolute;
top: -5px;
right: -5px;
z-index: 99;
width: 18px;
height: 18px;
background-color: grey;
color: white;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
font-size: 10px;
cursor: pointer;
}
.spec-input {
width: 250px;
margin-right: 30px;
}
.mt-5 {
margin-top: 10px;
}
.el-input-number {
width: 140px;
}
</style>

View File

@ -0,0 +1,226 @@
// const skuList = ref([
// {
// specValue: "蓝色",
// specSecondValue: "4+128G",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "4+128G",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "8+256G",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "8+256G",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "12+1T",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "12+1T",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "1000G",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "蓝色",
// specSecondValue: "1000G",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "4+128G",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "4+128G",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "8+256G",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "8+256G",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "12+1T",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "12+1T",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "1000G",
// specThirdValue: "5G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// {
// specValue: "黑色",
// specSecondValue: "1000G",
// specThirdValue: "4G",
// goodsGuid: 0,
// specValueName: "",
// goodsSkuImg: "",
// goodsSkuSkuCode: 0,
// goodsSkuPrice: 10,
// goodsSkuLinePrice: 0,
// goodsSkuStockNum: 0,
// goodsSkuWeight: 0,
// goodsSkuProps: ""
// },
// ])

View File

@ -0,0 +1,19 @@
//笛卡尔积算法
function descartes(array){
if( array.length < 2 ) return array[0] || [];
return [].reduce.call(array, function(col, set) {
var res = [];
col.forEach(function(c) {
set.forEach(function(s) {
var t = [].concat( Array.isArray(c) ? c : [c] );
t.push(s);
res.push(t);
})});
return res;
});
}
export default descartes