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 */
|
||||
import Vue from 'vue';
|
||||
import Popup from '../popup';
|
||||
@ -138,30 +9,14 @@ import SkuRowItem from './components/SkuRowItem';
|
||||
import SkuStepper from './components/SkuStepper';
|
||||
import SkuMessages from './components/SkuMessages';
|
||||
import SkuActions from './components/SkuActions';
|
||||
import {
|
||||
isAllSelected,
|
||||
isSkuChoosable,
|
||||
getSkuComb,
|
||||
getSelectedSkuValues
|
||||
} from './utils/skuHelper';
|
||||
import { isAllSelected, isSkuChoosable, getSkuComb, getSelectedSkuValues } from './utils/skuHelper';
|
||||
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;
|
||||
|
||||
export default create({
|
||||
name: 'sku',
|
||||
|
||||
components: {
|
||||
Popup,
|
||||
SkuHeader,
|
||||
SkuRow,
|
||||
SkuRowItem,
|
||||
SkuStepper,
|
||||
SkuMessages,
|
||||
SkuActions
|
||||
},
|
||||
|
||||
export default sfc({
|
||||
props: {
|
||||
sku: Object,
|
||||
goods: Object,
|
||||
@ -221,10 +76,7 @@ export default create({
|
||||
show(val) {
|
||||
this.$emit('input', val);
|
||||
if (!val) {
|
||||
const selectedSkuValues = getSelectedSkuValues(
|
||||
this.sku.tree,
|
||||
this.selectedSku
|
||||
);
|
||||
const selectedSkuValues = getSelectedSkuValues(this.sku.tree, this.selectedSku);
|
||||
|
||||
this.$emit('sku-close', {
|
||||
selectedSkuValues,
|
||||
@ -374,15 +226,11 @@ export default create({
|
||||
},
|
||||
|
||||
getSkuCartMessages() {
|
||||
return this.$refs.skuMessages
|
||||
? this.$refs.skuMessages.getCartMessages()
|
||||
: {};
|
||||
return this.$refs.skuMessages ? this.$refs.skuMessages.getCartMessages() : {};
|
||||
},
|
||||
|
||||
validateSkuMessages() {
|
||||
return this.$refs.skuMessages
|
||||
? this.$refs.skuMessages.validateMessages()
|
||||
: '';
|
||||
return this.$refs.skuMessages ? this.$refs.skuMessages.validateMessages() : '';
|
||||
},
|
||||
|
||||
validateSku() {
|
||||
@ -493,6 +341,121 @@ export default create({
|
||||
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>
|
||||
<cell-group :class="b()">
|
||||
<template v-for="(message, index) in messages">
|
||||
<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 { use } from '../../utils';
|
||||
import Cell from '../../cell';
|
||||
import CellGroup from '../../cell-group';
|
||||
import Field from '../../field';
|
||||
import validateEmail from '../../utils/validate/email';
|
||||
import validateNumber from '../../utils/validate/number';
|
||||
import SkuImgUploader from './SkuImgUploader';
|
||||
|
||||
const [sfc, bem] = use('sku-messages');
|
||||
|
||||
const PLACEHOLDER = {
|
||||
id_no: '输入身份证号码',
|
||||
text: '输入文本',
|
||||
@ -47,14 +19,7 @@ const PLACEHOLDER = {
|
||||
mobile: '输入手机号码'
|
||||
};
|
||||
|
||||
export default create({
|
||||
name: 'sku-messages',
|
||||
|
||||
components: {
|
||||
SkuImgUploader,
|
||||
Field
|
||||
},
|
||||
|
||||
export default sfc({
|
||||
props: {
|
||||
messages: Array,
|
||||
messageConfig: Object,
|
||||
@ -73,12 +38,6 @@ export default create({
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
messagePlaceholderMap() {
|
||||
return this.messageConfig.placeholderMap || {};
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
resetMessageValues(messages) {
|
||||
return (messages || []).map(() => ({ value: '' }));
|
||||
@ -125,7 +84,8 @@ export default create({
|
||||
|
||||
getPlaceholder(message) {
|
||||
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() {
|
||||
@ -137,7 +97,7 @@ export default create({
|
||||
|
||||
if (value === '') {
|
||||
// 必填字段的校验
|
||||
if (message.required == '1') { // eslint-disable-line
|
||||
if (String(message.required) === '1') { // eslint-disable-line
|
||||
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>
|
||||
<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 { use } from '../../utils';
|
||||
import Stepper from '../../stepper';
|
||||
import { LIMIT_TYPE } from '../constants';
|
||||
|
||||
const [sfc] = use('sku-stepper');
|
||||
const { QUOTA_LIMIT, STOCK_LIMIT } = LIMIT_TYPE;
|
||||
|
||||
export default create({
|
||||
name: 'sku-stepper',
|
||||
|
||||
components: {
|
||||
Stepper
|
||||
},
|
||||
|
||||
export default sfc({
|
||||
props: {
|
||||
quota: Number,
|
||||
hideQuotaText: Boolean,
|
||||
quotaUsed: Number,
|
||||
hideStock: Boolean,
|
||||
skuEventBus: Object,
|
||||
skuStockNum: Number,
|
||||
selectedSku: Object,
|
||||
selectedSkuComb: Object,
|
||||
selectedNum: Number,
|
||||
stepperTitle: String,
|
||||
hideQuotaText: Boolean,
|
||||
selectedSkuComb: Object,
|
||||
disableStepperInput: Boolean,
|
||||
customStepperConfig: Object
|
||||
},
|
||||
@ -142,6 +109,27 @@ export default create({
|
||||
handleStepperChange && handleStepperChange(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