mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-05-25 15:59:16 +08:00
[improvement] Sku: jsx (#2666)
This commit is contained in:
parent
7ee383129c
commit
3958a42484
@ -1,132 +1,3 @@
|
|||||||
<template>
|
|
||||||
<popup
|
|
||||||
v-if="!isSkuEmpty"
|
|
||||||
v-model="show"
|
|
||||||
position="bottom"
|
|
||||||
class="van-sku-container"
|
|
||||||
:close-on-click-overlay="closeOnClickOverlay"
|
|
||||||
:get-container="getContainer"
|
|
||||||
>
|
|
||||||
<!-- sku-header -->
|
|
||||||
<slot
|
|
||||||
name="sku-header"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:selected-sku-comb="selectedSkuComb"
|
|
||||||
>
|
|
||||||
<sku-header
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:goods="goods"
|
|
||||||
:sku="sku"
|
|
||||||
>
|
|
||||||
<slot
|
|
||||||
name="sku-header-price"
|
|
||||||
:price="price"
|
|
||||||
:selected-sku-comb="selectedSkuComb"
|
|
||||||
>
|
|
||||||
<div class="van-sku__goods-price">
|
|
||||||
<span class="van-sku__price-symbol">¥</span>
|
|
||||||
<span
|
|
||||||
v-text="price"
|
|
||||||
class="van-sku__price-num"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</slot>
|
|
||||||
</sku-header>
|
|
||||||
</slot>
|
|
||||||
<div
|
|
||||||
class="van-sku-body"
|
|
||||||
:style="bodyStyle"
|
|
||||||
>
|
|
||||||
<!-- sku-body-top -->
|
|
||||||
<slot
|
|
||||||
name="sku-body-top"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
/>
|
|
||||||
<!-- sku-group -->
|
|
||||||
<slot
|
|
||||||
name="sku-group"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-if="hasSku"
|
|
||||||
:class="skuGroupClass"
|
|
||||||
>
|
|
||||||
<sku-row
|
|
||||||
v-for="(skuTreeItem, index) in skuTree"
|
|
||||||
:key="index"
|
|
||||||
:sku-row="skuTreeItem"
|
|
||||||
>
|
|
||||||
<sku-row-item
|
|
||||||
v-for="(skuValue, valueIndex) in skuTreeItem.v"
|
|
||||||
:key="valueIndex"
|
|
||||||
:sku-key-str="skuTreeItem.k_s"
|
|
||||||
:sku-value="skuValue"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:sku-list="sku.list"
|
|
||||||
/>
|
|
||||||
</sku-row>
|
|
||||||
</div>
|
|
||||||
</slot>
|
|
||||||
<!-- extra-sku-group -->
|
|
||||||
<slot
|
|
||||||
name="extra-sku-group"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
/>
|
|
||||||
<!-- sku-stepper -->
|
|
||||||
<slot
|
|
||||||
name="sku-stepper"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:selected-sku-comb="selectedSkuComb"
|
|
||||||
:selected-num="selectedNum"
|
|
||||||
>
|
|
||||||
<sku-stepper
|
|
||||||
ref="skuStepper"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
:selected-sku="selectedSku"
|
|
||||||
:selected-sku-comb="selectedSkuComb"
|
|
||||||
:selected-num="selectedNum"
|
|
||||||
:stepper-title="stepperTitle"
|
|
||||||
:sku-stock-num="sku.stock_num"
|
|
||||||
:hide-quota-text="hideQuotaText"
|
|
||||||
:quota="quota"
|
|
||||||
:quota-used="quotaUsed"
|
|
||||||
:disable-stepper-input="disableStepperInput"
|
|
||||||
:hide-stock="hideStock"
|
|
||||||
:custom-stepper-config="customStepperConfig"
|
|
||||||
@change="$emit('stepper-change', $event)"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
<!-- sku-messages -->
|
|
||||||
<slot name="sku-messages">
|
|
||||||
<sku-messages
|
|
||||||
ref="skuMessages"
|
|
||||||
:goods-id="goodsId"
|
|
||||||
:message-config="messageConfig"
|
|
||||||
:messages="sku.messages"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</div>
|
|
||||||
<!-- sku-actions -->
|
|
||||||
<slot
|
|
||||||
name="sku-actions"
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
>
|
|
||||||
<sku-actions
|
|
||||||
:sku-event-bus="skuEventBus"
|
|
||||||
:buy-text="buyText"
|
|
||||||
:show-add-cart-btn="showAddCartBtn"
|
|
||||||
/>
|
|
||||||
</slot>
|
|
||||||
</popup>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Popup from '../popup';
|
import Popup from '../popup';
|
||||||
@ -138,30 +9,14 @@ import SkuRowItem from './components/SkuRowItem';
|
|||||||
import SkuStepper from './components/SkuStepper';
|
import SkuStepper from './components/SkuStepper';
|
||||||
import SkuMessages from './components/SkuMessages';
|
import SkuMessages from './components/SkuMessages';
|
||||||
import SkuActions from './components/SkuActions';
|
import SkuActions from './components/SkuActions';
|
||||||
import {
|
import { isAllSelected, isSkuChoosable, getSkuComb, getSelectedSkuValues } from './utils/skuHelper';
|
||||||
isAllSelected,
|
|
||||||
isSkuChoosable,
|
|
||||||
getSkuComb,
|
|
||||||
getSelectedSkuValues
|
|
||||||
} from './utils/skuHelper';
|
|
||||||
import { LIMIT_TYPE, UNSELECTED_SKU_VALUE_ID } from './constants';
|
import { LIMIT_TYPE, UNSELECTED_SKU_VALUE_ID } from './constants';
|
||||||
import create from '../utils/create';
|
import { use, useSlots } from '../utils';
|
||||||
|
|
||||||
|
const [sfc] = use('sku');
|
||||||
const { QUOTA_LIMIT } = LIMIT_TYPE;
|
const { QUOTA_LIMIT } = LIMIT_TYPE;
|
||||||
|
|
||||||
export default create({
|
export default sfc({
|
||||||
name: 'sku',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Popup,
|
|
||||||
SkuHeader,
|
|
||||||
SkuRow,
|
|
||||||
SkuRowItem,
|
|
||||||
SkuStepper,
|
|
||||||
SkuMessages,
|
|
||||||
SkuActions
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
sku: Object,
|
sku: Object,
|
||||||
goods: Object,
|
goods: Object,
|
||||||
@ -221,10 +76,7 @@ export default create({
|
|||||||
show(val) {
|
show(val) {
|
||||||
this.$emit('input', val);
|
this.$emit('input', val);
|
||||||
if (!val) {
|
if (!val) {
|
||||||
const selectedSkuValues = getSelectedSkuValues(
|
const selectedSkuValues = getSelectedSkuValues(this.sku.tree, this.selectedSku);
|
||||||
this.sku.tree,
|
|
||||||
this.selectedSku
|
|
||||||
);
|
|
||||||
|
|
||||||
this.$emit('sku-close', {
|
this.$emit('sku-close', {
|
||||||
selectedSkuValues,
|
selectedSkuValues,
|
||||||
@ -374,15 +226,11 @@ export default create({
|
|||||||
},
|
},
|
||||||
|
|
||||||
getSkuCartMessages() {
|
getSkuCartMessages() {
|
||||||
return this.$refs.skuMessages
|
return this.$refs.skuMessages ? this.$refs.skuMessages.getCartMessages() : {};
|
||||||
? this.$refs.skuMessages.getCartMessages()
|
|
||||||
: {};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
validateSkuMessages() {
|
validateSkuMessages() {
|
||||||
return this.$refs.skuMessages
|
return this.$refs.skuMessages ? this.$refs.skuMessages.validateMessages() : '';
|
||||||
? this.$refs.skuMessages.validateMessages()
|
|
||||||
: '';
|
|
||||||
},
|
},
|
||||||
|
|
||||||
validateSku() {
|
validateSku() {
|
||||||
@ -493,6 +341,121 @@ export default create({
|
|||||||
cartMessages: this.getSkuCartMessages()
|
cartMessages: this.getSkuCartMessages()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
if (this.isSkuEmpty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
sku,
|
||||||
|
goods,
|
||||||
|
price,
|
||||||
|
skuEventBus,
|
||||||
|
selectedSku,
|
||||||
|
selectedNum,
|
||||||
|
stepperTitle,
|
||||||
|
hideQuotaText,
|
||||||
|
selectedSkuComb
|
||||||
|
} = this;
|
||||||
|
|
||||||
|
const slotsProps = {
|
||||||
|
price,
|
||||||
|
selectedNum,
|
||||||
|
skuEventBus,
|
||||||
|
selectedSku,
|
||||||
|
selectedSkuComb
|
||||||
|
};
|
||||||
|
const slots = name => useSlots(this)(name, slotsProps);
|
||||||
|
|
||||||
|
const Header = slots('sku-header') || (
|
||||||
|
<SkuHeader sku={sku} goods={goods} skuEventBus={skuEventBus} selectedSku={selectedSku}>
|
||||||
|
{slots('sku-header-price') || (
|
||||||
|
<div class="van-sku__goods-price">
|
||||||
|
<span class="van-sku__price-symbol">¥</span>
|
||||||
|
<span class="van-sku__price-num">{price}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</SkuHeader>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Group =
|
||||||
|
slots('sku-group') ||
|
||||||
|
(this.hasSku && (
|
||||||
|
<div class={this.skuGroupClass}>
|
||||||
|
{this.skuTree.map(skuTreeItem => (
|
||||||
|
<SkuRow skuRow={skuTreeItem}>
|
||||||
|
{skuTreeItem.v.map(skuValue => (
|
||||||
|
<SkuRowItem
|
||||||
|
skuList={sku.list}
|
||||||
|
skuValue={skuValue}
|
||||||
|
selectedSku={selectedSku}
|
||||||
|
skuEventBus={skuEventBus}
|
||||||
|
skuKeyStr={skuTreeItem.k_s}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</SkuRow>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
));
|
||||||
|
|
||||||
|
const Stepper = slots('sku-stepper') || (
|
||||||
|
<SkuStepper
|
||||||
|
ref="skuStepper"
|
||||||
|
quota={this.quota}
|
||||||
|
hideStock={this.hideStock}
|
||||||
|
quotaUsed={this.quotaUsed}
|
||||||
|
skuEventBus={skuEventBus}
|
||||||
|
selectedNum={selectedNum}
|
||||||
|
selectedSku={selectedSku}
|
||||||
|
stepperTitle={stepperTitle}
|
||||||
|
skuStockNum={sku.stock_num}
|
||||||
|
hideQuotaText={hideQuotaText}
|
||||||
|
selectedSkuComb={selectedSkuComb}
|
||||||
|
disableStepperInput={this.disableStepperInput}
|
||||||
|
customStepperConfig={this.customStepperConfig}
|
||||||
|
onChange={event => {
|
||||||
|
this.$emit('stepper-change', event);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Messages = slots('sku-messages') || (
|
||||||
|
<SkuMessages
|
||||||
|
ref="skuMessages"
|
||||||
|
goodsId={this.goodsId}
|
||||||
|
messageConfig={this.messageConfig}
|
||||||
|
messages={sku.messages}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Actions = slots('sku-actions') || (
|
||||||
|
<SkuActions
|
||||||
|
buyText={this.buyText}
|
||||||
|
skuEventBus={skuEventBus}
|
||||||
|
showAddCartBtn={this.showAddCartBtn}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Popup
|
||||||
|
vModel={this.show}
|
||||||
|
position="bottom"
|
||||||
|
class="van-sku-container"
|
||||||
|
getContainer={this.getContainer}
|
||||||
|
closeOnClickOverlay={this.closeOnClickOverlay}
|
||||||
|
>
|
||||||
|
{Header}
|
||||||
|
<div class="van-sku-body" style={this.bodyStyle}>
|
||||||
|
{slots('sku-body-top')}
|
||||||
|
{Group}
|
||||||
|
{slots('extra-sku-group')}
|
||||||
|
{Stepper}
|
||||||
|
{Messages}
|
||||||
|
</div>
|
||||||
|
{Actions}
|
||||||
|
</Popup>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
|
32
packages/sku/components/SkuActions.js
Normal file
32
packages/sku/components/SkuActions.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { use } from '../../utils';
|
||||||
|
import Button from '../../button';
|
||||||
|
|
||||||
|
const [sfc, bem] = use('sku-actions');
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
props: {
|
||||||
|
buyText: String,
|
||||||
|
skuEventBus: Object,
|
||||||
|
showAddCartBtn: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
const emit = name => () => {
|
||||||
|
this.skuEventBus.$emit('sku:addCart');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={bem()}>
|
||||||
|
{this.showAddCartBtn && (
|
||||||
|
<Button bottomAction text="加入购物车" onClick={emit('sku:addCart')} />
|
||||||
|
)}
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
bottomAction
|
||||||
|
text={this.buyText || '立即购买'}
|
||||||
|
onClick={emit('sku:buy')}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,35 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="b()">
|
|
||||||
<van-button
|
|
||||||
v-if="showAddCartBtn"
|
|
||||||
bottom-action
|
|
||||||
text="加入购物车"
|
|
||||||
@click="skuEventBus.$emit('sku:addCart')"
|
|
||||||
/>
|
|
||||||
<van-button
|
|
||||||
type="primary"
|
|
||||||
bottom-action
|
|
||||||
:text="buyText || '立即购买'"
|
|
||||||
@click="skuEventBus.$emit('sku:buy')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import VanButton from '../../button';
|
|
||||||
import create from '../../utils/create';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'sku-actions',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
VanButton
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
buyText: String,
|
|
||||||
skuEventBus: Object,
|
|
||||||
showAddCartBtn: Boolean
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
61
packages/sku/components/SkuHeader.js
Normal file
61
packages/sku/components/SkuHeader.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
import { use } from '../../utils';
|
||||||
|
import Icon from '../../icon';
|
||||||
|
|
||||||
|
const [sfc, bem] = use('sku-header');
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
props: {
|
||||||
|
sku: Object,
|
||||||
|
goods: Object,
|
||||||
|
skuEventBus: Object,
|
||||||
|
selectedSku: Object
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
goodsImg() {
|
||||||
|
const s1Id = this.selectedSku.s1;
|
||||||
|
const skuImg = this.getSkuImg(s1Id);
|
||||||
|
// 优先使用选中 sku 的图片
|
||||||
|
return skuImg || this.goods.picture;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
getSkuImg(id) {
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
// skuImg 挂载在 skuTree 中 s1 上
|
||||||
|
const treeItem = this.sku.tree.filter(item => item.k_s === 's1')[0] || {};
|
||||||
|
|
||||||
|
if (!treeItem.v) return;
|
||||||
|
|
||||||
|
const matchedSku = treeItem.v.filter(skuValue => skuValue.id === id)[0];
|
||||||
|
if (matchedSku) return matchedSku.imgUrl || matchedSku.img_url;
|
||||||
|
},
|
||||||
|
|
||||||
|
previewImage() {
|
||||||
|
this.skuEventBus.$emit('sku:previewImage', this.goodsImg);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
return (
|
||||||
|
<div class={[bem(), 'van-hairline--bottom']}>
|
||||||
|
<div class={bem('img-wrap')} onClick={this.previewImage}>
|
||||||
|
<img src={this.goodsImg} />
|
||||||
|
</div>
|
||||||
|
<div class={bem('goods-info')}>
|
||||||
|
<div class="van-sku__goods-name van-ellipsis">{this.goods.title}</div>
|
||||||
|
{this.$slots.default}
|
||||||
|
<Icon
|
||||||
|
name="close"
|
||||||
|
class="van-sku__close-icon"
|
||||||
|
onClick={() => {
|
||||||
|
this.skuEventBus.$emit('sku:close');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,68 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div
|
|
||||||
:class="b()"
|
|
||||||
class="van-hairline--bottom"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
:class="b('img-wrap')"
|
|
||||||
@click="previewImage"
|
|
||||||
>
|
|
||||||
<img :src="goodsImg">
|
|
||||||
</div>
|
|
||||||
<div :class="b('goods-info')">
|
|
||||||
<div
|
|
||||||
v-text="goods.title"
|
|
||||||
class="van-sku__goods-name van-ellipsis"
|
|
||||||
/>
|
|
||||||
<!-- price display area -->
|
|
||||||
<slot />
|
|
||||||
<icon
|
|
||||||
name="close"
|
|
||||||
class="van-sku__close-icon"
|
|
||||||
@click="skuEventBus.$emit('sku:close')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../../utils/create';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'sku-header',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
sku: Object,
|
|
||||||
goods: Object,
|
|
||||||
skuEventBus: Object,
|
|
||||||
selectedSku: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
goodsImg() {
|
|
||||||
const s1Id = this.selectedSku.s1;
|
|
||||||
const skuImg = this.getSkuImg(s1Id);
|
|
||||||
// 优先使用选中sku的图片
|
|
||||||
return skuImg || this.goods.picture;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
getSkuImg(id) {
|
|
||||||
if (!id) return;
|
|
||||||
|
|
||||||
// 目前skuImg都挂载在skuTree中s1那类sku上
|
|
||||||
const treeItem = this.sku.tree.filter(item => item.k_s === 's1')[0] || {};
|
|
||||||
|
|
||||||
if (!treeItem.v) return;
|
|
||||||
|
|
||||||
const matchedSku = treeItem.v.filter(skuValue => skuValue.id === id)[0];
|
|
||||||
if (matchedSku) return (matchedSku.imgUrl || matchedSku.img_url);
|
|
||||||
},
|
|
||||||
|
|
||||||
previewImage() {
|
|
||||||
this.skuEventBus.$emit('sku:previewImage', this.goodsImg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
103
packages/sku/components/SkuImgUploader.js
Normal file
103
packages/sku/components/SkuImgUploader.js
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
import { use } from '../../utils';
|
||||||
|
import Icon from '../../icon';
|
||||||
|
import Loading from '../../loading';
|
||||||
|
import Uploader from '../../uploader';
|
||||||
|
|
||||||
|
const [sfc, bem] = use('sku-img-uploader');
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
props: {
|
||||||
|
value: String,
|
||||||
|
uploadImg: Function,
|
||||||
|
maxSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 6
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 正在上传的图片 base64
|
||||||
|
paddingImg: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
imgList() {
|
||||||
|
return this.value && !this.paddingImg ? [this.value] : [];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
afterReadFile(file) {
|
||||||
|
// 上传文件
|
||||||
|
this.paddingImg = file.content;
|
||||||
|
this.uploadImg(file.file, file.content)
|
||||||
|
.then(img => {
|
||||||
|
this.$emit('input', img);
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.paddingImg = '';
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
this.paddingImg = '';
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onOversize() {
|
||||||
|
this.$toast(`最大可上传图片为${this.maxSize}MB,请尝试压缩图片尺寸`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
const { imgList, paddingImg } = this;
|
||||||
|
|
||||||
|
const ImageList = (paddingImg || imgList.length > 0) && (
|
||||||
|
<div class="van-clearfix">
|
||||||
|
{imgList.map(img => (
|
||||||
|
<div class={bem('img')}>
|
||||||
|
<img src={img} />
|
||||||
|
<Icon
|
||||||
|
name="clear"
|
||||||
|
class={bem('delete')}
|
||||||
|
onClick={() => {
|
||||||
|
this.$emit('input', '');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
{paddingImg && (
|
||||||
|
<div class={bem('img')}>
|
||||||
|
<img src={paddingImg} />
|
||||||
|
<Loading type="spinner" class={bem('uploading')} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={bem()}>
|
||||||
|
<Uploader
|
||||||
|
disabled={!!paddingImg}
|
||||||
|
afterRead={this.afterReadFile}
|
||||||
|
maxSize={this.maxSize * 1024 * 1024}
|
||||||
|
onOversize={this.onOversize}
|
||||||
|
>
|
||||||
|
<div class={bem('header')}>
|
||||||
|
{paddingImg ? (
|
||||||
|
<div>正在上传...</div>
|
||||||
|
) : (
|
||||||
|
[
|
||||||
|
<Icon name="photograph" />,
|
||||||
|
<span class="label">{this.value ? '重拍' : '拍照'} 或 </span>,
|
||||||
|
<Icon name="photo" />,
|
||||||
|
<span class="label">{this.value ? '重新选择照片' : '选择照片'}</span>
|
||||||
|
]
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</Uploader>
|
||||||
|
{ImageList}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,104 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="b()">
|
|
||||||
<!-- 头部 -->
|
|
||||||
<van-uploader
|
|
||||||
:disabled="!!paddingImg"
|
|
||||||
:after-read="afterReadFile"
|
|
||||||
:max-size="maxSize * 1024 * 1024"
|
|
||||||
@oversize="onOversize"
|
|
||||||
>
|
|
||||||
<div :class="b('header')">
|
|
||||||
<div v-if="paddingImg">正在上传...</div>
|
|
||||||
<template v-else>
|
|
||||||
<icon name="photograph" />
|
|
||||||
<span class="label">{{ value ? '重拍' : '拍照' }}</span> 或
|
|
||||||
<icon name="photo" />
|
|
||||||
<span class="label">{{ value ? '重新选择照片' : '选择照片' }}</span>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</van-uploader>
|
|
||||||
<!-- 图片列表区域 -->
|
|
||||||
<div
|
|
||||||
v-if="paddingImg || imgList.length > 0"
|
|
||||||
class="van-clearfix"
|
|
||||||
>
|
|
||||||
<!-- 已有的图片,图片右上角显示删除按钮 -->
|
|
||||||
<div
|
|
||||||
v-for="img in imgList"
|
|
||||||
:class="b('img')"
|
|
||||||
>
|
|
||||||
<img :src="img">
|
|
||||||
<icon
|
|
||||||
name="clear"
|
|
||||||
:class="b('delete')"
|
|
||||||
@click="$emit('input', '')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<!-- 正在上传的图片,有上传等待提示 -->
|
|
||||||
<div
|
|
||||||
v-if="paddingImg"
|
|
||||||
:class="b('img')"
|
|
||||||
>
|
|
||||||
<img :src="paddingImg">
|
|
||||||
<loading
|
|
||||||
type="spinner"
|
|
||||||
:class="b('uploading')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import VanUploader from '../../uploader';
|
|
||||||
import create from '../../utils/create';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'sku-img-uploader',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
VanUploader
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
value: String,
|
|
||||||
uploadImg: Function,
|
|
||||||
maxSize: {
|
|
||||||
type: Number,
|
|
||||||
default: 6
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
// 正在上传的图片base 64
|
|
||||||
paddingImg: ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
imgList() {
|
|
||||||
return this.value && !this.paddingImg ? [this.value] : [];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
afterReadFile(file) {
|
|
||||||
// 上传文件
|
|
||||||
this.paddingImg = file.content;
|
|
||||||
this.uploadImg(file.file, file.content).then(img => {
|
|
||||||
this.$emit('input', img);
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.paddingImg = '';
|
|
||||||
});
|
|
||||||
}).catch(() => {
|
|
||||||
this.paddingImg = '';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onOversize() {
|
|
||||||
this.$toast(`最大可上传图片为${this.maxSize}MB,请尝试压缩图片尺寸`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,41 +1,13 @@
|
|||||||
<template>
|
import { use } from '../../utils';
|
||||||
<cell-group :class="b()">
|
import Cell from '../../cell';
|
||||||
<template v-for="(message, index) in messages">
|
import CellGroup from '../../cell-group';
|
||||||
<cell
|
|
||||||
v-if="message.type === 'image'"
|
|
||||||
:class="b('image-cell')"
|
|
||||||
label="仅限一张"
|
|
||||||
:key="`${goodsId}-${index}`"
|
|
||||||
:required="message.required == '1'"
|
|
||||||
:title="message.name"
|
|
||||||
>
|
|
||||||
<sku-img-uploader
|
|
||||||
v-model="messageValues[index].value"
|
|
||||||
:upload-img="messageConfig.uploadImg"
|
|
||||||
:max-size="messageConfig.uploadMaxSize"
|
|
||||||
/>
|
|
||||||
</cell>
|
|
||||||
<field
|
|
||||||
v-else
|
|
||||||
v-model="messageValues[index].value"
|
|
||||||
maxlength="200"
|
|
||||||
:key="`${goodsId}-${index}`"
|
|
||||||
:required="message.required == '1'"
|
|
||||||
:label="message.name"
|
|
||||||
:placeholder="getPlaceholder(message)"
|
|
||||||
:type="getType(message)"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</cell-group>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../../utils/create';
|
|
||||||
import Field from '../../field';
|
import Field from '../../field';
|
||||||
import validateEmail from '../../utils/validate/email';
|
import validateEmail from '../../utils/validate/email';
|
||||||
import validateNumber from '../../utils/validate/number';
|
import validateNumber from '../../utils/validate/number';
|
||||||
import SkuImgUploader from './SkuImgUploader';
|
import SkuImgUploader from './SkuImgUploader';
|
||||||
|
|
||||||
|
const [sfc, bem] = use('sku-messages');
|
||||||
|
|
||||||
const PLACEHOLDER = {
|
const PLACEHOLDER = {
|
||||||
id_no: '输入身份证号码',
|
id_no: '输入身份证号码',
|
||||||
text: '输入文本',
|
text: '输入文本',
|
||||||
@ -47,14 +19,7 @@ const PLACEHOLDER = {
|
|||||||
mobile: '输入手机号码'
|
mobile: '输入手机号码'
|
||||||
};
|
};
|
||||||
|
|
||||||
export default create({
|
export default sfc({
|
||||||
name: 'sku-messages',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
SkuImgUploader,
|
|
||||||
Field
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
messages: Array,
|
messages: Array,
|
||||||
messageConfig: Object,
|
messageConfig: Object,
|
||||||
@ -73,12 +38,6 @@ export default create({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
|
||||||
messagePlaceholderMap() {
|
|
||||||
return this.messageConfig.placeholderMap || {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
resetMessageValues(messages) {
|
resetMessageValues(messages) {
|
||||||
return (messages || []).map(() => ({ value: '' }));
|
return (messages || []).map(() => ({ value: '' }));
|
||||||
@ -125,7 +84,8 @@ export default create({
|
|||||||
|
|
||||||
getPlaceholder(message) {
|
getPlaceholder(message) {
|
||||||
const type = +message.multiple === 1 ? 'textarea' : message.type;
|
const type = +message.multiple === 1 ? 'textarea' : message.type;
|
||||||
return this.messagePlaceholderMap[type] || PLACEHOLDER[type];
|
const map = this.messageConfig.placeholderMap || {};
|
||||||
|
return map[type] || PLACEHOLDER[type];
|
||||||
},
|
},
|
||||||
|
|
||||||
validateMessages() {
|
validateMessages() {
|
||||||
@ -137,7 +97,7 @@ export default create({
|
|||||||
|
|
||||||
if (value === '') {
|
if (value === '') {
|
||||||
// 必填字段的校验
|
// 必填字段的校验
|
||||||
if (message.required == '1') { // eslint-disable-line
|
if (String(message.required) === '1') { // eslint-disable-line
|
||||||
const textType = message.type === 'image'
|
const textType = message.type === 'image'
|
||||||
? '请上传'
|
? '请上传'
|
||||||
: '请填写';
|
: '请填写';
|
||||||
@ -159,6 +119,37 @@ export default create({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
return (
|
||||||
|
<CellGroup class={bem()}>
|
||||||
|
{this.messages.map((message, index) => (message.type === 'image' ? (
|
||||||
|
<Cell
|
||||||
|
class={bem('image-cell')}
|
||||||
|
label="仅限一张"
|
||||||
|
title={message.name}
|
||||||
|
key={`${this.goodsId}-${index}`}
|
||||||
|
required={String(message.required) === '1'}
|
||||||
|
>
|
||||||
|
<SkuImgUploader
|
||||||
|
vModel={this.messageValues[index].value}
|
||||||
|
uploadImg={this.messageConfig.uploadImg}
|
||||||
|
maxSize={this.messageConfig.uploadMaxSize}
|
||||||
|
/>
|
||||||
|
</Cell>
|
||||||
|
) : (
|
||||||
|
<Field
|
||||||
|
vModel={this.messageValues[index].value}
|
||||||
|
maxlength="200"
|
||||||
|
label={message.name}
|
||||||
|
key={`${this.goodsId}-${index}`}
|
||||||
|
required={String(message.required) === '1'}
|
||||||
|
placeholder={this.getPlaceholder(message)}
|
||||||
|
type={this.getType(message)}
|
||||||
|
/>
|
||||||
|
)))}
|
||||||
|
</CellGroup>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
|
18
packages/sku/components/SkuRow.js
Normal file
18
packages/sku/components/SkuRow.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { use } from '../../utils';
|
||||||
|
|
||||||
|
const [sfc, bem] = use('sku-row');
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
props: {
|
||||||
|
skuRow: Object
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
return (
|
||||||
|
<div class={bem()}>
|
||||||
|
<div class={bem('title')}>{this.skuRow.k}:</div>
|
||||||
|
{this.$slots.default}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,18 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="b()">
|
|
||||||
<div :class="b('title')">{{ skuRow.k }}:</div>
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../../utils/create';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'sku-row',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
skuRow: Object
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
53
packages/sku/components/SkuRowItem.js
Normal file
53
packages/sku/components/SkuRowItem.js
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { use } from '../../utils';
|
||||||
|
import { isSkuChoosable } from '../utils/skuHelper';
|
||||||
|
|
||||||
|
const [sfc] = use('sku-row-item');
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
props: {
|
||||||
|
skuList: Array,
|
||||||
|
skuValue: Object,
|
||||||
|
skuKeyStr: String,
|
||||||
|
skuEventBus: Object,
|
||||||
|
selectedSku: Object
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
choosable() {
|
||||||
|
return isSkuChoosable(this.skuList, this.selectedSku, {
|
||||||
|
key: this.skuKeyStr,
|
||||||
|
valueId: this.skuValue.id
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onSelect() {
|
||||||
|
if (this.choosable) {
|
||||||
|
this.skuEventBus.$emit('sku:select', {
|
||||||
|
...this.skuValue,
|
||||||
|
skuKeyStr: this.skuKeyStr
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
const choosed = this.skuValue.id === this.selectedSku[this.skuKeyStr];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
class={[
|
||||||
|
'van-sku-row__item',
|
||||||
|
{
|
||||||
|
'van-sku-row__item--active': choosed,
|
||||||
|
'van-sku-row__item--disabled': !this.choosable
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
onClick={this.onSelect}
|
||||||
|
>
|
||||||
|
{this.skuValue.name}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,52 +0,0 @@
|
|||||||
<template>
|
|
||||||
<span
|
|
||||||
v-text="skuValue.name"
|
|
||||||
class="van-sku-row__item"
|
|
||||||
:class="{
|
|
||||||
'van-sku-row__item--active': isChoosed,
|
|
||||||
'van-sku-row__item--disabled': !isChoosable
|
|
||||||
}"
|
|
||||||
@click="onSelect"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../../utils/create';
|
|
||||||
import { isSkuChoosable } from '../utils/skuHelper';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'sku-row-item',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
skuEventBus: Object,
|
|
||||||
skuValue: Object,
|
|
||||||
skuList: Array,
|
|
||||||
selectedSku: Object,
|
|
||||||
skuKeyStr: String
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
isChoosed() {
|
|
||||||
return this.skuValue.id === this.selectedSku[this.skuKeyStr];
|
|
||||||
},
|
|
||||||
|
|
||||||
isChoosable() {
|
|
||||||
return isSkuChoosable(this.skuList, this.selectedSku, {
|
|
||||||
key: this.skuKeyStr,
|
|
||||||
valueId: this.skuValue.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onSelect() {
|
|
||||||
if (this.isChoosable) {
|
|
||||||
this.skuEventBus.$emit('sku:select', {
|
|
||||||
...this.skuValue,
|
|
||||||
skuKeyStr: this.skuKeyStr
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,55 +1,22 @@
|
|||||||
<template>
|
import { use } from '../../utils';
|
||||||
<div class="van-sku-stepper-stock">
|
|
||||||
<div class="van-sku-stepper-container">
|
|
||||||
<div class="van-sku__stepper-title">{{ stepperTitle || '购买数量' }}:</div>
|
|
||||||
<stepper
|
|
||||||
class="van-sku__stepper"
|
|
||||||
v-model="currentNum"
|
|
||||||
:min="1"
|
|
||||||
:max="stepperLimit"
|
|
||||||
:disable-input="disableStepperInput"
|
|
||||||
@overlimit="onOverLimit"
|
|
||||||
@change="onChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
v-if="!hideStock"
|
|
||||||
v-text="stockText"
|
|
||||||
class="van-sku__stock"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="!hideQuotaText && quotaText"
|
|
||||||
v-text="quotaText"
|
|
||||||
class="van-sku__quota"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../../utils/create';
|
|
||||||
import Stepper from '../../stepper';
|
import Stepper from '../../stepper';
|
||||||
import { LIMIT_TYPE } from '../constants';
|
import { LIMIT_TYPE } from '../constants';
|
||||||
|
|
||||||
|
const [sfc] = use('sku-stepper');
|
||||||
const { QUOTA_LIMIT, STOCK_LIMIT } = LIMIT_TYPE;
|
const { QUOTA_LIMIT, STOCK_LIMIT } = LIMIT_TYPE;
|
||||||
|
|
||||||
export default create({
|
export default sfc({
|
||||||
name: 'sku-stepper',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Stepper
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
quota: Number,
|
quota: Number,
|
||||||
hideQuotaText: Boolean,
|
|
||||||
quotaUsed: Number,
|
quotaUsed: Number,
|
||||||
hideStock: Boolean,
|
hideStock: Boolean,
|
||||||
skuEventBus: Object,
|
skuEventBus: Object,
|
||||||
skuStockNum: Number,
|
skuStockNum: Number,
|
||||||
selectedSku: Object,
|
selectedSku: Object,
|
||||||
selectedSkuComb: Object,
|
|
||||||
selectedNum: Number,
|
selectedNum: Number,
|
||||||
stepperTitle: String,
|
stepperTitle: String,
|
||||||
|
hideQuotaText: Boolean,
|
||||||
|
selectedSkuComb: Object,
|
||||||
disableStepperInput: Boolean,
|
disableStepperInput: Boolean,
|
||||||
customStepperConfig: Object
|
customStepperConfig: Object
|
||||||
},
|
},
|
||||||
@ -142,6 +109,27 @@ export default create({
|
|||||||
handleStepperChange && handleStepperChange(currentValue);
|
handleStepperChange && handleStepperChange(currentValue);
|
||||||
this.$emit('change', currentValue);
|
this.$emit('change', currentValue);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
return (
|
||||||
|
<div class="van-sku-stepper-stock">
|
||||||
|
<div class="van-sku-stepper-container">
|
||||||
|
<div class="van-sku__stepper-title">{this.stepperTitle || '购买数量'}:</div>
|
||||||
|
<Stepper
|
||||||
|
vModel={this.currentNum}
|
||||||
|
class="van-sku__stepper"
|
||||||
|
max={this.stepperLimit}
|
||||||
|
disableInput={this.disableStepperInput}
|
||||||
|
onOverlimit={this.onOverLimit}
|
||||||
|
onChange={this.onChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{!this.hideStock && <div class="van-sku__stock">{this.stockText}</div>}
|
||||||
|
{!this.hideQuotaText && this.quotaText && (
|
||||||
|
<div class="van-sku__quota">{this.quotaText}</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
|
Loading…
x
Reference in New Issue
Block a user