VUE-SKU组件源码及使用教程
这是一个基于 Vue & ElementUI 的电商 SKU 表单配置组件
组件名SkuForm.vue
<template>
<div class="sku-container">
<div v-if="!disabled" class="sku-check">
<div v-if="theme == 1" class="theme-1">
<el-card v-for="(item, index) in myAttribute" :key="index" class="item" shadow="never">
<div slot="header">{{ item.name }}</div>
<el-checkbox v-for="(item2, index2) in item.item" :key="index2" v-model="item2.checked" :label="item2.name" size="small" />
<el-input v-if="item.canAddAttribute" v-model="item.addAttribute" size="small" placeholder="新增一个规格" class="add-attr" @keyup.enter.native="onAddAttribute(index)">
<el-button slot="append" size="small" icon="el-icon-plus" @click="onAddAttribute(index)">添加</el-button>
</el-input>
</el-card>
</div>
<el-table v-else :data="myAttribute" :show-header="false" class="theme-2">
<el-table-column prop="name" width="120" :resizable="false" />
<el-table-column>
<template slot-scope="scope">
<el-checkbox v-for="(item2, index2) in scope.row.item" :key="index2" v-model="item2.checked" :label="item2.name" size="small" />
</template>
</el-table-column>
<el-table-column width="250">
<template slot-scope="scope">
<el-input v-model="scope.row.addAttribute" size="small" placeholder="新增一个规格" class="add-attr" @keyup.enter.native="onAddAttribute(scope.$index)">
<el-button slot="append" size="small" icon="el-icon-plus" @click="onAddAttribute(scope.$index)">添加</el-button>
</el-input>
</template>
</el-table-column>
</el-table>
</div>
<div class="sku-list">
<el-form ref="form" :model="form" status-icon inline-message>
<el-table :data="form.skuData" stripe border highlight-current-row>
<!-- 考虑到异步加载的情况,如果 attribute 数据先加载完成,则表头会立马展示,效果不理想,故使用emitAttribute 数据,该数据为计算属性,通过 myAttribute 生成,结构与 attribute 一致 -->
<el-table-column v-if="emitAttribute.length > 0" type="index" width="50" align="center" :resizable="false" />
<el-table-column v-for="(attr, index) in emitAttribute" :key="`attribute-${index}`" :label="attr.name" :prop="attr.name" width="120" align="center" :resizable="false" sortable />
<el-table-column v-for="(item, index) in structure" :key="`structure-${index}`" :label="item.label" :prop="item.name" align="center" :resizable="false" min-width="120px">
<!-- 自定义表头 -->
<template slot="header">
<span :class="{'required_title': item.required}">
{{ item.label }}
</span>
<el-tooltip v-if="item.tip" effect="dark" :content="item.tip" placement="top">
<i class="el-icon-info" />
</el-tooltip>
</template>
<!-- 自定义表格内部展示 -->
<template slot-scope="scope">
<!-- 增加是 key 是为了保证异步验证不会出现 skuData 数据变化后无法验证的 bug -->
<el-form-item v-if="item.type == 'input'" :key="`structure-input-${index}-${scope.row.sku}`" :prop="'skuData.' + scope.$index + '.' + item.name" :rules="rules[item.name]">
<el-input v-model="scope.row[item.name]" :placeholder="`请输入${item.label}`" size="small" />
</el-form-item>
<el-form-item v-else-if="item.type == 'slot'" :key="`structure-input-${index}-${scope.row.sku}`" :prop="'skuData.' + scope.$index + '.' + item.name" :rules="rules[item.name]">
<slot :name="item.name" :$index="scope.$index" :row="scope.row" :column="scope.column" />
</el-form-item>
</template>
</el-table-column>
<!-- 批量设置,当 sku 数超过 2 个时出现 -->
<template v-if="isBatch && form.skuData.length > 2" slot="append">
<el-table :data="[{}]" :show-header="false">
<el-table-column :width="attribute.length * 120 + 50" align="center" :resizable="false">批量设置</el-table-column>
<el-table-column v-for="(item, index) in structure" :key="`batch-structure-${index}`" align="center" :resizable="false" min-width="120px">
<el-input v-if="item.type == 'input' && item.batch != false" v-model="batch[item.name]" :placeholder="`填写一个${item.label}`" size="small" @keyup.enter.native="onBatchSet(item.name)" />
</el-table-column>
</el-table>
</template>
</el-table>
</el-form>
</div>
</div>
</template>
<script>
export default {
name: 'SkuForm',
props: {
/**
* 原始规格数据
* sourceAttribute: [
* { name: '颜色', item: ['黑', '金', '白'] },
* { name: '内存', item: ['16G', '32G'] },
* { name: '运营商', item: ['电信', '移动', '联通'] }
* ]
*/
sourceAttribute: {
type: Array,
default: () => []
},
/**
* 已使用的规格数据,用于复原数据,支持.sync修饰符
* attribute: [
* { name: '颜色', item: ['黑'] },
* { name: '运营商', item: ['电信', '移动', '联通'] }
* ]
*/
attribute: {
type: Array,
default: () => []
},
/**
* 用于复原sku数据,支持.sync修饰符
* sku: [
* { sku: '黑;电信', price: 1, stock: 1 },
* { sku: '黑;移动', price: 2, stock: 2 },
* { sku: '黑;联通', price: 3, stock: 3 }
* ]
*/
sku: {
type: Array,
default: () => []
},
/**
* 表格结构,注意name字段,用于输出sku数据
*/
structure: {
type: Array,
default: () => [
{ name: 'price', type: 'input', label: '价格' },
{ name: 'stock', type: 'input', label: '库存' }
]
},
// sku 字段分隔符
separator: {
type: String,
default: ';'
},
// 无规格的 sku
emptySku: {
type: String,
default: ''
},
// 是否显示 sku 选择栏
disabled: {
type: Boolean,
default: false
},
// 主题风格
theme: {
type: Number,
default: 1
},
// 是否开启异步加载
async: {
type: Boolean,
default: false
}
},
data() {
return {
isInit: false,
myAttribute: [],
form: {
skuData: []
},
batch: {}
}
},
computed: {
rules() {
// 重新生成验证规则
let rules = {}
this.structure.forEach(v => {
if (v.type == 'input') {
rules[v.name] = []
if (v.required) {
rules[v.name].push({ required: true, message: `${v.label}不能为空`, trigger: 'blur' })
}
if (v.validate) {
rules[v.name].push({ validator: this.customizeValidate, trigger: 'blur' })
}
} else if (v.type == 'slot') {
rules[v.name] = []
if (v.required) {
rules[v.name].push({ required: true, message: `${v.label}不能为空`, trigger: ['change', 'blur'] })
}
if (v.validate) {
rules[v.name].push({ validator: this.customizeValidate, trigger: ['change', 'blur'] })
}
}
})
return rules
},
isBatch() {
return this.structure.some(item => {
return item.type == 'input' && item.batch != false
})
},
// 将 myAttribute 数据还原会 attribute 数据的结构,用于更新 attribute
emitAttribute() {
let attribute = []
this.myAttribute.forEach(v1 => {
const obj = {
name: v1.name,
item: []
}
v1.item.forEach(v2 => {
if (v2.checked) {
obj.item.push(v2.name)
}
})
if (obj.item.length !== 0) {
attribute.push(obj)
}
})
return attribute
}
},
watch: {
myAttribute: {
handler() {
if (!this.isInit) {
// 更新父组件
this.$emit('update:attribute', this.emitAttribute)
}
// 解决通过 $emit 更新后无法拿到 attribute 最新数据的问题
this.$nextTick(() => {
if (this.attribute.length !== 0) {
this.combinationAttribute()
} else {
this.form.skuData = []
const obj = {
sku: this.emptySku
}
this.structure.forEach(v => {
if (!(v.type == 'slot' && v.skuProperty == false)) {
obj[v.name] = typeof v.defaultValue != 'undefined' ? v.defaultValue : ''
}
})
this.form.skuData.push(obj)
}
this.clearValidate()
})
},
deep: true
},
'form.skuData': {
handler(newValue, oldValue) {
if (!this.isInit || (newValue.length == 1 && newValue[0].sku == this.emptySku)) {
// 如果有老数据,或者 sku 数据为空,则更新父级 sku 数据
if (oldValue.length || !this.sku.length) {
// 更新父组件
const arr = []
newValue.forEach(v1 => {
const obj = {
sku: v1.sku
}
this.structure.forEach(v2 => {
if (!(v2.type == 'slot' && v2.skuProperty == false)) {
obj[v2.name] = v1[v2.name] || (typeof v2.defaultValue != 'undefined' ? v2.defaultValue : '')
}
})
arr.push(obj)
})
this.$emit('update:sku', arr)
}
}
},
deep: true
}
},
mounted() {
!this.async && this.init()
},
methods: {
init() {
this.$nextTick(() => {
this.isInit = true
// 初始化 myAttribute
let myAttribute = []
// 根据 sourceAttribute 复原 myAttribute 的结构
this.sourceAttribute.forEach(v => {
const temp = {
name: v.name,
canAddAttribute: typeof v.canAddAttribute != 'undefined' ? v.canAddAttribute : true,
addAttribute: '',
item: []
}
v.item.forEach(itemName => {
temp.item.push({
name: itemName,
checked: false
})
})
myAttribute.push(temp)
})
// 根据 attribute 更新 myAttribute
this.attribute.forEach(attrVal => {
myAttribute.forEach(myAttrVal => {
if (attrVal.name === myAttrVal.name) {
attrVal.item.forEach(attrName => {
if (
!myAttrVal.item.some(myAttrItem => {
if (attrName === myAttrItem.name) {
myAttrItem.checked = true
}
return attrName === myAttrItem.name
})
) {
myAttrVal.item.push({
name: attrName,
checked: true
})
}
})
}
})
})
this.myAttribute = myAttribute
// 通过 sku 更新 skuData,但因为 skuData 是实时监听 myAttribute 变化并自动生成,而 watch 是在 methods 后执行,所以增加 setTimeout 方法,确保 skuData 生成后在执行下面的代码
setTimeout(() => {
this.sku.forEach(skuItem => {
this.form.skuData.forEach(skuDataItem => {
if (skuItem.sku === skuDataItem.sku) {
this.structure.forEach(structureItem => {
skuDataItem[structureItem.name] = skuItem[structureItem.name]
})
}
})
})
this.isInit = false
}, 0)
})
},
// 根据 attribute 进行排列组合,生成 skuData 数据
combinationAttribute(index = 0, dataTemp = []) {
if (index === 0) {
for (let i = 0; i < this.attribute[0].item.length; i++) {
const obj = {
sku: this.attribute[0].item[i],
[this.attribute[0].name]: this.attribute[0].item[i]
}
this.structure.forEach(v => {
if (!(v.type == 'slot' && v.skuProperty == false)) {
obj[v.name] = typeof v.defaultValue != 'undefined' ? v.defaultValue : ''
}
})
dataTemp.push(obj)
}
} else {
const temp = []
for (let i = 0; i < dataTemp.length; i++) {
for (let j = 0; j < this.attribute[index].item.length; j++) {
temp.push(JSON.parse(JSON.stringify(dataTemp[i])))
temp[temp.length - 1][this.attribute[index].name] = this.attribute[index].item[j]
temp[temp.length - 1]['sku'] = [temp[temp.length - 1]['sku'], this.attribute[index].item[j]].join(this.separator)
}
}
dataTemp = temp
}
if (index !== this.attribute.length - 1) {
this.combinationAttribute(index + 1, dataTemp)
} else {
if (!this.isInit || this.async) {
// 将原有的 sku 数据和新的 sku 数据比较,相同的 sku 则把原有的 sku 数据覆盖到新的 sku 数据里
for (let i = 0; i < this.form.skuData.length; i++) {
for (let j = 0; j < dataTemp.length; j++) {
if (this.form.skuData[i].sku === dataTemp[j].sku) {
dataTemp[j] = this.form.skuData[i]
}
}
}
}
this.form.skuData = dataTemp
}
},
// 新增一个规格
onAddAttribute(index) {
this.myAttribute[index].addAttribute = this.myAttribute[index].addAttribute.trim()
if (this.myAttribute[index].addAttribute !== '') {
if (!this.myAttribute[index].addAttribute.includes(this.separator)) {
const flag = this.myAttribute[index].item.some(item => {
return item.name === this.myAttribute[index].addAttribute
})
if (!flag) {
this.myAttribute[index].item.push({
name: this.myAttribute[index].addAttribute,
checked: true
})
this.myAttribute[index].addAttribute = ''
} else {
this.$message({
type: 'warning',
message: '请勿添加相同规格'
})
}
} else {
this.$message({
type: 'warning',
message: `规格里不允许出现「 ${this.separator} 」字符,请检查后重新添加`
})
}
}
},
onBatchSet(type) {
if (this.batch[type] != '') {
this.form.skuData.forEach(v => {
v[type] = this.batch[type]
})
this.batch[type] = ''
// 批量设置完成后,触发一次当前列的验证
this.validateFieldByColumns([type], () => {})
}
},
// 自定义输入框验证,通过调用 structure 里的 validate 方法实现,重点是 callback 要带过去
customizeValidate(rule, value, callback) {
let [model, index, name] = rule.field.split('.')
this.structure.forEach(v => {
if (v.name == name) {
v.validate(this.form[model], index, callback)
}
})
},
// sku 表单验证
validate(callback) {
this.$refs['form'].validate(valid => {
callback(valid)
})
},
validateFieldByColumns(colums, callback) {
let props = []
this.form.skuData.forEach((v, i) => {
colums.forEach(v => {
props.push(`skuData.${i}.${v}`)
})
})
this.$refs['form'].validateField(props, valid => {
callback(valid)
})
},
validateFieldByRows(index, prop, callback) {
this.$refs['form'].validateField([`skuData.${index}.${prop}`], valid => {
callback(valid)
})
},
clearValidate() {
this.$refs['form'].clearValidate()
}
}
}
</script>
<style lang="scss" scoped>
.sku-container {
::v-deep .el-card {
margin: 10px 0;
.el-card__header {
line-height: initial;
padding: 10px 20px;
}
.el-card__body {
padding: 10px 20px 20px;
}
}
.sku-check {
.theme-1 {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 10px;
.item {
width: 32%;
&:last-child:nth-child(3n - 1) {
margin-right: calc(100% - 32% * 2 - 4% / 2) !important;
}
.add-attr {
width: 100%;
margin-top: 10px;
}
}
}
.theme-2 {
border: 1px solid #ebeef5;
border-bottom: 0;
margin-bottom: 20px;
}
}
.sku-name {
text-align: right;
}
.batch-set {
width: 100%;
margin-top: 5px;
}
.sku-list {
line-height: initial;
::v-deep .el-input__inner {
text-align: center;
}
::v-deep .el-table__append-wrapper {
overflow: initial;
.el-table {
overflow: initial;
.el-table__body-wrapper {
overflow: initial;
}
}
}
::v-deep .el-form-item {
margin-bottom: 0;
.el-form-item__content {
line-height: initial;
.el-form-item__error {
margin-left: 0;
}
}
}
.required_title::before {
content: '*';
color: #f56c6c;
}
}
}
</style>
组件参数:
Props:
参数 | 说明 | 类型 | 默认值 |
---|---|---|---|
source-attribute | SKU可选属性 | array | [] |
structure | 表单结构 | array | [{ name: 'price', type: 'input', label: '价格' }, { name: 'stock', type: 'input', label: '库存' }] |
attribute | SKU已选属性,支持 .sync 修饰符 | array | [] |
sku | SKU数据,支持 .sync 修饰符 | array | [] |
separator | SKU字段分隔符 | string | ; |
emptySku | 无属性SKU名称 | string | |
disabled | 是否显示SKU选择栏 | boolean | false |
theme | SKU选择栏主题风格,可选1或2 | int | 1 |
async | 是否开启异步加载 | boolean | false |
#source-attribute
名称 | 说明 | 类型 | 默认值 |
---|---|---|---|
name | 属性名称 | string | |
item | 属性可选项 | array | |
canAddAttribute | 是否可以添加属性 | boolean | true |
// 例子
[
{ name: '颜色', item: ['黑', '金', '白'] },
{ name: '内存', item: ['16G', '32G'], canAddAttribute: false },
{ name: '运营商', item: ['电信', '移动', '联通'] }
]
#structure
名称 | 说明 | 类型 | 默认值 | 可选值 |
---|---|---|---|---|
name | SKU数据里的属性 | string | ||
type | 表单展示形式,默认为输入框,当设置为 slot 时,为自定义插槽 | string | input | input, slot |
skuProperty | 当 type 设置为 slot 时,可选择是否插槽数据是否记录到 sku 数据里 | boolean | true | |
defaultValue | 默认值 | any | ||
label | 表头名称 | string | ||
tip | 鼠标悬停提示 | string | ||
batch | 是否开启批量设置 | boolean | true | |
required | 是否必填 | boolean | false | |
validate | 自定义校验回调方法 | function | false |
// 例子
[
{
name: 'code',
type: 'input',
label: '商品唯一编码',
required: true,
batch: false
},
{
name: 'originalprice',
type: 'input',
label: '成本价'
},
{
name: 'price',
type: 'input',
label: '销售价'
},
{
name: 'stock',
type: 'input',
defaultValue: 10,
label: '库存',
tip: '库存数不能少于10',
validate: (data, index, callback) => {
if (parseInt(data[index].stock) < 10) {
callback(new Error('库存不能小于10'))
}
callback()
}
}
]
#attribute
名称 | 说明 | 类型 | 可选值 |
---|---|---|---|
name | 属性名称 | string | |
item | 属性已选项 | array |
// 例子
[
{ name: '颜色', item: ['黑', '红'] },
{ name: '运营商', item: ['电信'] }
]
// 需要注意 attribute 的属性名称需要 source-attribute 里存在,但 attribute 的属性已选项则没有限制
// 例如下面的例子则是错误示范
[
{ name: '尺码', item: ['L', 'M'] },
{ name: '运营商', item: ['电信'] }
]
Methods
方法名 | 说明 |
---|---|
init | 初始化方法,如果数据是异步载入,需要在获取到数据后手动进行SKU表单的初始化 |
validate | SKU表单验证方法,参数为一个回调函数 |
validateField | SKU表单验证指定列方法,第一个参数为列名(structure 里的属性名),第二个参数为一个回调函数 |
clearValidate | 清除SKU表单验证结果 |
使用示例
基本示例
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
attribute: [],
sku: []
}
}
}
</script>
自定义SKU(连接符)
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:attribute.sync="attribute"
:sku.sync="sku"
separator="__"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
attribute: [
{
name: '颜色',
item: ['黑']
},
{
name: '内存',
item: ['16G']
}
],
sku: [
{
sku: '黑__16G',
price: 80,
stock: 100
}
]
}
}
}
</script>
关闭SKU可选属性配置
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白'],
canAddAttribute: false
},
{
name: '内存',
item: ['16G', '32G'],
canAddAttribute: false
}
],
attribute: [],
sku: []
}
}
}
</script>
禁用SKU可选属性配置
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:attribute.sync="attribute"
:sku.sync="sku"
:disabled="true"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
attribute: [
{
name: '颜色',
item: ['黑']
},
{
name: '内存',
item: ['16G', '32G']
}
],
sku: [
{
sku: '黑;16G',
price: 80,
stock: 100
},
{
sku: '黑;32G',
price: 80,
stock: 100
}
]
}
}
}
</script>
主题风格
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:attribute.sync="attribute"
:sku.sync="sku"
:theme="theme"
/>
<el-button type="primary" style="margin-top: 10px;" @click="switchTheme">切换主题</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
attribute: [],
sku: [],
theme: 1
}
},
methods: {
switchTheme() {
this.theme = this.theme == 1 ? 2 : 1
}
}
}
</script>
自定义表格
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'originalprice',
type: 'input',
label: '原价'
},
{
name: 'price',
type: 'input',
label: '现价'
},
{
name: 'stock',
type: 'input',
label: '库存'
}
],
attribute: [],
sku: []
}
}
}
</script>
自定义表格(插槽-文本)
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
>
<template #price="slotProps">
{{ slotProps.row.price }}
</template>
<template #totalPrice="slotProps">
{{ total(slotProps.row) }}
</template>
</SkuForm>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'price',
type: 'slot',
label: '现价'
},
{
name: 'stock',
type: 'input',
label: '库存'
},
{
name: 'totalPrice',
type: 'slot',
// 如果该字段无需记录到 sku 数据里,则设置为 false
skuProperty: false,
label: '总价',
tip: '总价 = 价格 * 库存,如果价格或库存为空时,则不计算'
}
],
attribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G']
}
],
sku: [
{
sku: '黑;16G',
price: 85,
stock: 100
},
{
sku: '金;16G',
price: 85,
stock: 50
},
{
sku: '白;16G',
price: 85,
stock: 50
}
]
}
},
methods: {
total(data) {
let totalPrice = ''
if (data.price && data.stock) {
totalPrice = (parseFloat(data.price) * parseFloat(data.stock)).toFixed(2)
totalPrice += ' 元'
}
return totalPrice
}
}
}
</script>
自定义表格(插槽-组件)
<template>
<div>
<SkuForm
ref="skuForm"
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
>
<template #score="slotProps">
<div>
<el-rate v-model="slotProps.row.score" />
</div>
</template>
<template #image="slotProps">
<div class="image-upload-container">
<el-image v-if="slotProps.row.image" :src="slotProps.row.image" :preview-src-list="[slotProps.row.image]" fit="cover" title="点击预览" />
<el-upload :show-file-list="false" action="http://scrm.1daas.com/api/upload/upload" :data="{token: 'TKD917339526087186'}" name="image" :before-upload="beforeUpload" :on-success="res => imageUpload(res, slotProps)" class="images-upload">
<el-button size="small" icon="el-icon-upload2">{{ slotProps.row.image ? '重新上传' : '上传图片' }}</el-button>
</el-upload>
<el-button v-if="slotProps.row.image" size="small" icon="el-icon-delete" @click="imageRemove(slotProps)" />
</div>
</template>
</SkuForm>
<el-button type="primary" style="margin-top: 10px;" @click="submit">提交</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'price',
type: 'input',
label: '现价'
},
{
name: 'stock',
type: 'input',
label: '库存'
},
{
name: 'score',
type: 'slot',
defaultValue: 0,
label: '评分'
},
{
name: 'image',
type: 'slot',
label: '图片',
required: true
}
],
attribute: [],
sku: []
}
},
methods: {
beforeUpload(file) {
const ext = ['jpg', 'png', 'gif', 'bmp']
const size = 2
const fileName = file.name.split('.')
const fileExt = fileName[fileName.length - 1]
const isTypeOk = ext.indexOf(fileExt) >= 0
const isSizeOk = file.size / 1024 / 1024 < size
if (!isTypeOk) {
this.$message.error(`上传图片只支持 ${ ext.join(' / ') } 格式!`)
}
if (!isSizeOk) {
this.$message.error(`上传图片大小不能超过 ${size}MB!`)
}
return isTypeOk && isSizeOk
},
// 图片上传
imageUpload(res, data) {
// 这里会返回上传结果,提取出图片地址url
console.log(res)
// 模拟返回数据
let imagePath = 'http://images.lookbi.com/uploads/apply/166/e2e1b23647d67df2655d5e6bed76670c.jpg'
data.row.image = imagePath
this.$message.success('图片上传成功')
this.$refs.skuForm.validateFieldByRows(data.$index, 'image', () => {})
},
imageRemove(data) {
data.row.image = ''
},
submit() {
this.$refs.skuForm.validate(valid => {
if (valid) {
this.$message.success('验证通过')
} else {
this.$message.warning('验证失败')
}
})
}
}
}
</script>
<style lang="scss" scoped>
/deep/ .el-upload-dragger {
width: initial;
height: initial;
border: 0;
border-radius: 0;
background-color: initial;
overflow: auto;
}
/deep/ .image-upload-container {
.el-image {
vertical-align: middle;
margin: 0 5px;
width: 30px;
height: 30px;
}
.images-upload,
> .el-button {
display: inline-block;
margin: 0 5px;
vertical-align: middle;
}
}
</style>
数据还原(初始数据)
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'originalprice',
type: 'input',
label: '原价'
},
{
name: 'price',
type: 'input',
label: '现价'
},
{
name: 'stock',
type: 'input',
label: '库存'
}
],
attribute: [
{
name: '颜色',
item: ['黑', '金']
},
{
name: '内存',
item: ['16G']
}
],
sku: [
{
sku: '黑;16G',
originalprice: 100,
price: 80,
stock: 100
},
{
sku: '金;16G',
originalprice: 100,
price: 85,
stock: 50
}
]
}
}
}
</script>
批量设置
当 SKU 数据超过 2 条时,自动开启批量设置,可在 structure 里设置 batch: false 进行关闭
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'price',
type: 'input',
label: '价格'
},
{
name: 'stock',
type: 'input',
label: '库存',
batch: false
}
],
attribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G']
}
],
sku: [
{
sku: '黑;16G',
price: 85,
stock: 100
},
{
sku: '金;16G',
price: 85,
stock: 50
},
{
sku: '白;16G',
price: 85,
stock: 50
}
]
}
}
}
</script>
必填验证
<template>
<div>
<SkuForm
ref="skuForm"
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-button type="primary" @click="submit" style="margin-top: 10px;">提交</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'price',
type: 'input',
label: '价格',
required: true
},
{
name: 'stock',
type: 'input',
label: '库存',
required: true
}
],
attribute: [],
sku: []
}
},
methods: {
submit() {
this.$refs.skuForm.validate(valid => {
if (valid) {
this.$message.success('验证通过')
} else {
this.$message.warning('验证失败')
}
})
}
}
}
</script>
自定义验证
<template>
<div>
<SkuForm
ref="skuForm"
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-button type="primary" @click="submit" style="margin-top: 10px;">提交</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'price',
type: 'input',
label: '价格'
},
{
name: 'stock',
type: 'input',
label: '库存',
tip: '库存数不能少于10',
required: true,
// data: 完整 sku 数据,index: 当前 sku 在 data 中的下标,callback: 验证结果回调函数
validate: (data, index, callback) => {
if (data[index].stock && parseInt(data[index].stock) < 10) {
callback(new Error('库存不能小于10'))
}
callback()
}
}
],
attribute: [],
sku: []
}
},
methods: {
submit() {
this.$refs.skuForm.validate(valid => {
if (valid) {
this.$message.success('验证通过')
} else {
this.$message.warning('验证失败')
}
})
}
}
}
</script>
异步验证
<template>
<div>
<SkuForm
ref="skuForm"
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-button type="primary" @click="submit" style="margin-top: 10px;">提交</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'code',
type: 'input',
label: '商品唯一编码',
tip: '模拟异步操作,如果输入123则会提示编码已存在',
required: true,
validate: (data, index, callback) => {
setTimeout(() => {
if (data[index].code == '123') {
callback(new Error('商品唯一编码已存在'))
}
callback()
}, 1000)
}
},
{
name: 'price',
type: 'input',
label: '价格'
},
{
name: 'stock',
type: 'input',
label: '库存'
}
],
attribute: [],
sku: []
}
},
methods: {
submit() {
this.$refs.skuForm.validate(valid => {
if (valid) {
this.$message.success('验证通过')
} else {
this.$message.warning('验证失败')
}
})
}
}
}
</script>
指定列验证
<template>
<div>
<SkuForm
ref="skuForm"
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
/>
<el-button type="primary" style="margin-top: 10px;" @click="clear">清除验证结果</el-button>
<el-button type="primary" style="margin-top: 10px;" @click="check('price')">验证价格</el-button>
<el-button type="primary" style="margin-top: 10px;" @click="check('stock')">验证库存</el-button>
<el-button type="primary" style="margin-top: 10px;" @click="checkAll">验证多列(价格和库存)</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
],
structure: [
{
name: 'price',
type: 'input',
label: '价格',
required: true
},
{
name: 'stock',
type: 'input',
label: '库存',
tip: '库存数不能少于10',
required: true,
// data: 完整 sku 数据,index: 当前 sku 在 data 中的下标,callback: 验证结果回调函数
validate: (data, index, callback) => {
if (data[index].stock && parseInt(data[index].stock) < 10) {
callback(new Error('库存不能小于10'))
} else {
callback()
}
}
}
],
attribute: [
{
name: '颜色',
item: ['黑', '金', '白']
}
],
sku: []
}
},
methods: {
clear() {
this.$refs.skuForm.clearValidate()
},
check(name) {
let isError = false
this.$refs.skuForm.validateFieldByColumns([name], errorMessage => {
if (errorMessage) {
isError = true
}
})
if (isError) {
// 此处可以写验证失败后的业务代码
}
},
checkAll() {
let isError = false
this.$refs.skuForm.validateFieldByColumns(['price', 'stock'], errorMessage => {
if (errorMessage) {
isError = true
}
})
if (isError) {
// 此处可以写验证失败后的业务代码
}
}
}
}
</script>
异步加载
<template>
<div>
<SkuForm
ref="skuForm"
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
async
/>
<el-button type="primary" :loading="loading" style="margin-top: 10px;" @click="load(1)">模拟加载数据1</el-button>
<el-button type="primary" :loading="loading" style="margin-top: 10px;" @click="load(2)">模拟加载数据2</el-button>
<el-button type="primary" :loading="loading" style="margin-top: 10px;" @click="load(3)">模拟加载数据3</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
loading: false,
sourceAttribute: [],
structure: [
{
name: 'originalprice',
type: 'input',
label: '成本价'
},
{
name: 'price',
type: 'input',
label: '销售价'
},
{
name: 'stock',
type: 'input',
label: '库存'
}
],
attribute: [],
sku: []
}
},
mounted() {},
methods: {
load(type) {
this.loading = true
if (type == 1) {
setTimeout(() => {
this.sourceAttribute = [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
}
]
setTimeout(() => {
this.attribute = [
{
name: '颜色',
item: ['黑', '金']
},
{
name: '内存',
item: ['64G']
}
]
setTimeout(() => {
this.sku = [
{
sku: '黑;64G',
originalprice: '100',
price: '80',
stock: '100'
},
{
sku: '金;64G',
originalprice: '100',
price: '85',
stock: '50'
}
]
// 切记,必须在 attribute、sku 数据都加载后再执行 init() 方法
this.$refs.skuForm.init()
this.loading = false
}, 300)
}, 300)
}, 300)
} else if (type == 2) {
setTimeout(() => {
this.sourceAttribute = [
{
name: '内存',
item: ['16G', '32G']
},
{
name: '颜色',
item: ['黑', '金', '白']
}
]
setTimeout(() => {
this.attribute = [
{
name: '内存',
item: ['64G']
},
{
name: '颜色',
item: ['红', '白']
}
]
setTimeout(() => {
this.sku = [
{
sku: '64G;红',
originalprice: 10,
price: 8,
stock: 10
},
{
sku: '64G;白',
originalprice: 10,
price: 8,
stock: 5
}
]
// 切记,必须在 attribute、sku 数据都加载后再执行 init() 方法
this.$refs.skuForm.init()
this.loading = false
}, 300)
}, 300)
}, 300)
} else {
setTimeout(() => {
this.sourceAttribute = [
{
name: '颜色',
item: ['黑', '金', '白']
}
]
this.attribute = []
this.sku = []
this.$refs.skuForm.init()
this.loading = false
}, 300)
}
}
}
}
</script>
前台应用实现
假设我们有一组已经配置好的 SKU 数据,如下:
<template>
<div>
<SkuForm
:source-attribute="sourceAttribute"
:structure="structure"
:attribute.sync="attribute"
:sku.sync="sku"
:disabled="true"
>
<template #price="slotProps">
{{ slotProps.row.price }}
</template>
<template #stock="slotProps">
{{ slotProps.row.stock }}
</template>
</SkuForm>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">attribute 数据</el-divider>
<pre><code>{{ attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">sku 数据</el-divider>
<pre><code>{{ sku }}</code></pre>
</el-col>
</el-row>
</div>
</template>
<script>
export default {
data() {
return {
sourceAttribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
},
{
name: '运营商',
item: ['电信', '移动', '联通']
}
],
structure: [
{
name: 'price',
type: 'slot',
label: '价格'
},
{
name: 'stock',
type: 'slot',
label: '库存'
}
],
attribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
},
{
name: '运营商',
item: ['电信', '移动', '联通']
}
],
sku: [
{ sku: '黑;16G;电信', price: 100, stock: 10 },
{ sku: '黑;16G;移动', price: 101, stock: 11 },
{ sku: '黑;16G;联通', price: 102, stock: 0 },
{ sku: '黑;32G;电信', price: 103, stock: 13},
{ sku: '黑;32G;移动', price: 104, stock: 14},
{ sku: '黑;32G;联通', price: 105, stock: 0},
{ sku: '金;16G;电信', price: 106, stock: 16},
{ sku: '金;16G;移动', price: 107, stock: 17},
{ sku: '金;16G;联通', price: 108, stock: 18},
{ sku: '金;32G;电信', price: 109, stock: 0},
{ sku: '金;32G;移动', price: 110, stock: 20},
{ sku: '金;32G;联通', price: 111, stock: 21},
{ sku: '白;16G;电信', price: 112, stock: 0},
{ sku: '白;16G;移动', price: 113, stock: 23},
{ sku: '白;16G;联通', price: 114, stock: 24},
{ sku: '白;32G;电信', price: 115, stock: 0},
{ sku: '白;32G;移动', price: 116, stock: 26},
{ sku: '白;32G;联通', price: 117, stock: 27}
]
}
}
}
</script>
对于前台来说,只需要用到 attribute 和 sku 数据,当然需要对这两组数组进行加工处理下:
<template>
<el-row type="flex" class="sku-info">
<el-col v-for="(attr, index) in process_attribute" :key="index">
<h3>{{ attr.name }}</h3>
<el-button-group>
<el-button v-for="(item, index2) in attr.item" :key="index2" :type="item.actived ? 'primary' : ''" :disabled="item.disabled" @click="skuClick(index, index2)">{{ item.name }}</el-button>
</el-button-group>
</el-col>
</el-row>
<div style="margin-top: 20px;">
库存:
<span>{{ stock }}</span>
</div>
<div style="margin-bottom: 20px;">
价格:
<span v-if="minprice == maxprice">{{ minprice }}</span>
<span v-else>{{ minprice }} - {{ maxprice }}</span>
</div>
<el-button type="primary" @click="submit">购买</el-button>
<el-row type="flex" :gutter="20">
<el-col>
<el-divider content-position="left">加工处理过的 attribute 数据</el-divider>
<pre><code>{{ process_attribute }}</code></pre>
</el-col>
<el-col>
<el-divider content-position="left">加工处理过的 sku 数据</el-divider>
<pre><code>{{ process_sku }}</code></pre>
</el-col>
</el-row>
</template>
<script>
export default {
data() {
return {
// sku字段分隔符
separator: ';',
attribute: [
{
name: '颜色',
item: ['黑', '金', '白']
},
{
name: '内存',
item: ['16G', '32G']
},
{
name: '运营商',
item: ['电信', '移动', '联通']
}
],
sku: [
{ sku: '黑;16G;电信', price: 100, stock: 10 },
{ sku: '黑;16G;移动', price: 101, stock: 11 },
{ sku: '黑;16G;联通', price: 102, stock: 0 },
{ sku: '黑;32G;电信', price: 103, stock: 13},
{ sku: '黑;32G;移动', price: 104, stock: 14},
{ sku: '黑;32G;联通', price: 105, stock: 0},
{ sku: '金;16G;电信', price: 106, stock: 16},
{ sku: '金;16G;移动', price: 107, stock: 17},
{ sku: '金;16G;联通', price: 108, stock: 18},
{ sku: '金;32G;电信', price: 109, stock: 0},
{ sku: '金;32G;移动', price: 110, stock: 20},
{ sku: '金;32G;联通', price: 111, stock: 21},
{ sku: '白;16G;电信', price: 112, stock: 0},
{ sku: '白;16G;移动', price: 113, stock: 23},
{ sku: '白;16G;联通', price: 114, stock: 24},
{ sku: '白;32G;电信', price: 115, stock: 0},
{ sku: '白;32G;移动', price: 116, stock: 26},
{ sku: '白;32G;联通', price: 117, stock: 27}
],
process_attribute: [],
process_sku: [],
// 当前选中 sku 的库存及价格区间
stock: '',
minprice: '',
maxprice: ''
}
},
mounted() {
this.init()
},
methods: {
init() {
// 对 attribute 数据进行加工,并存入 process_attribute 中
this.attribute.map(attr => {
let temp = {
name: attr.name
}
temp.item = attr.item.map(item => {
return {
name: item,
actived: false,
disabled: false
}
})
this.process_attribute.push(temp)
})
// 对 sku 数据进行加工,并存入 process_sku 中
this.sku.map(v => {
var combArr = this.arrayCombine(v.sku.split(this.separator))
for (var j = 0; j < combArr.length; j++) {
var key = combArr[j].join(this.separator)
if (this.process_sku[key]) {
// 库存累加,价格添加进数组
this.process_sku[key].stock += v.stock
this.process_sku[key].prices.push(v.price)
} else {
this.process_sku[key] = {
stock: v.stock,
prices: [v.price]
}
}
}
})
// 更新数据视图
this.process_sku = Object.assign({}, this.process_sku)
this.skuCheck()
},
arrayCombine(targetArr) {
var resultArr = []
for (var n = 0; n <= targetArr.length; n++) {
var flagArrs = this.getFlagArrs(targetArr.length, n)
while (flagArrs.length) {
var flagArr = flagArrs.shift()
var combArr = Array(targetArr.length)
for (var i = 0; i < targetArr.length; i++) {
if (flagArr[i]) {
combArr[i] = targetArr[i]
}
}
resultArr.push(combArr)
}
}
return resultArr
},
getFlagArrs(m, n) {
var flagArrs = [],
flagArr = [],
isEnd = false
for (var i = 0; i < m; i++) {
flagArr[i] = i < n ? 1 : 0
}
flagArrs.push(flagArr.concat())
// 当n不等于0并且m大于n的时候进入
if (n && m > n) {
while (!isEnd) {
var leftCnt = 0
for (var i = 0; i < m - 1; i++) {
if (flagArr[i] == 1 && flagArr[i + 1] == 0) {
for (var j = 0; j < i; j++) {
flagArr[j] = j < leftCnt ? 1 : 0
}
flagArr[i] = 0
flagArr[i + 1] = 1
var aTmp = flagArr.concat()
flagArrs.push(aTmp)
if (aTmp.slice(-n).join('').indexOf('0') == -1) {
isEnd = true
}
break
}
flagArr[i] == 1 && leftCnt++
}
}
}
return flagArrs
},
skuClick(key1, key2) {
if (!this.process_attribute[key1].item[key2].disabled) {
this.process_attribute[key1].item.map((item, index) => {
item.actived = index == key2 ? !item.actived : false
})
this.skuCheck()
this.getStockPrice()
}
},
skuCheck() {
let sku = []
this.process_attribute.map(attr => {
let name = ''
attr.item.map(item => {
if (item.actived) {
name = item.name
}
})
sku.push(name)
})
this.stock = this.process_sku[sku.join(this.separator)].stock
this.minprice = Math.min.apply(Math, this.process_sku[sku.join(this.separator)].prices)
this.maxprice = Math.max.apply(Math, this.process_sku[sku.join(this.separator)].prices)
},
getStockPrice() {
this.process_attribute.map(attr => {
attr.item.map(item => {
item.disabled = false
})
})
let count = 0
let i = 0
this.process_attribute.map((attr, index) => {
let flag = false
attr.item.map(item => {
if (item.actived) {
flag = true
}
})
if (!flag) {
count += 1
i = index
}
})
// 当只有一组规格没选时
if (count == 1) {
this.process_attribute[i].item.map(item => {
let sku = []
let text = item.name
this.process_attribute.map((attr, index) => {
if (index != i) {
attr.item.map(item2 => {
if (item2.actived) {
sku.push(item2.name)
}
})
} else {
sku.push(text)
}
})
if (this.process_sku[sku.join(this.separator)].stock == 0) {
item.disabled = true
}
})
}
// 当所有规格都有选时
if (count == 0) {
this.process_attribute.map((attr, index) => {
let i = index
this.process_attribute[index].item.map(item => {
if (!item.actived) {
let sku = []
let text = item.name
this.process_attribute.map((list, index) => {
if (index != i) {
list.item.map(item2 => {
if (item2.actived) {
sku.push(item2.name)
}
})
} else {
sku.push(text)
}
})
if (this.process_sku[sku.join(this.separator)].stock == 0) {
item.disabled = true
}
}
})
})
}
},
submit() {
let sku = []
let isSelectSKU = this.process_attribute.every(attr => {
let filter = attr.item.filter(v => v.actived)
if (filter.length != 0) {
sku.push(filter[0].name)
}
return filter.length != 0
})
if (isSelectSKU) {
this.$message.success(`当前SKU为:${sku.join(this.separator)}`)
} else {
this.$message.warning('请选择完整商品属性')
}
}
}
}
</script>
源码地址:https://github.com/hooray/vue-sku-form
版权声明:本文为原创文章,版权归 全栈开发技术博客 所有。
本文链接:https://www.lvtao.net/dev/vue-sku-form.html
转载时须注明出处及本声明