feat(Sku): add new startSaleNum prop (#5105)

This commit is contained in:
Waiter 2019-11-28 10:51:27 +08:00 committed by neverland
parent d8e30bdcac
commit 17472e04c5
8 changed files with 132 additions and 42 deletions

View File

@ -141,6 +141,7 @@ export default {
| message-config | Message related config | *object* | `{}` | - | | message-config | Message related config | *object* | `{}` | - |
| get-container | Return the mount node for sku | *string \| () => Element* | - | - | | get-container | Return the mount node for sku | *string \| () => Element* | - | - |
| safe-area-inset-bottom | Whether to enable bottom safe area adaptation | *boolean* | `false` | 2.2.1 | | safe-area-inset-bottom | Whether to enable bottom safe area adaptation | *boolean* | `false` | 2.2.1 |
| start-sale-num | Minimum quantity | *number* | `1` | 2.2.15 |
### Events ### Events
@ -258,10 +259,10 @@ customStepperConfig: {
quotaText: 'only 5 can buy', quotaText: 'only 5 can buy',
// custom callback when over limit // custom callback when over limit
handleOverLimit: (data) => { handleOverLimit: (data) => {
const { action, limitType, quota, quotaUsed } = data; const { action, limitType, quota, quotaUsed, startSaleNum } = data;
if (action === 'minus') { if (action === 'minus') {
Toast('at least select one'); Toast(`at least select ${startSaleNum > 1 ? startSaleNum : 'one'}`);
} else if (action === 'plus') { } else if (action === 'plus') {
// const { LIMIT_TYPE } = Sku.skuConstants; // const { LIMIT_TYPE } = Sku.skuConstants;
if (limitType === LIMIT_TYPE.QUOTA_LIMIT) { if (limitType === LIMIT_TYPE.QUOTA_LIMIT) {

View File

@ -145,6 +145,7 @@ export default {
| initial-sku | 默认选中的 sku具体参考高级用法 | *object* | `{}` | - | | initial-sku | 默认选中的 sku具体参考高级用法 | *object* | `{}` | - |
| show-soldout-sku | 是否展示售罄的 sku默认展示并置灰 | *boolean* | `true` | - | | show-soldout-sku | 是否展示售罄的 sku默认展示并置灰 | *boolean* | `true` | - |
| safe-area-inset-bottom | 是否开启底部安全区适配,[详细说明](#/zh-CN/quickstart#di-bu-an-quan-qu-gua-pei) | *boolean* | `false` | 2.2.1 | | safe-area-inset-bottom | 是否开启底部安全区适配,[详细说明](#/zh-CN/quickstart#di-bu-an-quan-qu-gua-pei) | *boolean* | `false` | 2.2.1 |
| start-sale-num | 起售数量 | *number* | `1` | 2.2.15 |
### Events ### Events
@ -270,10 +271,10 @@ customStepperConfig: {
quotaText: '每次限购xxx件', quotaText: '每次限购xxx件',
// 自定义步进器超过限制时的回调 // 自定义步进器超过限制时的回调
handleOverLimit: (data) => { handleOverLimit: (data) => {
const { action, limitType, quota, quotaUsed } = data; const { action, limitType, quota, quotaUsed, startSaleNum } = data;
if (action === 'minus') { if (action === 'minus') {
Toast('至少选择一件商品'); Toast(startSaleNum > 1 ? `${startSaleNum}件起售` : '至少选择一件商品');
} else if (action === 'plus') { } else if (action === 'plus') {
// const { LIMIT_TYPE } = Sku.skuConstants; // const { LIMIT_TYPE } = Sku.skuConstants;
if (limitType === LIMIT_TYPE.QUOTA_LIMIT) { if (limitType === LIMIT_TYPE.QUOTA_LIMIT) {

View File

@ -45,6 +45,10 @@ export default createComponent({
type: Number, type: Number,
default: 0 default: 0
}, },
startSaleNum: {
type: Number,
default: 1
},
initialSku: { initialSku: {
type: Object, type: Object,
default: () => ({}) default: () => ({})
@ -236,22 +240,6 @@ export default createComponent({
]; ];
}, },
quotaText() {
const { quotaText, hideQuotaText } = this.customStepperConfig;
if (hideQuotaText) return '';
let text = '';
if (quotaText) {
text = quotaText;
} else if (this.quota > 0) {
text = t('quotaLimit', this.quota);
}
return text;
},
selectedText() { selectedText() {
if (this.selectedSkuComb) { if (this.selectedSkuComb) {
return `${t('selected')} ${this.selectedSkuValues.map(item => item.name).join('')}`; return `${t('selected')} ${this.selectedSkuValues.map(item => item.name).join('')}`;
@ -274,6 +262,7 @@ export default createComponent({
skuEventBus.$on('sku:numChange', this.onNumChange); skuEventBus.$on('sku:numChange', this.onNumChange);
skuEventBus.$on('sku:previewImage', this.onPreviewImage); skuEventBus.$on('sku:previewImage', this.onPreviewImage);
skuEventBus.$on('sku:overLimit', this.onOverLimit); skuEventBus.$on('sku:overLimit', this.onOverLimit);
skuEventBus.$on('sku:stepperState', this.onStepperState);
skuEventBus.$on('sku:addCart', this.onAddCart); skuEventBus.$on('sku:addCart', this.onAddCart);
skuEventBus.$on('sku:buy', this.onBuy); skuEventBus.$on('sku:buy', this.onBuy);
@ -289,6 +278,8 @@ export default createComponent({
const { skuStepper } = this.$refs; const { skuStepper } = this.$refs;
const { selectedNum } = this.initialSku; const { selectedNum } = this.initialSku;
const num = isDef(selectedNum) ? selectedNum : 1; const num = isDef(selectedNum) ? selectedNum : 1;
// 用来缓存不合法的情况
this.stepperError = null;
if (skuStepper) { if (skuStepper) {
skuStepper.setCurrentNum(num); skuStepper.setCurrentNum(num);
@ -409,18 +400,35 @@ export default createComponent({
} }
if (action === 'minus') { if (action === 'minus') {
if (this.startSaleNum > 1) {
Toast(t('minusStartTip', this.startSaleNum));
} else {
Toast(t('minusTip')); Toast(t('minusTip'));
}
} else if (action === 'plus') { } else if (action === 'plus') {
if (limitType === QUOTA_LIMIT) { if (limitType === QUOTA_LIMIT) {
let msg = t('quotaLimit', quota); if (quotaUsed > 0) {
if (quotaUsed > 0) msg += `${t('quotaCount', quotaUsed)}`; Toast(t('quotaUsedTip', quota, quotaUsed));
Toast(msg); } else {
Toast(t('quotaTip', quota));
}
} else { } else {
Toast(t('soldout')); Toast(t('soldout'));
} }
} }
}, },
onStepperState(data) {
if (data.valid) {
this.stepperError = null;
} else {
this.stepperError = {
...data,
action: 'plus',
};
}
},
onAddCart() { onAddCart() {
this.onBuyOrAddCart('add-cart'); this.onBuyOrAddCart('add-cart');
}, },
@ -430,6 +438,10 @@ export default createComponent({
}, },
onBuyOrAddCart(type) { onBuyOrAddCart(type) {
// 有信息表示该sku根本不符合购买条件
if (this.stepperError) {
return this.onOverLimit(this.stepperError);
}
const error = this.validateSku(); const error = this.validateSku();
if (error) { if (error) {
Toast(error); Toast(error);
@ -463,7 +475,6 @@ export default createComponent({
selectedSku, selectedSku,
selectedNum, selectedNum,
stepperTitle, stepperTitle,
hideQuotaText,
selectedSkuComb selectedSkuComb
} = this; } = this;
@ -494,7 +505,6 @@ export default createComponent({
{!this.hideStock && ( {!this.hideStock && (
<SkuHeaderItem> <SkuHeaderItem>
<span class="van-sku__stock">{this.stockText}</span> <span class="van-sku__stock">{this.stockText}</span>
{!hideQuotaText && this.quotaText && <span class="van-sku__quota">({this.quotaText})</span>}
</SkuHeaderItem> </SkuHeaderItem>
)} )}
{this.hasSku && !this.hideSelectedText && ( {this.hasSku && !this.hideSelectedText && (
@ -530,6 +540,7 @@ export default createComponent({
stock={this.stock} stock={this.stock}
quota={this.quota} quota={this.quota}
quotaUsed={this.quotaUsed} quotaUsed={this.quotaUsed}
startSaleNum={this.startSaleNum}
skuEventBus={skuEventBus} skuEventBus={skuEventBus}
selectedNum={selectedNum} selectedNum={selectedNum}
selectedSku={selectedSku} selectedSku={selectedSku}
@ -537,6 +548,7 @@ export default createComponent({
skuStockNum={sku.stock_num} skuStockNum={sku.stock_num}
disableStepperInput={this.disableStepperInput} disableStepperInput={this.disableStepperInput}
customStepperConfig={this.customStepperConfig} customStepperConfig={this.customStepperConfig}
hideQuotaText={this.hideQuotaText}
onChange={event => { onChange={event => {
this.$emit('stepper-change', event); this.$emit('stepper-change', event);
}} }}

View File

@ -16,6 +16,7 @@ export default createComponent({
stepperTitle: String, stepperTitle: String,
disableStepperInput: Boolean, disableStepperInput: Boolean,
customStepperConfig: Object, customStepperConfig: Object,
hideQuotaText: Boolean,
quota: { quota: {
type: Number, type: Number,
default: 0 default: 0
@ -23,7 +24,11 @@ export default createComponent({
quotaUsed: { quotaUsed: {
type: Number, type: Number,
default: 0 default: 0
} },
startSaleNum: {
type: Number,
default: 1,
},
}, },
data() { data() {
@ -40,9 +45,17 @@ export default createComponent({
}, },
stepperLimit(limit) { stepperLimit(limit) {
if (limit < this.currentNum) { if (limit < this.currentNum && this.stepperMinLimit <= limit) {
this.currentNum = limit; this.currentNum = limit;
} }
this.checkState(this.stepperMinLimit, limit);
},
stepperMinLimit(start) {
if (start > this.currentNum || start > this.stepperLimit) {
this.currentNum = start;
}
this.checkState(start, this.stepperLimit);
} }
}, },
@ -62,7 +75,35 @@ export default createComponent({
} }
return limit; return limit;
},
stepperMinLimit() {
return this.startSaleNum < 1 ? 1 : this.startSaleNum;
},
quotaText() {
const { quotaText, hideQuotaText } = this.customStepperConfig;
if (hideQuotaText) return '';
let text = '';
if (quotaText) {
text = quotaText;
} else {
const textArr = [];
if (this.startSaleNum > 1) {
textArr.push(t('quotaStart', this.startSaleNum));
} }
if (this.quota > 0) {
textArr.push(t('quotaLimit', this.quota));
}
text = textArr.join(t('comma'));
}
return text;
},
},
created() {
this.checkState(this.stepperMinLimit, this.stepperLimit);
}, },
methods: { methods: {
@ -75,7 +116,8 @@ export default createComponent({
action, action,
limitType: this.limitType, limitType: this.limitType,
quota: this.quota, quota: this.quota,
quotaUsed: this.quotaUsed quotaUsed: this.quotaUsed,
startSaleNum: this.startSaleNum,
}); });
}, },
@ -83,7 +125,27 @@ export default createComponent({
const { handleStepperChange } = this.customStepperConfig; const { handleStepperChange } = this.customStepperConfig;
handleStepperChange && handleStepperChange(currentValue); handleStepperChange && handleStepperChange(currentValue);
this.$emit('change', currentValue); this.$emit('change', currentValue);
},
checkState(min, max) {
// 如果选择小于起售,则强制变为起售
if (this.currentNum < min || min > max) {
this.currentNum = min;
} else if (this.currentNum > max) {
// 当前选择数量大于最大可选时,需要重置已选数量
this.currentNum = max;
} }
this.skuEventBus.$emit('sku:stepperState', {
valid: min <= max,
min,
max,
limitType: this.limitType,
quota: this.quota,
quotaUsed: this.quotaUsed,
startSaleNum: this.startSaleNum,
});
},
}, },
render() { render() {
@ -94,11 +156,13 @@ export default createComponent({
<Stepper <Stepper
vModel={this.currentNum} vModel={this.currentNum}
class="van-sku__stepper" class="van-sku__stepper"
min={this.stepperMinLimit}
max={this.stepperLimit} max={this.stepperLimit}
disableInput={this.disableStepperInput} disableInput={this.disableStepperInput}
onOverlimit={this.onOverLimit} onOverlimit={this.onOverLimit}
onChange={this.onChange} onChange={this.onChange}
/> />
{!this.hideQuotaText && this.quotaText && <span class="van-sku__stepper-quota">({this.quotaText})</span>}
</div> </div>
</div> </div>
); );

View File

@ -2,6 +2,7 @@ export default {
goods_id: '946755', goods_id: '946755',
quota: 15, quota: 15,
quota_used: 0, quota_used: 0,
start_sale_num: 10,
goods_info: { goods_info: {
title: '测试商品', title: '测试商品',
picture: picture:

View File

@ -11,6 +11,7 @@
:hide-stock="skuData.sku.hide_stock" :hide-stock="skuData.sku.hide_stock"
:quota="skuData.quota" :quota="skuData.quota"
:quota-used="skuData.quota_used" :quota-used="skuData.quota_used"
:start-sale-num="skuData.start_sale_num"
:close-on-click-overlay="closeOnClickOverlay" :close-on-click-overlay="closeOnClickOverlay"
:message-config="messageConfig" :message-config="messageConfig"
:custom-sku-validator="customSkuValidator" :custom-sku-validator="customSkuValidator"
@ -42,6 +43,7 @@
:hide-stock="skuData.sku.hide_stock" :hide-stock="skuData.sku.hide_stock"
:quota="skuData.quota" :quota="skuData.quota"
:quota-used="skuData.quota_used" :quota-used="skuData.quota_used"
:start-sale-num="skuData.start_sale_num"
:custom-stepper-config="customStepperConfig" :custom-stepper-config="customStepperConfig"
:message-config="messageConfig" :message-config="messageConfig"
hide-quota-text hide-quota-text
@ -70,6 +72,7 @@
:hide-stock="skuData.sku.hide_stock" :hide-stock="skuData.sku.hide_stock"
:quota="skuData.quota" :quota="skuData.quota"
:quota-used="skuData.quota_used" :quota-used="skuData.quota_used"
:start-sale-num="skuData.start_sale_num"
:custom-stepper-config="customStepperConfig" :custom-stepper-config="customStepperConfig"
:message-config="messageConfig" :message-config="messageConfig"
:show-soldout-sku="false" :show-soldout-sku="false"
@ -99,6 +102,7 @@
:hide-stock="skuData.sku.hide_stock" :hide-stock="skuData.sku.hide_stock"
:quota="skuData.quota" :quota="skuData.quota"
:quota-used="skuData.quota_used" :quota-used="skuData.quota_used"
:start-sale-num="skuData.start_sale_num"
show-add-cart-btn show-add-cart-btn
reset-stepper-on-hide reset-stepper-on-hide
safe-area-inset-bottom safe-area-inset-bottom
@ -126,7 +130,7 @@
square square
size="large" size="large"
type="danger" type="danger"
@click="props.skuEventBus.$emit('sku:buy')" @click="skuEventBus.$emit('sku:buy')"
> >
{{ $t('button2') }} {{ $t('button2') }}
</van-button> </van-button>
@ -185,10 +189,10 @@ export default {
quotaText: '单次限购100件', quotaText: '单次限购100件',
stockFormatter: (stock) => `剩余${stock}`, stockFormatter: (stock) => `剩余${stock}`,
handleOverLimit: (data) => { handleOverLimit: (data) => {
const { action, limitType, quota } = data; const { action, limitType, quota, startSaleNum = 1 } = data;
if (action === 'minus') { if (action === 'minus') {
this.$toast('至少选择一件商品'); this.$toast(startSaleNum > 1 ? `${startSaleNum}件起售` : '至少选择一件商品');
} else if (action === 'plus') { } else if (action === 'plus') {
if (limitType === LIMIT_TYPE.QUOTA_LIMIT) { if (limitType === LIMIT_TYPE.QUOTA_LIMIT) {
this.$toast(`限购${quota}`); this.$toast(`限购${quota}`);

View File

@ -198,6 +198,7 @@
&-container { &-container {
height: 30px; height: 30px;
margin-right: 20px; margin-right: 20px;
overflow: hidden;
} }
} }
@ -208,6 +209,14 @@
float: left; float: left;
line-height: 30px; line-height: 30px;
} }
&-quota {
display: inline-block;
float: right;
color: @red;
font-size: @font-size-sm;
line-height: 30px;
}
} }
&__stock { &__stock {
@ -221,12 +230,6 @@
} }
} }
&__quota {
display: inline-block;
color: @red;
font-size: @font-size-sm;
}
&-messages { &-messages {
padding-bottom: @padding-xl; padding-bottom: @padding-xl;

View File

@ -11,11 +11,12 @@ export default {
soldout: '库存不足', soldout: '库存不足',
originPrice: '原价', originPrice: '原价',
minusTip: '至少选择一件', minusTip: '至少选择一件',
minusStartTip: (start: number) => `${start}件起售`,
unavailable: '商品已经无法购买啦', unavailable: '商品已经无法购买啦',
stock: '剩余', stock: '剩余',
stockUnit: '件', stockUnit: '件',
quotaLimit: (quota: number) => `每人限购${quota}`, quotaTip: (quota: number) => `每人限购${quota}`,
quotaCount: (count: number) => `你已购买${count}` quotaUsedTip: (quota: number, count: number) => `每人限购${quota}件,你已购买${count}`
}, },
vanSkuActions: { vanSkuActions: {
buy: '立即购买', buy: '立即购买',
@ -26,6 +27,9 @@ export default {
fail: '上传失败<br />重新上传' fail: '上传失败<br />重新上传'
}, },
vanSkuStepper: { vanSkuStepper: {
quotaLimit: (quota: number) => `限购${quota}`,
quotaStart: (start: number) => `${start}件起售`,
comma: '',
num: '购买数量' num: '购买数量'
}, },
vanSkuMessages: { vanSkuMessages: {