2023-03-30 11:13:44
商品 SKU(Stock Keeping Unit,库存量单位)一般指的是同一件商品的不同规格或属性的组合。在 Vue 3 中,可以通过一个组件来实现商品 SKU 的新增和编辑功能。先看效果图
以下是一个简单的商品 SKU 新增编辑组件的示例代码:
<template>
<el-tag v-for="tag in props.guige" :key="tag" size="large" class="mx-1" closable :disable-transitions="false"
@close="handleClose(tag)">
{{ tag }}
</el-tag>
<el-input v-if="inputVisible" ref="InputRef" v-model="inputValue" class="ml-1 w-20" @keyup.enter="onConfirmguige"
@blur="onConfirmguige" />
<el-button v-else class="button-new-tag ml-1" icon="plus" @click="showInput">
新增规格
</el-button>
<el-divider v-if="props.guige.length > 0">属性值</el-divider>
<view class="flex w-full mb-2" v-for="(item, key) in props.guige" :key="key">
<span class="mr-2">{{ item }}</span>
<el-tag v-for="tag in props.attr[key]" :key="tag" size="large" class="mx-1" closable :disable-transitions="false"
@close="handleCloseAttr(key, tag)">
{{ tag }}
</el-tag>
<el-input v-if="inputVisibleAttr && todo == key" ref="InputAttrRef" v-model="inputValueAttr" class="ml-1 w-20"
@keyup.enter="onConfirmAttr" @blur="onConfirmAttr" />
<el-button v-else class="button-new-tag ml-1" icon="plus" @click="showInputAttr(key)">
新增属性
</el-button>
</view>
<el-divider v-if="props.skus.length > 0">设置价格</el-divider>
<view class="flex w-full items-center" v-if="props.skus.length > 0" >
<span class="mr-2">价格</span>
<el-input v-model="allset.price" class="w-20 m-2" placeholder="价格" />
<span class="mr-2">库存</span>
<el-input v-model="allset.stock" class="w-20 m-2" placeholder="库存" />
<el-button type="primary" @click="setAll">批量设置</el-button>
</view>
<view class="flex w-full" v-for="(item , idx) in props.skus" :key="idx" >
<el-input v-model="item.name" class="w-50 m-2" size="small" readonly placeholder="名称" />
<el-input v-model="item.price" class="w-50 m-2" size="small" placeholder="价格" />
<el-input v-model="item.stock" class="w-50 m-2" size="small" placeholder="库存" />
</view>
</template>
<script lang="ts" setup>
import { reactive,defineProps, defineEmits,ref,nextTick,defineExpose } from "vue"
import type { FormInstance, ElInput } from 'element-plus'
const props=defineProps({
guige:{
type:Array,
default:[]
},
attr:{
type:Array,
default:[]
},
skus:{
type:Array,
default:[]
}
})
const emit = defineEmits();
const inputVisible = ref(false)
const inputVisibleAttr = ref(false)
const InputRef = ref<InstanceType<typeof ElInput>>()
const InputAttrRef = ref<InstanceType<typeof ElInput>>()
const inputValue = ref<any>('')
const inputValueAttr = ref('')
const todo = ref('')
const showInput = () => {
inputVisible.value = true
nextTick(() => {
InputRef.value!.input!.focus()
})
}
const allset=reactive({
price:0,
stock:0
})
const showInputAttr = (item: any) => {
inputVisibleAttr.value = true
todo.value = item
nextTick(() => {
// InputAttrRef.value!.input!.focus()
})
}
const onConfirmguige = (value: string) => {
if (inputValue.value) {
props.guige.push(inputValue.value)
props.attr.push([])
}
inputVisible.value = false
inputValue.value = ''
}
const handleClose = (tag: string) => {
props.guige.splice(props.guige.indexOf(tag), 1)
props.attr.splice(props.guige.indexOf(tag), 1)
getskus()
}
const handleCloseAttr = (key: number, tag: string) => {
props.attr[key].splice(props.attr[key].indexOf(tag), 1)
getskus()
}
const onConfirmAttr = (value: string) => {
if (inputValueAttr.value) {
props.attr[todo.value].push(inputValueAttr.value)
}
inputVisibleAttr.value = false
inputValueAttr.value = ''
getskus()
}
//批量设置
const setAll = ()=>{
props.skus.map((item,idx)=>{
props.skus[idx].price=allset.price
props.skus[idx].stock=allset.stock
})
}
const combineArrays = (arrays: any) => {
if (arrays.length === 0) {
return [[]];
}
const result = [];
const subResult: any = combineArrays(arrays.slice(1));
for (let i = 0; i < arrays[0].length; i++) {
for (let j = 0; j < subResult.length; j++) {
result.push([arrays[0][i], ...subResult[j]]);
}
}
return result;
}
const getskus = () => {
const skuAttr = combineArrays(props.attr)
props.skus.splice(0,props.skus.length)
skuAttr.map((item: any, idx: number) => {
props.skus.push({
name:item.join('-'),
price:'',
stock:''
})
})
}
defineExpose({
...props
})
</script>
在上面的示例代码中,我们使用了 Vue 3 中的 reactive
函数来创建了一个响应式对象 state
,并定义了商品 SKU 的规格、属性、价格和库存等信息。
在模板中,我们使用了 v-model
指令来将输入框中的值绑定到 state
对象上,并使用了 @click
指令来绑定按钮的点击事件,实现了新增规格、新增属性和保存商品 SKU 的功能。
在 addSpec
和 addAttr
方法中,我们使用了 push
方法将新增的规格和属性添加到 specs
和 attrs
数组中。
在 save
方法中,我们将所有的数据提交到服务器保存。
需要注意的是,由于在 Vue 3 中使用了 reactive
函数来实现数据响应式,因此当我们修改 specs
和 attrs
数组中的数据时,这些变化会自动地反映到视图中,无需手动触发更新。