2025-06-26 14:55:08 +08:00

802 lines
24 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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, isEmpty } 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,
skuProperties: 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,
currentSkuProperties: []
};
},
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: {
isSkuProperties: function isSkuProperties() {
return this.skuProperties && this.skuProperties.length;
},
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.filter(function (i) {
return i.is_necessary !== false;
}).some(function (i) {
return (_this.selectedProp[i.k_id] || []).length === 0;
});
},
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 || this.isSkuProperties) {
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
};
} // 更新当前规格属性数据
this.setCurrentSkuProperties(skuComb ? skuComb.id : null);
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.isSkuProperties ? this.currentSkuProperties : 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: {
setCurrentSkuProperties: function setCurrentSkuProperties(id) {
var _this$skuProperties;
var target = ((_this$skuProperties = this.skuProperties) == null ? void 0 : _this$skuProperties.find(function (item) {
return item.sku_id === id;
})) || {};
this.currentSkuProperties = target.properties || [];
},
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 (selectedProp[item.k_id]) {
_this3.selectedProp[item.k_id] = selectedProp[item.k_id];
}
});
if (isEmpty(this.selectedProp)) {
this.propList.forEach(function (item) {
var _item$v;
// 没有加价的属性,默认选中第一个
if ((item == null ? void 0 : (_item$v = item.v) == null ? void 0 : _item$v.length) > 0) {
var v = item.v,
k_id = item.k_id;
var isHasConfigPrice = v.some(function (i) {
return +i.price !== 0;
}); // 没有加价属性
if (!isHasConfigPrice) {
// 找到第一个不被禁用的属性
// 历史如果没有 text_status 字段的,就相当于沿用直接原来的逻辑取第一个属性
var firstEnableProp = v.find(function (prop) {
return prop.text_status !== 0;
});
if (firstEnableProp) {
_this3.selectedProp[k_id] = [firstEnableProp.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)); // 切换sku清空当前选择属性数据触发prop-clear
if (this.isSkuProperties) {
this.selectedProp = {};
this.onPropClear();
}
this.$emit('sku-selected', {
skuValue: skuValue,
selectedSku: this.selectedSku,
selectedSkuComb: this.selectedSkuComb
});
},
onPropClear: function onPropClear() {
this.$emit('sku-prop-clear');
},
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,
"disabled": skuValue.text_status === 0
}
});
})]);
})]);
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, slots('before-sku-messages'), Messages, slots('after-sku-messages')]), slots('sku-actions-top'), Actions]);
}
});