未验证 提交 34a2d208 编写于 作者: Y yangxiaolu1993 提交者: GitHub

feat: sku 商品规格组件 #64 (#827)

* feat: sku

* feat: 可配置颜色提出

* feat: prop 调整
上级 caaee8c8
......@@ -946,6 +946,17 @@
"show": false,
"exportEmpty": true,
"author": "szg2008"
},
{
"version": "3.0.0",
"taro": true,
"name": "Sku",
"type": "component",
"cName": "商品规格选择",
"desc": "商品规格选择",
"sort": 1,
"show": true,
"author": "yangxiaolu3"
}
]
}
......
<template>
<view class="nut-sku-header">
<img :src="goods.imagePath" />
<view class="nut-sku-header-right">
<template v-if="getSlots('sku-header-price')">
<slot name="sku-header-price"></slot>
</template>
<nut-price v-else :price="goods.price" :needSymbol="true" :thousands="false"> </nut-price>
<template v-if="getSlots('sku-header-extra')">
<slot name="sku-header-extra"></slot>
</template>
<view class="nut-sku-header-right-extra" v-if="goods.skuId && !getSlots('sku-header-extra')"
>商品编号:{{ goods.skuId }}</view
>
</view>
</view>
</template>
<script lang="ts">
import { ref, watch, onMounted } from 'vue';
import { createComponent } from '../../../utils/create';
const { componentName, create } = createComponent('sku-header');
export default create({
props: {
goods: {
type: Object,
default: {}
}
},
emits: [],
setup(props: any, { emit, slots }) {
const getSlots = (name: string) => slots[name];
return {
getSlots
};
}
});
</script>
<template>
<view class="nut-sku-operate" v-if="btnOptions.length > 0">
<view class="nut-sku-operate-desc" v-if="btnExtraText" v-html="btnExtraText"></view>
<slot name="operate-btn"></slot>
<view class="nut-sku-operate-btn" v-if="!getSlots('operate-btn')">
<view
:class="[`nut-sku-operate-btn-${btn}`, 'nut-sku-operate-btn-item']"
v-for="(btn, i) in btnOptions"
:key="i"
@click="clickBtnOperate(btn)"
>{{ getBtnDesc(btn) }}</view
>
</view>
</view>
</template>
<script lang="ts">
import { ref, onMounted } from 'vue';
import { createComponent } from '../../../utils/create';
const { componentName, create } = createComponent('sku-operate');
export default create({
props: {
// 底部按钮配置 confirm cart buy
btnOptions: {
type: Array,
default: () => ['confirm']
},
btnExtraText: {
type: String,
default: ''
},
// 立即购买文案
buyText: {
type: String,
default: '立即购买'
},
// 加入购物车文案
addCartText: {
type: String,
default: '加入购物车'
},
confirmText: {
type: String,
default: '确定'
}
},
emits: ['click', 'changeSku', 'changeBuyCount', 'clickBtnOperate'],
setup(props: any, { emit, slots }) {
const getBtnDesc = (type: string) => {
let mapD: { [props: string]: string } = {
confirm: props.confirmText,
cart: props.addCartText,
buy: props.buyText
};
return mapD[type];
};
onMounted(() => {
console.log(slots);
});
const getSlots = (name: string) => slots[name];
const clickBtnOperate = (btn: string) => {
emit('clickBtnOperate', btn);
};
return {
getBtnDesc,
clickBtnOperate,
getSlots
};
}
});
</script>
<template>
<view class="nut-sku-select">
<view class="nut-sku-select-item" :key="item.id" v-for="(item, index) in skuInfo">
<view class="nut-sku-select-item-title">{{ item.name }}</view>
<view class="nut-sku-select-item-skus">
<view
class="nut-sku-select-item-skus-sku"
@click="changeSaleChild(itemAttr, itemAttrIndex, item, index)"
:class="[{ active: !itemAttr.disable && itemAttr.active }, { disable: itemAttr.disable }]"
:key="itemAttr.name"
v-for="(itemAttr, itemAttrIndex) in item.list"
>
{{ itemAttr.name }}
</view>
</view>
</view>
</view>
</template>
<script lang="ts">
import { ref, watch, onMounted } from 'vue';
import { createComponent } from '../../../utils/create';
const { componentName, create } = createComponent('sku-select');
export default create({
props: {
sku: {
type: Array,
default: () => []
}
},
emits: ['selectSku'],
setup(props: any, { emit }) {
const skuInfo = ref([]);
watch(
() => props.sku,
(value) => {
// console.log('发生变化');
skuInfo.value = [].slice.call(value);
},
{ deep: true }
);
onMounted(() => {
if (props.sku.length > 0) {
skuInfo.value = [].slice.call(props.sku);
}
});
// 切换商品 Sku
const changeSaleChild = (attrItem: any, index: any, parentItem: any, parentIndex: any) => {
if (attrItem.checkFlag || attrItem.disable) {
return;
}
emit('selectSku', {
sku: attrItem,
skuIndex: index,
parentSku: parentItem,
parentIndex: parentIndex
});
};
return {
skuInfo,
changeSaleChild
};
}
});
</script>
<template>
<view class="nut-sku-stepper">
<view class="nut-sku-stepper-title">{{ stepperTitle }}</view>
<view class="nut-sku-stepper-limit" v-html="getExtraText()"></view>
<view class="nut-sku-stepper-count">
<nut-inputnumber
v-model="goodsCount"
:min="stepperMin"
:max="stepperMax"
@add="add"
@reduce="reduce"
@overlimit="overlimit"
@change="changeStepper"
/>
</view>
</view>
</template>
<script lang="ts">
import { ref, onMounted } from 'vue';
import { TypeOfFun } from '../../../utils/util';
import { createComponent } from '../../../utils/create';
const { componentName, create } = createComponent('sku-stepper');
export default create({
props: {
// 购买数量最大值
stepperMax: {
type: [Number, String],
default: 99999
},
stepperMin: {
type: [Number, String],
default: 1
},
// stepper 前文案提示
stepperExtraText: {
type: [Function, Boolean],
default: false
},
// 数量选择左侧文案
stepperTitle: {
type: String,
default: '购买数量'
}
},
emits: ['click', 'changeSku', 'changeStepper', 'clickBtnOptions', 'overLimit', 'reduce', 'add'],
setup(props: any, { emit }) {
const goodsCount = ref(props.stepperMin);
onMounted(() => {
goodsCount.value = props.stepperMin;
});
const getExtraText = () => {
const { stepperExtraText } = props;
if (stepperExtraText && TypeOfFun(stepperExtraText) == 'function') {
return stepperExtraText();
} else {
return '';
}
};
// 修改购买数量 add 加 reduce 减
const add = (value: number) => {
emit('add', value);
};
const reduce = (value: number) => {
emit('reduce', value);
};
// stepper 极限值
const overlimit = (e: Event, action: string) => {
emit('overLimit', {
action,
value: parseInt(goodsCount.value + '')
});
};
// stepper 发生了改变
const changeStepper = (value: number) => {
goodsCount.value = value;
emit('changeStepper', value);
};
return {
goodsCount,
add,
reduce,
overlimit,
getExtraText,
changeStepper
};
}
});
</script>
export const Sku = [
{
id: 1,
name: '颜色',
list: [
{
name: '亮黑色',
id: 100016015112,
active: true,
disable: false
},
{
name: '釉白色',
id: 100016015142,
active: false,
disable: false
},
{
name: '秘银色',
id: 100016015078,
active: false,
disable: false
},
{
name: '夏日胡杨',
id: 100009064831,
active: false,
disable: false
},
{
name: '秋日胡杨',
id: 100009064830,
active: false,
disable: false
}
]
},
{
id: 2,
name: '版本',
list: [
{
name: '8GB+128GB',
id: 100016015102,
active: true,
disable: false
},
{
name: '8GB+256GB',
id: 100016015122,
active: false,
disable: false
}
]
},
{
id: 3,
name: '版本',
list: [
{
name: '4G(有充版)',
id: 100016015103,
active: true,
disable: false
},
{
name: '5G(有充版)',
id: 100016015123,
active: false,
disable: false
},
{
name: '5G(无充版)',
id: 100016015104,
active: true,
disable: true
},
{
name: '5G(无充)质保换新版',
id: 100016015125,
active: false,
disable: false
}
]
}
];
export const Goods = {
skuId: '100016015112',
price: '4599.00',
imagePath:
'//m.360buyimg.com/mobilecms/s750x750_jfs/t1/210630/17/8651/208682/618a5bd6Eddc8ea0e/b5e55e1a03bc0126.jpg!q80.dpg.webp'
};
export const imagePathMap = {
100016015112:
'//m.360buyimg.com/mobilecms/s750x750_jfs/t1/210630/17/8651/208682/618a5bd6Eddc8ea0e/b5e55e1a03bc0126.jpg!q80.dpg.web',
100016015142: '//img14.360buyimg.com/n4/jfs/t1/216079/14/3895/201095/618a5c0cEe0b9e2ba/cf5b98fb6128a09e.jpg',
100016015078: '//img14.360buyimg.com/n4/jfs/t1/215845/12/3788/221990/618a5c4dEc71cb4c7/7bd6eb8d17830991.jpg',
100009064831: '//img14.360buyimg.com/n4/jfs/t1/203247/8/14659/237368/618a5c87Ecc968774/b0bb25331e5e2d1a.jpg',
100009064830: '//img14.360buyimg.com/n4/jfs/t1/160950/40/25098/234168/618a5cb9E65ba975e/7f8f93ea7767a51b.jpg'
};
<template>
<div class="demo">
<h2>基本用法</h2>
<nut-cell :title="`基本用法`" desc="" @click="base = true"></nut-cell>
<h2>不可售</h2>
<nut-cell title="不可售" desc="" @click="notSell = true"></nut-cell>
<h2>自定义计步器</h2>
<nut-cell title="自定义计步器" desc="" @click="customStepper = true"></nut-cell>
<h2>自定义插槽</h2>
<nut-cell title="通过插槽自定义设置" desc="" @click="customBySlot = true"></nut-cell>
<nut-sku
v-model:visible="base"
:sku="skuData"
:goods="goodsInfo"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
<nut-sku
v-model:visible="notSell"
:sku="skuData"
:goods="goodsInfo"
:btnExtraText="btnExtraText"
@changeStepper="changeStepper"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@close="close"
>
<template #sku-operate>
<div class="sku-operate-box">
<nut-button class="sku-operate-box-dis" type="warning">查看相似商品</nut-button>
<nut-button class="sku-operate-box-dis" type="info">到货通知</nut-button>
</div>
</template>
</nut-sku>
<nut-sku
v-model:visible="customStepper"
:sku="skuData"
:goods="goodsInfo"
:stepperMax="7"
:stepperMin="2"
:stepperExtraText="stepperExtraText"
@changeStepper="changeStepper"
@overLimit="overLimit"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
<nut-sku
v-model:visible="customBySlot"
:sku="skuData"
:goods="goodsInfo"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close()"
>
<template #sku-header-price>
<div>
<nut-price :price="goodsInfo.price" :needSymbol="true" :thousands="false"> </nut-price>
<span class="tag"></span>
</div>
</template>
<template #sku-header-extra>
<span class="nut-sku-header-right-extra">重量:0.1kg 编号:{{ goodsInfo.skuId }} </span>
</template>
<template #sku-operate>
<div class="sku-operate-box">
<nut-button class="sku-operate-item" shape="square" type="warning">加入购物车</nut-button>
<nut-button class="sku-operate-item" shape="square" type="primary">立即购买</nut-button>
</div>
</template>
<template #sku-select-top>
<div class="address">
<nut-cell
style="box-shadow: none; padding: 13px 0"
title="送至"
:desc="addressDesc"
@click="showAddressPopup = true"
></nut-cell>
</div>
</template>
</nut-sku>
<nut-address
v-model:visible="showAddressPopup"
type="exist"
:exist-address="existAddress"
@close="close"
:is-show-custom-address="false"
@selected="selectedAddress"
exist-address-title="配送至"
></nut-address>
</div>
</template>
<script lang="ts">
import { reactive, ref, toRefs, onMounted } from 'vue';
import { Sku, Goods, imagePathMap } from './data';
import { createComponent } from '../../utils/create';
import { Toast } from '@/packages/nutui.vue';
const { createDemo } = createComponent('sku');
interface Skus {
id: number;
name: string;
list: SkuItem[];
[key: string]: any;
}
interface SkuItem {
id: number;
name: string;
imagePath: string;
[key: string]: any;
}
interface GoodsProps {
skuId: string | number;
price: string; // 商品信息展示区,商品价格
imagePath?: string;
[key: string]: any;
}
interface Data {
skuData: Skus[];
goodsInfo: GoodsProps;
}
export default createDemo({
props: {},
setup() {
const popup = reactive({
base: false,
notSell: false,
customStepper: false,
customBySlot: false,
showAddressPopup: false
});
const data = reactive<Data>({
skuData: [],
goodsInfo: {}
});
const stepperExtraText = () => {
return `<div style="width:100%;text-align:right;color:#F00">2 件起售</div>`;
};
const btnExtraText = ref('抱歉,此商品在所选区域暂无存货');
const addressDesc = ref('(配送地会影响库存,请先确认)');
const existAddress = ref([
{
id: 1,
addressDetail: 'th ',
cityName: '石景山区',
countyName: '城区',
provinceName: '北京',
selectedAddress: true,
townName: ''
},
{
id: 2,
addressDetail: '12_ ',
cityName: '电饭锅',
countyName: '扶绥县',
provinceName: '北京',
selectedAddress: false,
townName: ''
},
{
id: 3,
addressDetail: '发大水比 ',
cityName: '放到',
countyName: '广宁街道',
provinceName: '钓鱼岛全区',
selectedAddress: false,
townName: ''
},
{
id: 4,
addressDetail: '还是想吧百度吧 ',
cityName: '研发',
countyName: '八里庄街道',
provinceName: '北京',
selectedAddress: false,
townName: ''
}
]);
onMounted(() => {
getData();
});
const getData = () => {
setTimeout(() => {
data.skuData = Sku;
data.goodsInfo = Goods;
}, 500);
};
const selectSku = (s: any) => {
const { sku, parentIndex } = s;
if (sku.disable) return false;
data.skuData[parentIndex].list.forEach((s) => {
s.active = s.id == sku.id;
});
data.goodsInfo = {
skuId: sku.id,
price: '4599.00' // 商品信息展示区,商品价格
};
data.skuData[0].list.forEach((el) => {
if (el.active && !el.disable) {
data.goodsInfo.imagePath = imagePathMap[el.id];
}
});
};
// stepper 更改
const changeStepper = (count: number) => {
console.log('购买数量', count);
};
// stepper 极限值
const overLimit = (val: any) => {
if (val.action == 'reduce') {
Toast.text(`至少买${val.value}件哦`);
} else {
Toast.text(`最多买${val.value}件哦`);
}
};
const clickBtnOperate = (op: string) => {
console.log('点击了操作按钮', op);
};
// 关闭弹框
const close = () => {
console.log('选择弹框关闭');
};
const selectedAddress = (prevExistAdd: any, nowExistAdd: any) => {
const { provinceName, countyName, cityName } = nowExistAdd;
addressDesc.value = `${provinceName}${countyName}${cityName}`;
};
return {
selectSku,
changeStepper,
Goods,
clickBtnOperate,
close,
existAddress,
selectedAddress,
addressDesc,
stepperExtraText,
btnExtraText,
overLimit,
...toRefs(popup),
...toRefs(data)
};
}
});
</script>
<style lang="scss" scoped>
.tag {
display: inline-block;
width: 50px;
height: 15px;
font-size: 12px;
margin-left: 10px;
background: url('//storage.360buyimg.com/imgtools/bbdf6c9a2a-e3f6fbc0-fb4d-11eb-a27f-676da10c85f4.png') no-repeat
center center;
background-size: 100% 100%;
}
.sku-operate-box {
width: 100%;
display: flex;
padding: 8px 10px;
box-sizing: border-box;
.sku-operate-item {
width: 100%;
flex-shrink: 1;
&:first-child {
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
}
&:last-child {
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
}
}
.sku-operate-box-dis {
width: 100%;
flex-shrink: 1;
&:first-child {
margin-right: 18px;
}
}
}
</style>
# Sku 商品规格组件
### 介绍
按需加载请加载对应依赖组件:Popup、InputNumber、Price
### 安装
``` javascript
import { createApp } from 'vue';
//vue
import { Sku, Popup, InputNumber, Price } from '@nutui/nutui';
//taro
import { Sku, Popup, InputNumber, Price } from '@nutui/nutui-taro';
const app = createApp();
app.use(Sku);
app.use(Popup);
app.use(InputNumber);
app.use(Price);
```
## 代码演示
### 基础用法
```html
<nut-sku
v-model:visible="base"
:sku="sku"
:goods="goods"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
```
```javascript
setup() {
const base = ref(false);
const data = reactive({
sku: [
// 具体数据结构见下方文档
],
goods: {
// 具体数据结构见下方文档
}
});
onMounted(() => {});
// 切换规格类目
const selectSku = (ss: string) => {
const { sku, skuIndex, parentSku, parentIndex } = ss;
if (sku.disable) return false;
data.sku[parentIndex].list.forEach((s) => {
s.active = s.id == sku.id;
});
data.goods = {
skuId: sku.id,
price: '4599.00',
imagePath:
'//img14.360buyimg.com/n4/jfs/t1/215845/12/3788/221990/618a5c4dEc71cb4c7/7bd6eb8d17830991.jpg'
};
};
// 底部操作按钮触发
const clickBtnOperate = (op:string)=>{
console.log('点击了操作按钮',op)
}
// 关闭商品规格弹框
const close = ()=>{}
return { base, selectSku, clickBtnOperate,close, ...toRefs(data) };
}
```
### 不可售
```html
<nut-sku
v-model:visible="notSell"
:sku="skuData"
:goods="goodsInfo"
:btnExtraText="btnExtraText"
@changeStepper="changeStepper"
@selectSku="selectSku"
@close="close"
>
<template #sku-operate>
<div class="sku-operate-box">
<nut-button class="sku-operate-box-dis" type="warning">查看相似商品</nut-button>
<nut-button class="sku-operate-box-dis" type="info">到货通知</nut-button>
</div>
</template>
</nut-sku>
```
```javascript
setup() {
const notSell = ref(false);
const data = reactive({
sku: [
// 数据结构见下方文档
],
goods: {
// 数据结构见下方文档
}
});
const btnExtraText = ref('抱歉,此商品在所选区域暂无存货');
// inputNumber 更改
const changeStepper = (count: number) => {
console.log('购买数量', count);
};
// 切换规格类目
const selectSku = (ss: string) => {
const { sku, skuIndex, parentSku, parentIndex } = ss;
if (sku.disable) return false;
data.sku[parentIndex].list.forEach((s) => {
s.active = s.id == sku.id;
});
data.goods = {
skuId: sku.id,
price: '4599.00',
imagePath:
'//img14.360buyimg.com/n4/jfs/t1/216079/14/3895/201095/618a5c0cEe0b9e2ba/cf5b98fb6128a09e.jpg'
};
};
// 底部操作按钮触发
const clickBtnOperate = (op:string)=>{
console.log('点击了操作按钮',op)
}
return { notSell, changeStepper,selectSku,btnExtraText,...toRefs(data) };
}
```
```css
.sku-operate-box {
width: 100%;
display: flex;
padding: 8px 10px;
box-sizing: border-box;
.sku-operate-box-dis{
width: 100%;
flex-shrink: 1;
&:first-child{
margin-right: 18px;
}
}
}
```
### 自定义步进器
可以按照需求配置数字输入框的最大值、最小值、文案等
```html
<nut-sku
v-model:visible="customStepper"
:sku="sku"
:goods="goods"
:showSaleLimit="true"
:stepperMax="7"
:stepperMin="2"
:stepperExtraText="stepperExtraText"
@changeStepper="changeStepper"
@overLimit="overLimit"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
```
```javascript
setup() {
const customStepper = ref(false);
const data = reactive({
sku: [
// 数据结构见下方文档
],
goods: {
// 数据结构见下方文档
}
});
const stepperExtraText = () => {
return `<div style="width:100%;text-align:right;color:#F00">2 件起售</div>`
};
// inputNumber 更改
const changeStepper = (count: number) => {
console.log('购买数量', count);
};
// inputNumber 极限值
const overLimit = (val: any) => {
if (val.action == 'reduce') {
Toast.text(`至少买${val.value}件哦`);
} else {
Toast.text(`最多买${val.value}件哦`);
}
};
// 切换规格类目
const selectSku = (ss: string) => {
const { sku, skuIndex, parentSku, parentIndex } = ss;
if (sku.disable) return false;
data.sku[parentIndex].list.forEach((s) => {
s.active = s.id == sku.id;
});
data.goods = {
skuId: sku.id,
price: '4599.00',
imagePath:
'//img14.360buyimg.com/n4/jfs/t1/215845/12/3788/221990/618a5c4dEc71cb4c7/7bd6eb8d17830991.jpg'
};
};
// 底部操作按钮触发
const clickBtnOperate = (op:string)=>{
console.log('点击了操作按钮',op)
}
return { overLimit, changeStepper,selectSku, clickBtnOperate,stepperExtraText,...toRefs(data) };
}
```
### 自定义插槽
Sku 组件默认划分为若干区域,这些区域都定义成了插槽,可以按照需求进行替换。
```html
<nut-sku
v-model:visible="customBySlot"
:sku="sku"
:goods="goods"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close()"
>
<!-- 商品展示区,价格区域 -->
<template #sku-header-price>
<div>
<nut-price :price="goodsInfo.price" :needSymbol="true" :thousands="false"> </nut-price>
<span class="tag"></span>
</div>
</template>
<!-- 商品展示区,编号区域 -->
<template #sku-header-extra>
<span class="nut-sku-header-right-extra">重量:0.1kg 编号:{{skuId}} </span>
</template>
<!-- sku 展示区上方与商品信息展示区下方区域,无默认展示内容 -->
<template #sku-select-top>
<div class="address">
<nut-cell style="box-shadow:none;padding:13px 0" title="送至" :desc="addressDesc" @click="showAddressPopup=true"></nut-cell>
</div>
</template>
<!-- 底部按钮操作区 -->
<template #sku-operate>
<div class="sku-operate-box">
<nut-button class="sku-operate-item" shape="square" type="warning">加入购物车</nut-button>
<nut-button class="sku-operate-item" shape="square" type="primary">立即购买</nut-button>
</div>
</template>
</nut-sku>
<nut-address
v-model:visible="showAddressPopup"
type="exist"
:exist-address="existAddress"
@close="close"
:is-show-custom-address="false"
@selected="selectedAddress"
exist-address-title="配送至"
></nut-address>
```
```javascript
setup() {
const customBySlot = ref(false);
const showAddressPopup = ref(false);
const data = reactive({
sku: [
// 数据结构见下方文档
],
goods: {
// 数据结构见下方文档
}
});
const addressDesc = ref('(配送地会影响库存,请先确认)');
const existAddress = ref([
{
id: 1,
addressDetail: 'th ',
cityName: '石景山区',
countyName: '城区',
provinceName: '北京',
selectedAddress: true,
townName: ''
},
{
id: 2,
addressDetail: '12 ',
cityName: '电饭锅',
countyName: '扶绥县',
provinceName: '北京',
selectedAddress: false,
townName: ''
},
{
id: 3,
addressDetail: '发大水比 ',
cityName: '放到',
countyName: '广宁街道',
provinceName: '钓鱼岛全区',
selectedAddress: false,
townName: ''
},
{
id: 4,
addressDetail: '还是想吧百度吧 ',
cityName: '研发',
countyName: '八里庄街道',
provinceName: '北京',
selectedAddress: false,
townName: ''
}
]);
// 切换规格类目
const selectSku = (ss: string) => {
const { sku, skuIndex, parentSku, parentIndex } = ss;
if (sku.disable) return false;
data.sku[parentIndex].list.forEach((s) => {
s.active = s.id == sku.id;
});
data.goods = {
skuId: sku.id,
price: '6002.10',
imagePath:
'//img14.360buyimg.com/n4/jfs/t1/215845/12/3788/221990/618a5c4dEc71cb4c7/7bd6eb8d17830991.jpg'
};
};
const selectedAddress = (prevExistAdd: any, nowExistAdd: any) => {
const { provinceName, countyName, cityName } = nowExistAdd;
addressDesc.value = `${provinceName}${countyName}${cityName}`;
};
// 底部操作按钮触发
const clickBtnOperate = (op:string)=>{
console.log('点击了操作按钮',op)
}
return { customBySlot, selectSku, clickBtnOperate,existAddress,addressDesc,selectedAddress,...toRefs(data) };
}
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------------------------|--------|------------------|
| v-model:visible | 是否显示商品规格弹框 | boolean | false |
| sku | 商品 sku 数据 | Array | [] |
| goods | 商品信息 | Object | - |
| stepper-max | 设置 inputNumber 最大值 | [String, Number] | 99999 |
| stepper-min | 设置 inputNumber 最小值 | [String, Number] | 1 |
| btn-options | 底部按钮设置。['confirm','buy','cart' ] 分别对应确定、立即购买、加入购物车 | Array | ['confirm'] |
| btn-extra-text | 按钮上部添加文案,默认为空,有值时显示 | String | - |
| stepper-title | 数量选择组件左侧文案 | String | '购买数量' |
| stepper-extra-text | inputNumber 与标题之间的文案 | [Function, false] | false |
| buy-text | 立即购买按钮文案 | String | 立即购买 |
| add-cart-text | 加入购物车按钮文案 | String | 加入购物车 |
| confirm-text | 确定按钮文案 | String | 确定 |
### Events
| 事件名 | 说明 | 回调参数 |
|--------|----------------|--------------|
| select-sku | 切换规格类目时触发 | {sku,skuIndex,parentSku,parentIndex} |
| add | inputNumber 点击增加按钮时触发 | value |
| reduce | inputNumber 点击减少按钮时触发 | value |
| overLimit | inputNumber 点击不可用的按钮时触发 | value |
| change-stepper | 购买变化时触发 | value |
| click-btn-operate | 点击底部按钮时触发 | {type:'confirm',value:'inputNumber value'} |
| click-close-icon | 点击左上角关闭 icon 时触发 | - |
| click-overlay | 点击遮罩时触发 | - |
| close | 关闭弹层时触发 | - |
### Slots
Sku 组件默认划分为若干区域,这些区域都定义成了插槽,可以按照需求进行替换。
| 事件名 | 说明 |
|--------|----------------|
| sku-header | 商品信息展示区,包含商品图片、价格、编号 |
| sku-header-price | 商品信息展示区,价格区域展示|
| sku-header-extra | 商品信息展示区,编号区域展示 |
| sku-select-top | sku 展示区上方与商品信息展示区下方区域,无默认展示内容 |
| sku-select | sku 展示区 |
| sku-stepper | 数量选择区 |
| sku-stepper-bottom | 数量选择区下方区域 |
| sku-operate | 底部按钮操作区域 |
### goods 对象结构
```javascript
goods:{
skuId:'', // 商品信息展示区,商品编号
price: "0", // 商品信息展示区,商品价格
imagePath: "", // 商品信息展示区,商品图
}
```
### sku 数组结构
sku 数组中,每一个数组索引代表一个规格类目。其中,list 代表该规格类目下的类目值。每个类目值对象包括:name、id、active(是否选中)、disable(是否可选)
```javascript
sku : [{
id: 1,
name: '颜色',
list: [{
name: '亮黑色',
id: 100016015112,
active: true,
disable: false
},
{
name: '釉白色',
id: 100016015142,
active: false,
disable: false
},
{
name: '秘银色',
id: 100016015078,
active: false,
disable: false
},
{
name: '夏日胡杨',
id: 100009064831,
active: false,
disable: false
},
{
name: '秋日胡杨',
id: 100009064830,
active: false,
disable: false
}
]
},
{
id: 2,
name: '版本',
list: [{
name: '8GB+128GB',
id: 100016015102,
active: true,
disable: false
},
{
name: '8GB+256GB',
id: 100016015122,
active: false,
disable: false
}
]
},
{
id: 3,
name: '版本',
list: [{
name: '4G(有充版)',
id: 100016015103,
active: true,
disable: false
},
{
name: '5G(有充版)',
id: 100016015123,
active: false,
disable: false
},
{
name: '5G(无充版)',
id: 100016015104,
active: true,
disable: true
},
{
name: '5G(无充)质保换新版',
id: 100016015125,
active: false,
disable: false
}
]
}
];
```
.nut-sku {
height: 100%;
display: flex;
flex-direction: column;
padding: 0px;
background: $white;
&-header {
height: 100px;
display: flex;
flex-shrink: 0;
margin-top: 18px;
padding: 0 18px;
> img {
width: 100px;
height: 100px;
flex-shrink: 0;
margin-right: 12px;
}
&-right {
flex: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
&-extra {
font-size: 12px;
color: $text-color;
}
}
}
&-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
&::-webkit-scrollbar {
display: none;
}
margin-top: 24px;
padding: 0 18px;
}
&-select {
&-item {
display: flex;
flex-direction: column;
&-title {
height: 13px;
font-weight: bold;
font-size: 13px;
color: $black;
margin-bottom: 18px;
}
&-skus {
display: flex;
flex-wrap: wrap;
&-sku {
margin-right: 12px;
height: 30px;
line-height: 30px;
padding: 0 18px;
border-radius: 15px;
font-size: 11px;
color: $black;
flex-shrink: 0;
margin-bottom: 12px;
background: rgba(242, 242, 242, 1);
border: 1px solid rgba(242, 242, 242, 1);
&.active {
background: rgba(252, 237, 235, 1);
border: $sku-item-border;
color: $primary-color;
}
&.disable {
color: $text-color;
text-decoration: $sku-item-disable-line;
}
}
}
}
}
&-stepper {
display: flex;
justify-content: space-between;
margin: 10px 0 30px;
&-title {
font-weight: bold;
font-size: 13px;
color: $black;
margin-right: 12px;
}
&-limit {
flex: 1;
display: flex;
align-items: center;
font-size: 12px;
color: $text-color;
}
&-count {
display: flex;
align-items: center;
&-lowestBuy {
font-size: 12px;
color: $primary-color;
}
}
}
&-operate {
width: 100%;
&-desc {
display: block;
width: 100%;
padding: 10px 0;
text-align: center;
background: #fbf9da;
color: #de6a1c;
font-size: 12px;
}
&-btn {
height: 54px;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
background: $white;
text-align: center;
padding: 0 18px;
box-sizing: border-box;
&-item {
width: 100%;
height: 40px;
line-height: 40px;
margin-right: 18px;
background: $sku-opetate-bg-default;
border-radius: 21px;
font-size: 15px;
color: $white;
&:last-child {
margin-right: 0;
}
}
&-buy {
background: $sku-opetate-bg-buy;
}
}
}
}
<template>
<nut-popup
position="bottom"
closeable
round
v-model:visible="showPopup"
@click-close-icon="closePopup('icon')"
@click-overlay="closePopup('overlay')"
@close="closePopup('close')"
style="height: 75%"
>
<view class="nut-sku">
<slot name="sku-header"></slot>
<sku-header :goods="goods" v-if="!getSlots('sku-header')">
<template #sku-header-price v-if="getSlots('sku-header-price')">
<slot name="sku-header-price"></slot>
</template>
<template #sku-header-extra v-if="getSlots('sku-header-extra')">
<slot name="sku-header-extra"></slot>
</template>
</sku-header>
<view class="nut-sku-content">
<slot name="sku-select-top"></slot>
<slot name="sku-select"></slot>
<SkuSelect v-if="!getSlots('sku-select')" :sku="sku" @selectSku="selectSku"></SkuSelect>
<slot name="sku-stepper"></slot>
<sku-stepper
v-if="!getSlots('sku-stepper')"
:goods="goods"
:stepperTitle="stepperTitle"
:stepperMax="stepperMax"
:stepperMin="stepperMin"
:purchased="purchased"
:showSaleLimit="showSaleLimit"
:showSaleLowest="showSaleLowest"
:saleLowestText="saleLowestText"
:saleLimitText="saleLimitText"
:purchasedText="purchasedText"
@add="add"
@reduce="reduce"
@changeStepper="changeStepper"
@stepperOverLimit="stepperOverLimit"
></sku-stepper>
<slot name="sku-stepper-bottom"></slot>
</view>
<slot name="sku-operate"></slot>
<sku-operate
v-if="!getSlots('sku-operate')"
:btnOptions="btnOptions"
:buyText="buyText"
:addCartText="addCartText"
:confirmText="confirmText"
@clickBtnOperate="clickBtnOperate"
></sku-operate>
</view>
</nut-popup>
</template>
<script lang="ts">
import { ref, watch, onMounted } from 'vue';
import SkuHeader from './components/SkuHeader.vue';
import SkuSelect from './components/SkuSelect.vue';
import SkuStepper from './components/SkuStepper.vue';
import SkuOperate from './components/SkuOperate.vue';
import { createComponent } from '../../utils/create';
const { componentName, create } = createComponent('sku');
export default create({
props: {
visible: {
type: Boolean,
default: false
},
sku: {
type: Array,
default: []
},
goods: {
type: Object,
default: {}
},
// 是否显示限购文案
showSaleLimit: {
type: Boolean,
default: false
},
// stepper 最大值
stepperMax: {
type: [Number, String],
default: 99999
},
// stepper 最小值
stepperMin: {
type: [Number, String],
default: 1
},
// 已购数量
purchased: {
type: [Number, String],
default: 0
},
// 是否显示起购文案
showSaleLowest: {
type: Boolean,
default: false
},
// 底部按钮配置 confirm cart buy
btnOptions: {
type: Array,
default: () => ['confirm']
},
// 数量选择左侧文案
stepperTitle: {
type: String,
default: '购买数量'
},
// 起购文案提示
saleLowestText: {
type: [Function, Boolean],
default: false
},
// 限购文案提示
saleLimitText: {
type: [Function, Boolean],
default: false
},
// 已购文案提示
purchasedText: {
type: [Function, Boolean],
default: false
},
// 立即购买文案
buyText: {
type: String,
default: '立即购买'
},
// 加入购物车文案
addCartText: {
type: String,
default: '加入购物车'
},
// 确定文案
confirmText: {
type: String,
default: '确定'
}
},
emits: [
'update:visible',
'selectSku',
'changeStepper',
'clickBtnOperate',
'clickCloseIcon',
'clickOverlay',
'close',
'reduce',
'add',
'overLimit'
],
components: {
SkuHeader,
SkuSelect,
SkuStepper,
SkuOperate
},
setup(props: any, { emit, slots }) {
const showPopup = ref(props.visible);
const goodsCount = ref(props.stepperMin);
watch(
() => props.visible,
(value) => {
showPopup.value = value;
}
);
watch(
() => showPopup.value,
(value) => {
if (value == false) {
close();
}
}
);
const getSlots = (name: string) => slots[name];
// 商品规格 sku 选择
const selectSku = (skus: any) => {
emit('selectSku', skus);
};
// 数量计步器变化
const changeStepper = (value: number) => {
goodsCount.value = value;
emit('changeStepper', value);
};
// 修改购买数量 add 加 reduce 减
const add = (value: number) => {
emit('add', value);
};
const reduce = (value: number) => {
emit('reduce', value);
};
// 触发极限值
const stepperOverLimit = (count: any) => {
emit('overLimit', count);
};
// 点击 button 操作
const clickBtnOperate = (btn: string) => {
emit('clickBtnOperate', {
type: btn,
value: goodsCount.value
});
};
// 关闭
const closePopup = (type: string) => {
if (type == 'icon') {
emit('click-close-icon');
}
if (type == 'overlay') {
emit('click-overlay');
}
if (type == 'close') {
emit('close');
}
showPopup.value = false;
};
const close = () => {
emit('update:visible', false);
};
return {
showPopup,
closePopup,
selectSku,
changeStepper,
stepperOverLimit,
clickBtnOperate,
add,
reduce,
getSlots
};
}
});
</script>
<template>
<nut-popup
position="bottom"
closeable
round
v-model:visible="showPopup"
@click-close-icon="closePopup('icon')"
@click-overlay="closePopup('overlay')"
@close="closePopup('close')"
style="height: 75%"
>
<view class="nut-sku">
<slot name="sku-header"></slot>
<sku-header :goods="goods" v-if="!getSlots('sku-header')">
<template #sku-header-price v-if="getSlots('sku-header-price')">
<slot name="sku-header-price"></slot>
</template>
<template #sku-header-extra v-if="getSlots('sku-header-extra')">
<slot name="sku-header-extra"></slot>
</template>
</sku-header>
<view class="nut-sku-content">
<slot name="sku-select-top"></slot>
<slot name="sku-select"></slot>
<SkuSelect v-if="!getSlots('sku-select')" :sku="sku" @selectSku="selectSku"></SkuSelect>
<slot name="sku-stepper"></slot>
<sku-stepper
v-if="!getSlots('sku-stepper')"
:goods="goods"
:stepperTitle="stepperTitle"
:stepperMax="stepperMax"
:stepperMin="stepperMin"
:stepperExtraText="stepperExtraText"
@add="add"
@reduce="reduce"
@changeStepper="changeStepper"
@overLimit="stepperOverLimit"
></sku-stepper>
<slot name="sku-stepper-bottom"></slot>
</view>
<sku-operate
:btnOptions="btnOptions"
:btnExtraText="btnExtraText"
:buyText="buyText"
:addCartText="addCartText"
:confirmText="confirmText"
@clickBtnOperate="clickBtnOperate"
>
<template #operate-btn v-if="getSlots('sku-operate')">
<slot name="sku-operate"></slot>
</template>
</sku-operate>
</view>
</nut-popup>
</template>
<script lang="ts">
import { ref, watch, onMounted } from 'vue';
import SkuHeader from './components/SkuHeader.vue';
import SkuSelect from './components/SkuSelect.vue';
import SkuStepper from './components/SkuStepper.vue';
import SkuOperate from './components/SkuOperate.vue';
import { createComponent } from '../../utils/create';
const { componentName, create } = createComponent('sku');
export default create({
props: {
visible: {
type: Boolean,
default: false
},
sku: {
type: Array,
default: []
},
goods: {
type: Object,
default: {}
},
// stepper 最大值
stepperMax: {
type: [Number, String],
default: 99999
},
// stepper 最小值
stepperMin: {
type: [Number, String],
default: 1
},
// 底部按钮配置 confirm cart buy
btnOptions: {
type: Array,
default: () => ['confirm']
},
// 数量选择左侧文案
stepperTitle: {
type: String,
default: '购买数量'
},
// stepper 前面文案
stepperExtraText: {
type: [Function, Boolean],
default: false
},
btnExtraText: {
type: String,
default: ''
},
// 立即购买文案
buyText: {
type: String,
default: '立即购买'
},
// 加入购物车文案
addCartText: {
type: String,
default: '加入购物车'
},
// 确定文案
confirmText: {
type: String,
default: '确定'
}
},
emits: [
'update:visible',
'selectSku',
'changeStepper',
'clickBtnOperate',
'clickCloseIcon',
'clickOverlay',
'close',
'reduce',
'add',
'overLimit',
'clickOverlay'
],
components: {
SkuHeader,
SkuSelect,
SkuStepper,
SkuOperate
},
setup(props: any, { emit, slots }) {
const showPopup = ref(props.visible);
const goodsCount = ref(props.stepperMin);
watch(
() => props.visible,
(value) => {
showPopup.value = value;
}
);
watch(
() => showPopup.value,
(value) => {
if (value == false) {
close();
}
}
);
onMounted(() => {
console.log('更新参数');
});
const getSlots = (name: string) => slots[name];
// 商品规格 sku 选择
const selectSku = (skus: any) => {
emit('selectSku', skus);
};
// 数量计步器变化
const changeStepper = (value: number) => {
goodsCount.value = value;
emit('changeStepper', value);
};
// 修改购买数量 add 加 reduce 减
const add = (value: number) => {
emit('add', value);
};
const reduce = (value: number) => {
emit('reduce', value);
};
// 触发极限值
const stepperOverLimit = (count: any) => {
emit('overLimit', count);
};
// 点击 button 操作
const clickBtnOperate = (btn: string) => {
emit('clickBtnOperate', {
type: btn,
value: goodsCount.value
});
};
// 关闭
const closePopup = (type: string) => {
if (type == 'icon') {
emit('click-close-icon');
}
if (type == 'overlay') {
emit('click-overlay');
}
if (type == 'close') {
emit('close');
}
showPopup.value = false;
};
const close = () => {
emit('update:visible', false);
};
return {
showPopup,
closePopup,
selectSku,
changeStepper,
stepperOverLimit,
clickBtnOperate,
add,
reduce,
getSlots
};
}
});
</script>
......@@ -362,5 +362,21 @@ $searchbar-background: $white !default;
$searchbar-input-background: #f7f7f7 !default;
$searchbar-right-out-color: $black !default;
// sku
$sku-item-border: 1px solid $primary-color;
$sku-item-disable-line: line-through;
$sku-opetate-bg-default: linear-gradient(
135deg,
rgba(242, 20, 12, 1) 0%,
rgba(242, 39, 12, 1) 70%,
rgba(242, 77, 12, 1) 100%
);
$sku-opetate-bg-buy: linear-gradient(
135deg,
rgba(255, 186, 13, 1) 0%,
rgba(255, 195, 13, 1) 69%,
rgba(255, 207, 13, 1) 100%
);
@import './mixins/index';
@import './animation/index';
// 变量类型判断
export const TypeOfFun = (value: any) => {
if (null === value) {
return 'null';
}
const type = typeof value;
if ('undefined' === type || 'string' === type) {
return type;
}
const typeString = toString.call(value);
switch (typeString) {
case '[object Array]':
return 'array';
case '[object Date]':
return 'date';
case '[object Boolean]':
return 'boolean';
case '[object Number]':
return 'number';
case '[object Function]':
return 'function';
case '[object RegExp]':
return 'regexp';
case '[object Object]':
if (undefined !== value.nodeType) {
if (3 == value.nodeType) {
return /\S/.test(value.nodeValue) ? 'textnode' : 'whitespace';
} else {
return 'element';
}
} else {
return 'object';
}
default:
return 'unknow';
}
};
......@@ -76,7 +76,13 @@ export default {
},
{
root: 'business',
pages: ['pages/address/index', 'pages/signature/index', 'pages/barrage/index', 'pages/timeselect/index']
pages: [
'pages/address/index',
'pages/signature/index',
'pages/barrage/index',
'pages/timeselect/index',
'pages/sku/index'
]
}
],
window: {
......
export const Sku = [
{
id: 1,
name: '种类',
list: [
{
name: '五香味150g*3',
id: 100016015112,
_active: true,
_disable: false
},
{
name: '五香味150g*8',
id: 100016015142,
_active: false,
_disable: true
},
{
name: '香辣味150g*3',
id: 100016015078,
_active: false,
_disable: false
},
{
name: '香辣味150g*8',
id: 100009064831,
_active: false,
_disable: true
}
]
},
{
id: 2,
name: '规格',
list: [
{
name: '150g',
id: 100016015102,
_active: true,
_disable: false
},
{
name: '150g*8',
id: 100016015122,
_active: false,
_disable: false
}
]
}
];
export const Goods = {
skuId: '100016015112',
price: '9.10',
imagePath:
'https://img13.360buyimg.com/imagetools/s750x750_jfs/t1/155184/15/3792/210311/5f98da84Efb6e1da6/d2a3b5ca1fc7019c.jpg'
};
export default {
navigationBarTitleText: 'Sku',
disableScroll: true
};
<template>
<div class="demo">
<h2>基本用法</h2>
<nut-cell :title="`基本用法`" desc="" @click="base = true"></nut-cell>
<h2>限购模式</h2>
<nut-cell title="设置限购、已购、起购" desc="" @click="openQuota = true"></nut-cell>
<h2>自定义计步器</h2>
<nut-cell title="自定义计步器" desc="" @click="customStepper = true"></nut-cell>
<h2>自定义插槽</h2>
<nut-cell title="通过插槽自定义设置" desc="" @click="customBySlot = true"></nut-cell>
<nut-sku
v-model:visible="base"
:sku="skuData"
:goods="goodsInfo"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
<nut-sku
v-model:visible="openQuota"
:sku="skuData"
:goods="goodsInfo"
:showSaleLimit="true"
:showSaleLowest="true"
:stepperMax="7"
:stepperMin="2"
:purchased="2"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
<nut-sku
v-model:visible="customStepper"
:sku="skuData"
:goods="goodsInfo"
:showSaleLimit="true"
:stepperMax="7"
:stepperMin="2"
:saleLowestText="saleLowestText"
:saleLimitText="saleLimitText"
@changeStepper="changeStepper"
@overLimit="overLimit"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close"
></nut-sku>
<nut-sku
v-model:visible="customBySlot"
:sku="skuData"
:goods="goodsInfo"
:btnOptions="['buy', 'cart']"
@selectSku="selectSku"
@clickBtnOperate="clickBtnOperate"
@close="close()"
>
<template #sku-header-price>
<div>
<nut-price :price="goodsInfo.price" :needSymbol="true" :thousands="false"> </nut-price>
<span class="tag"></span>
</div>
</template>
<template #sku-header-extra>
<span class="nut-sku-header-right-extra">重量:0.1kg 编号:{{ goodsInfo.skuId }} </span>
</template>
<template #sku-operate>
<div class="sku-operate-box">
<nut-button class="sku-operate-item" shape="square" type="warning">加入购物车</nut-button>
<nut-button class="sku-operate-item" shape="square" type="primary">立即购买</nut-button>
</div>
</template>
<template #sku-select-top>
<div class="address">
<nut-cell
style="box-shadow: none; padding: 13px 0"
title="送至"
:desc="addressDesc"
@click="showAddressPopup = true"
></nut-cell>
</div>
</template>
</nut-sku>
<nut-address
v-model:visible="showAddressPopup"
type="exist"
:exist-address="existAddress"
@close="close"
:is-show-custom-address="false"
@selected="selectedAddress"
exist-address-title="配送至"
></nut-address>
</div>
</template>
<script lang="ts">
import { reactive, ref, toRefs, onMounted, defineComponent } from 'vue';
import { Sku, Goods } from './data';
interface Skus {
id: number;
name: string;
list: SkuItem[];
[key: string]: any;
}
interface SkuItem {
id: number;
name: string;
imagePath: string;
[key: string]: any;
}
interface GoodsProps {
skuId: string | number;
price: string; // 商品信息展示区,商品价格
imagePath: string;
[key: string]: any;
}
interface Data {
skuData: Skus[];
goodsInfo: GoodsProps;
}
export default defineComponent({
props: {},
setup() {
const popup = reactive({
base: false,
openQuota: false,
customStepper: false,
customBySlot: false,
showAddressPopup: false
});
const data = reactive<Data>({
skuData: [],
goodsInfo: {}
});
const saleLowestText = (min: string) => `${min} 件起售`;
const saleLimitText = (max: string) => `最多买${max}件`;
const addressDesc = ref('(配送地会影响库存,请先确认)');
const existAddress = ref([
{
id: 1,
addressDetail: 'th ',
cityName: '石景山区',
countyName: '城区',
provinceName: '北京',
selectedAddress: true,
townName: ''
},
{
id: 2,
addressDetail: '12_ ',
cityName: '电饭锅',
countyName: '扶绥县',
provinceName: '北京',
selectedAddress: false,
townName: ''
},
{
id: 3,
addressDetail: '发大水比 ',
cityName: '放到',
countyName: '广宁街道',
provinceName: '钓鱼岛全区',
selectedAddress: false,
townName: ''
},
{
id: 4,
addressDetail: '还是想吧百度吧 ',
cityName: '研发',
countyName: '八里庄街道',
provinceName: '北京',
selectedAddress: false,
townName: ''
}
]);
onMounted(() => {
getData();
});
const getData = () => {
setTimeout(() => {
data.skuData = Sku;
data.goodsInfo = Goods;
}, 500);
};
const selectSku = (s: any) => {
const { sku, skuIndex, parentSku, parentIndex } = s;
if (sku._disable) return false;
data.skuData[parentIndex].list.forEach((s) => {
s._active = s.id == sku.id;
});
data.goodsInfo = {
skuId: sku.id,
price: '9.10', // 商品信息展示区,商品价格
imagePath:
'https://img20.360buyimg.com/imagetools/s750x750_jfs/t1/201286/22/5692/60152/6136fb94Eea1a9d48/211f40f9d27e6cea.jpg' // 商品信息展示区,商品图
};
};
// stepper 更改
const changeStepper = (count: number) => {
console.log('购买数量', count);
};
// stepper 极限值
const overLimit = (val: any) => {
if (val.action == 'reduce') {
console.log(`至少买${val.value}件哦`);
} else {
console.log(`最多买${val.value}件哦`);
}
};
const clickBtnOperate = (op: string) => {
console.log('点击了操作按钮', op);
};
// 关闭弹框
const close = () => {
console.log('选择弹框关闭');
};
const selectedAddress = (prevExistAdd: any, nowExistAdd: any) => {
const { provinceName, countyName, cityName } = nowExistAdd;
addressDesc.value = `${provinceName}${countyName}${cityName}`;
};
return {
selectSku,
changeStepper,
Goods,
clickBtnOperate,
close,
existAddress,
selectedAddress,
addressDesc,
saleLowestText,
saleLimitText,
overLimit,
...toRefs(popup),
...toRefs(data)
};
}
});
</script>
<style lang="scss">
.tag {
display: inline-block;
width: 50px;
height: 15px;
font-size: 12px;
margin-left: 10px;
background: url('//storage.360buyimg.com/imgtools/bbdf6c9a2a-e3f6fbc0-fb4d-11eb-a27f-676da10c85f4.png') no-repeat
center center;
background-size: 100% 100%;
}
.sku-operate-box {
width: 100%;
display: flex;
padding: 8px 0px;
.sku-operate-item {
width: 100%;
flex-shrink: 1;
&:first-child {
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
}
&:last-child {
border-top-right-radius: 20px;
border-bottom-right-radius: 20px;
}
}
}
</style>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册