You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

748 lines
22 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

import _extends from "@babel/runtime/helpers/esm/extends";
import Vue from 'vue';
import Popup from '../popup';
import Toast from '../toast';
import ImagePreview from '../image-preview';
import SkuHeader from './components/SkuHeader';
import SkuHeaderItem from './components/SkuHeaderItem';
import SkuRow from './components/SkuRow';
import SkuRowItem from './components/SkuRowItem';
import SkuRowPropItem from './components/SkuRowPropItem';
import SkuStepper from './components/SkuStepper';
import SkuMessages from './components/SkuMessages';
import SkuActions from './components/SkuActions';
import { createNamespace } from '../utils';
import { isAllSelected, isSkuChoosable, getSkuComb, getSelectedSkuValues, getSelectedPropValues, getSelectedProperties } from './utils/sku-helper';
import { LIMIT_TYPE, UNSELECTED_SKU_VALUE_ID } from './constants';
var namespace = createNamespace('sku');
var createComponent = namespace[0],
bem = namespace[1],
t = namespace[2];
var QUOTA_LIMIT = LIMIT_TYPE.QUOTA_LIMIT;
export default createComponent({
props: {
sku: Object,
goods: Object,
value: Boolean,
buyText: String,
goodsId: [Number, String],
priceTag: String,
lazyLoad: Boolean,
hideStock: Boolean,
properties: Array,
addCartText: String,
stepperTitle: String,
getContainer: [String, Function],
hideQuotaText: Boolean,
hideSelectedText: Boolean,
resetStepperOnHide: Boolean,
customSkuValidator: Function,
disableStepperInput: Boolean,
resetSelectedSkuOnHide: Boolean,
quota: {
type: Number,
default: 0
},
quotaUsed: {
type: Number,
default: 0
},
startSaleNum: {
type: Number,
default: 1
},
initialSku: {
type: Object,
default: function _default() {
return {};
}
},
stockThreshold: {
type: Number,
default: 50
},
showSoldoutSku: {
type: Boolean,
default: true
},
showAddCartBtn: {
type: Boolean,
default: true
},
disableSoldoutSku: {
type: Boolean,
default: true
},
customStepperConfig: {
type: Object,
default: function _default() {
return {};
}
},
showHeaderImage: {
type: Boolean,
default: true
},
previewOnClickImage: {
type: Boolean,
default: true
},
safeAreaInsetBottom: {
type: Boolean,
default: true
},
closeOnClickOverlay: {
type: Boolean,
default: true
},
bodyOffsetTop: {
type: Number,
default: 200
},
messageConfig: {
type: Object,
default: function _default() {
return {
initialMessages: {},
placeholderMap: {},
uploadImg: function uploadImg() {
return Promise.resolve();
},
uploadMaxSize: 5
};
}
}
},
data: function data() {
return {
selectedSku: {},
selectedProp: {},
selectedNum: 1,
show: this.value
};
},
watch: {
show: function show(val) {
this.$emit('input', val);
if (!val) {
this.$emit('sku-close', {
selectedSkuValues: this.selectedSkuValues,
selectedNum: this.selectedNum,
selectedSkuComb: this.selectedSkuComb
});
if (this.resetStepperOnHide) {
this.resetStepper();
}
if (this.resetSelectedSkuOnHide) {
this.resetSelectedSku();
}
}
},
value: function value(val) {
this.show = val;
},
skuTree: 'resetSelectedSku',
initialSku: function initialSku() {
this.resetStepper();
this.resetSelectedSku();
}
},
computed: {
skuGroupClass: function skuGroupClass() {
return ['van-sku-group-container', {
'van-sku-group-container--hide-soldout': !this.showSoldoutSku
}];
},
bodyStyle: function bodyStyle() {
if (this.$isServer) {
return;
}
var maxHeight = window.innerHeight - this.bodyOffsetTop;
return {
maxHeight: maxHeight + 'px'
};
},
isSkuCombSelected: function isSkuCombSelected() {
var _this = this;
// SKU 未选完
if (this.hasSku && !isAllSelected(this.skuTree, this.selectedSku)) {
return false;
} // 属性未全选
return !this.propList.some(function (it) {
return (_this.selectedProp[it.k_id] || []).length < 1;
});
},
isSkuEmpty: function isSkuEmpty() {
return Object.keys(this.sku).length === 0;
},
hasSku: function hasSku() {
return !this.sku.none_sku;
},
hasSkuOrAttr: function hasSkuOrAttr() {
return this.hasSku || this.propList.length > 0;
},
selectedSkuComb: function selectedSkuComb() {
var skuComb = null;
if (this.isSkuCombSelected) {
if (this.hasSku) {
skuComb = getSkuComb(this.skuList, this.selectedSku);
} else {
skuComb = {
id: this.sku.collection_id,
price: Math.round(this.sku.price * 100),
stock_num: this.sku.stock_num
};
}
if (skuComb) {
skuComb.properties = getSelectedProperties(this.propList, this.selectedProp);
skuComb.property_price = this.selectedPropValues.reduce(function (acc, cur) {
return acc + (cur.price || 0);
}, 0);
}
}
return skuComb;
},
selectedSkuValues: function selectedSkuValues() {
return getSelectedSkuValues(this.skuTree, this.selectedSku);
},
selectedPropValues: function selectedPropValues() {
return getSelectedPropValues(this.propList, this.selectedProp);
},
price: function price() {
if (this.selectedSkuComb) {
return ((this.selectedSkuComb.price + this.selectedSkuComb.property_price) / 100).toFixed(2);
} // sku.price是一个格式化好的价格区间
return this.sku.price;
},
originPrice: function originPrice() {
if (this.selectedSkuComb && this.selectedSkuComb.origin_price) {
return ((this.selectedSkuComb.origin_price + this.selectedSkuComb.property_price) / 100).toFixed(2);
}
return this.sku.origin_price;
},
skuTree: function skuTree() {
return this.sku.tree || [];
},
skuList: function skuList() {
return this.sku.list || [];
},
propList: function propList() {
return this.properties || [];
},
imageList: function imageList() {
var imageList = [this.goods.picture];
if (this.skuTree.length > 0) {
this.skuTree.forEach(function (treeItem) {
if (!treeItem.v) {
return;
}
treeItem.v.forEach(function (vItem) {
var imgUrl = vItem.previewImgUrl || vItem.imgUrl || vItem.img_url;
if (imgUrl && imageList.indexOf(imgUrl) === -1) {
imageList.push(imgUrl);
}
});
});
}
return imageList;
},
stock: function stock() {
var stockNum = this.customStepperConfig.stockNum;
if (stockNum !== undefined) {
return stockNum;
}
if (this.selectedSkuComb) {
return this.selectedSkuComb.stock_num;
}
return this.sku.stock_num;
},
stockText: function stockText() {
var h = this.$createElement;
var stockFormatter = this.customStepperConfig.stockFormatter;
if (stockFormatter) {
return stockFormatter(this.stock);
}
return [t('stock') + " ", h("span", {
"class": bem('stock-num', {
highlight: this.stock < this.stockThreshold
})
}, [this.stock]), " " + t('stockUnit')];
},
selectedText: function selectedText() {
var _this2 = this;
if (this.selectedSkuComb) {
var values = this.selectedSkuValues.concat(this.selectedPropValues);
return t('selected') + " " + values.map(function (item) {
return item.name;
}).join(' ');
}
var unselectedSku = this.skuTree.filter(function (item) {
return _this2.selectedSku[item.k_s] === UNSELECTED_SKU_VALUE_ID;
}).map(function (item) {
return item.k;
});
var unselectedProp = this.propList.filter(function (item) {
return (_this2.selectedProp[item.k_id] || []).length < 1;
}).map(function (item) {
return item.k;
});
return t('select') + " " + unselectedSku.concat(unselectedProp).join(' ');
}
},
created: function created() {
var skuEventBus = new Vue();
this.skuEventBus = skuEventBus;
skuEventBus.$on('sku:select', this.onSelect);
skuEventBus.$on('sku:propSelect', this.onPropSelect);
skuEventBus.$on('sku:numChange', this.onNumChange);
skuEventBus.$on('sku:previewImage', this.onPreviewImage);
skuEventBus.$on('sku:overLimit', this.onOverLimit);
skuEventBus.$on('sku:stepperState', this.onStepperState);
skuEventBus.$on('sku:addCart', this.onAddCart);
skuEventBus.$on('sku:buy', this.onBuy);
this.resetStepper();
this.resetSelectedSku(); // 组件初始化后的钩子抛出skuEventBus
this.$emit('after-sku-create', skuEventBus);
},
methods: {
resetStepper: function resetStepper() {
var skuStepper = this.$refs.skuStepper;
var selectedNum = this.initialSku.selectedNum;
var num = selectedNum != null ? selectedNum : this.startSaleNum; // 用来缓存不合法的情况
this.stepperError = null;
if (skuStepper) {
skuStepper.setCurrentNum(num);
} else {
// 当首次加载skuStepper 为空)时,传入数量如果不合法,可能会存在问题
this.selectedNum = num;
}
},
// @exposed-api
resetSelectedSku: function resetSelectedSku() {
var _this3 = this;
this.selectedSku = {}; // 重置 selectedSku
this.skuTree.forEach(function (item) {
_this3.selectedSku[item.k_s] = UNSELECTED_SKU_VALUE_ID;
});
this.skuTree.forEach(function (item) {
var key = item.k_s; // 规格值只有1个时优先判断
var valueId = item.v.length === 1 ? item.v[0].id : _this3.initialSku[key];
if (valueId && isSkuChoosable(_this3.skuList, _this3.selectedSku, {
key: key,
valueId: valueId
})) {
_this3.selectedSku[key] = valueId;
}
});
var skuValues = this.selectedSkuValues;
if (skuValues.length > 0) {
this.$nextTick(function () {
_this3.$emit('sku-selected', {
skuValue: skuValues[skuValues.length - 1],
selectedSku: _this3.selectedSku,
selectedSkuComb: _this3.selectedSkuComb
});
});
} // 重置商品属性
this.selectedProp = {};
var _this$initialSku$sele = this.initialSku.selectedProp,
selectedProp = _this$initialSku$sele === void 0 ? {} : _this$initialSku$sele; // 只有一个属性值时,默认选中,且选中外部传入信息
this.propList.forEach(function (item) {
if (item.v && item.v.length === 1) {
_this3.selectedProp[item.k_id] = [item.v[0].id];
} else if (selectedProp[item.k_id]) {
_this3.selectedProp[item.k_id] = selectedProp[item.k_id];
}
});
var propValues = this.selectedPropValues;
if (propValues.length > 0) {
this.$emit('sku-prop-selected', {
propValue: propValues[propValues.length - 1],
selectedProp: this.selectedProp,
selectedSkuComb: this.selectedSkuComb
});
} // 抛出重置事件
this.$emit('sku-reset', {
selectedSku: this.selectedSku,
selectedProp: this.selectedProp,
selectedSkuComb: this.selectedSkuComb
});
this.centerInitialSku();
},
getSkuMessages: function getSkuMessages() {
return this.$refs.skuMessages ? this.$refs.skuMessages.getMessages() : {};
},
getSkuCartMessages: function getSkuCartMessages() {
return this.$refs.skuMessages ? this.$refs.skuMessages.getCartMessages() : {};
},
validateSkuMessages: function validateSkuMessages() {
return this.$refs.skuMessages ? this.$refs.skuMessages.validateMessages() : '';
},
validateSku: function validateSku() {
if (this.selectedNum === 0) {
return t('unavailable');
}
if (this.isSkuCombSelected) {
return this.validateSkuMessages();
} // 自定义sku校验
if (this.customSkuValidator) {
var err = this.customSkuValidator(this);
if (err) return err;
}
return t('selectSku');
},
onSelect: function onSelect(skuValue) {
var _extends2, _extends3;
// 点击已选中的sku时则取消选中
this.selectedSku = this.selectedSku[skuValue.skuKeyStr] === skuValue.id ? _extends({}, this.selectedSku, (_extends2 = {}, _extends2[skuValue.skuKeyStr] = UNSELECTED_SKU_VALUE_ID, _extends2)) : _extends({}, this.selectedSku, (_extends3 = {}, _extends3[skuValue.skuKeyStr] = skuValue.id, _extends3));
this.$emit('sku-selected', {
skuValue: skuValue,
selectedSku: this.selectedSku,
selectedSkuComb: this.selectedSkuComb
});
},
onPropSelect: function onPropSelect(propValue) {
var _extends4;
var arr = this.selectedProp[propValue.skuKeyStr] || [];
var pos = arr.indexOf(propValue.id);
if (pos > -1) {
arr.splice(pos, 1);
} else if (propValue.multiple) {
arr.push(propValue.id);
} else {
arr.splice(0, 1, propValue.id);
}
this.selectedProp = _extends({}, this.selectedProp, (_extends4 = {}, _extends4[propValue.skuKeyStr] = arr, _extends4));
this.$emit('sku-prop-selected', {
propValue: propValue,
selectedProp: this.selectedProp,
selectedSkuComb: this.selectedSkuComb
});
},
onNumChange: function onNumChange(num) {
this.selectedNum = num;
},
onPreviewImage: function onPreviewImage(selectedValue) {
var _this4 = this;
var imageList = this.imageList;
var index = 0;
var indexImage = imageList[0];
if (selectedValue && selectedValue.imgUrl) {
this.imageList.some(function (image, pos) {
if (image === selectedValue.imgUrl) {
index = pos;
return true;
}
return false;
});
indexImage = selectedValue.imgUrl;
}
var params = _extends({}, selectedValue, {
index: index,
imageList: this.imageList,
indexImage: indexImage
});
this.$emit('open-preview', params);
if (!this.previewOnClickImage) {
return;
}
ImagePreview({
images: this.imageList,
startPosition: index,
onClose: function onClose() {
_this4.$emit('close-preview', params);
}
});
},
onOverLimit: function onOverLimit(data) {
var action = data.action,
limitType = data.limitType,
quota = data.quota,
quotaUsed = data.quotaUsed;
var handleOverLimit = this.customStepperConfig.handleOverLimit;
if (handleOverLimit) {
handleOverLimit(data);
return;
}
if (action === 'minus') {
if (this.startSaleNum > 1) {
Toast(t('minusStartTip', this.startSaleNum));
} else {
Toast(t('minusTip'));
}
} else if (action === 'plus') {
if (limitType === QUOTA_LIMIT) {
if (quotaUsed > 0) {
Toast(t('quotaUsedTip', quota, quotaUsed));
} else {
Toast(t('quotaTip', quota));
}
} else {
Toast(t('soldout'));
}
}
},
onStepperState: function onStepperState(data) {
this.stepperError = data.valid ? null : _extends({}, data, {
action: 'plus'
});
},
onAddCart: function onAddCart() {
this.onBuyOrAddCart('add-cart');
},
onBuy: function onBuy() {
this.onBuyOrAddCart('buy-clicked');
},
onBuyOrAddCart: function onBuyOrAddCart(type) {
// sku 不符合购买条件
if (this.stepperError) {
return this.onOverLimit(this.stepperError);
}
var error = this.validateSku();
if (error) {
Toast(error);
} else {
this.$emit(type, this.getSkuData());
}
},
// @exposed-api
getSkuData: function getSkuData() {
return {
goodsId: this.goodsId,
messages: this.getSkuMessages(),
selectedNum: this.selectedNum,
cartMessages: this.getSkuCartMessages(),
selectedSkuComb: this.selectedSkuComb
};
},
// 当 popup 完全打开后执行
onOpened: function onOpened() {
this.centerInitialSku();
},
centerInitialSku: function centerInitialSku() {
var _this5 = this;
(this.$refs.skuRows || []).forEach(function (it) {
var _ref = it.skuRow || {},
k_s = _ref.k_s;
it.centerItem(_this5.initialSku[k_s]);
});
}
},
render: function render() {
var _this6 = this;
var h = arguments[0];
if (this.isSkuEmpty) {
return;
}
var sku = this.sku,
skuList = this.skuList,
goods = this.goods,
price = this.price,
lazyLoad = this.lazyLoad,
originPrice = this.originPrice,
skuEventBus = this.skuEventBus,
selectedSku = this.selectedSku,
selectedProp = this.selectedProp,
selectedNum = this.selectedNum,
stepperTitle = this.stepperTitle,
selectedSkuComb = this.selectedSkuComb,
showHeaderImage = this.showHeaderImage,
disableSoldoutSku = this.disableSoldoutSku;
var slotsProps = {
price: price,
originPrice: originPrice,
selectedNum: selectedNum,
skuEventBus: skuEventBus,
selectedSku: selectedSku,
selectedSkuComb: selectedSkuComb
};
var slots = function slots(name) {
return _this6.slots(name, slotsProps);
};
var Header = slots('sku-header') || h(SkuHeader, {
"attrs": {
"sku": sku,
"goods": goods,
"skuEventBus": skuEventBus,
"selectedSku": selectedSku,
"showHeaderImage": showHeaderImage
}
}, [h("template", {
"slot": "sku-header-image-extra"
}, [slots('sku-header-image-extra')]), slots('sku-header-price') || h("div", {
"class": "van-sku__goods-price"
}, [h("span", {
"class": "van-sku__price-symbol"
}, ["\uFFE5"]), h("span", {
"class": "van-sku__price-num"
}, [price]), this.priceTag && h("span", {
"class": "van-sku__price-tag"
}, [this.priceTag])]), slots('sku-header-origin-price') || originPrice && h(SkuHeaderItem, [t('originPrice'), " \uFFE5", originPrice]), !this.hideStock && h(SkuHeaderItem, [h("span", {
"class": "van-sku__stock"
}, [this.stockText])]), this.hasSkuOrAttr && !this.hideSelectedText && h(SkuHeaderItem, [this.selectedText]), slots('sku-header-extra')]);
var Group = slots('sku-group') || this.hasSkuOrAttr && h("div", {
"class": this.skuGroupClass
}, [this.skuTree.map(function (skuTreeItem) {
return h(SkuRow, {
"attrs": {
"skuRow": skuTreeItem
},
"ref": "skuRows",
"refInFor": true
}, [skuTreeItem.v.map(function (skuValue) {
return h(SkuRowItem, {
"attrs": {
"skuList": skuList,
"lazyLoad": lazyLoad,
"skuValue": skuValue,
"skuKeyStr": skuTreeItem.k_s,
"selectedSku": selectedSku,
"skuEventBus": skuEventBus,
"disableSoldoutSku": disableSoldoutSku,
"largeImageMode": skuTreeItem.largeImageMode
}
});
})]);
}), this.propList.map(function (skuTreeItem) {
return h(SkuRow, {
"attrs": {
"skuRow": skuTreeItem
}
}, [skuTreeItem.v.map(function (skuValue) {
return h(SkuRowPropItem, {
"attrs": {
"skuValue": skuValue,
"skuKeyStr": skuTreeItem.k_id + '',
"selectedProp": selectedProp,
"skuEventBus": skuEventBus,
"multiple": skuTreeItem.is_multiple
}
});
})]);
})]);
var Stepper = slots('sku-stepper') || h(SkuStepper, {
"ref": "skuStepper",
"attrs": {
"stock": this.stock,
"quota": this.quota,
"quotaUsed": this.quotaUsed,
"startSaleNum": this.startSaleNum,
"skuEventBus": skuEventBus,
"selectedNum": selectedNum,
"stepperTitle": stepperTitle,
"skuStockNum": sku.stock_num,
"disableStepperInput": this.disableStepperInput,
"customStepperConfig": this.customStepperConfig,
"hideQuotaText": this.hideQuotaText
},
"on": {
"change": function change(event) {
_this6.$emit('stepper-change', event);
}
}
});
var Messages = slots('sku-messages') || h(SkuMessages, {
"ref": "skuMessages",
"attrs": {
"goodsId": this.goodsId,
"messageConfig": this.messageConfig,
"messages": sku.messages
}
});
var Actions = slots('sku-actions') || h(SkuActions, {
"attrs": {
"buyText": this.buyText,
"skuEventBus": skuEventBus,
"addCartText": this.addCartText,
"showAddCartBtn": this.showAddCartBtn
}
});
return h(Popup, {
"attrs": {
"round": true,
"closeable": true,
"position": "bottom",
"getContainer": this.getContainer,
"closeOnClickOverlay": this.closeOnClickOverlay,
"safeAreaInsetBottom": this.safeAreaInsetBottom
},
"class": "van-sku-container",
"on": {
"opened": this.onOpened
},
"model": {
value: _this6.show,
callback: function callback($$v) {
_this6.show = $$v;
}
}
}, [Header, h("div", {
"class": "van-sku-body",
"style": this.bodyStyle
}, [slots('sku-body-top'), Group, slots('extra-sku-group'), Stepper, Messages]), slots('sku-actions-top'), Actions]);
}
});