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

4 years ago
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]);
}
});