mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
[improvement] Coupon: jsx (#2456)
This commit is contained in:
parent
c6e1aaf5da
commit
7f853f694a
@ -1,21 +1,9 @@
|
|||||||
<template>
|
import { use } from '../utils';
|
||||||
<cell
|
import Cell from '../cell';
|
||||||
:class="b()"
|
|
||||||
:title="title || $t('title')"
|
|
||||||
:value="value"
|
|
||||||
:border="border"
|
|
||||||
:is-link="editable"
|
|
||||||
:value-class="valueClass"
|
|
||||||
@click="$emit('click')"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
const [sfc, bem, t] = use('coupon-cell');
|
||||||
import create from '../utils/create';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'coupon-cell',
|
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
model: {
|
model: {
|
||||||
prop: 'chosenCoupon'
|
prop: 'chosenCoupon'
|
||||||
},
|
},
|
||||||
@ -31,13 +19,13 @@ export default create({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
},
|
},
|
||||||
chosenCoupon: {
|
|
||||||
type: Number,
|
|
||||||
default: -1
|
|
||||||
},
|
|
||||||
editable: {
|
editable: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true
|
default: true
|
||||||
|
},
|
||||||
|
chosenCoupon: {
|
||||||
|
type: Number,
|
||||||
|
default: -1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -49,12 +37,27 @@ export default create({
|
|||||||
const value = coupon.denominations || coupon.value;
|
const value = coupon.denominations || coupon.value;
|
||||||
return `-${this.currency}${(value / 100).toFixed(2)}`;
|
return `-${this.currency}${(value / 100).toFixed(2)}`;
|
||||||
}
|
}
|
||||||
return coupons.length === 0 ? this.$t('tips') : this.$t('count', coupons.length);
|
return coupons.length === 0 ? t('tips') : t('count', coupons.length);
|
||||||
},
|
},
|
||||||
|
|
||||||
valueClass() {
|
valueClass() {
|
||||||
return this.coupons[this.chosenCoupon] ? 'van-coupon-cell--selected' : '';
|
return this.coupons[this.chosenCoupon] ? 'van-coupon-cell--selected' : '';
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
return (
|
||||||
|
<Cell
|
||||||
|
class={bem()}
|
||||||
|
title={this.title || t('title')}
|
||||||
|
value={this.value}
|
||||||
|
border={this.border}
|
||||||
|
is-link={this.editable}
|
||||||
|
value-class={this.valueClass}
|
||||||
|
onClick={() => {
|
||||||
|
this.$emit('click');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
|
7
packages/coupon-cell/index.less
Normal file
7
packages/coupon-cell/index.less
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
@import '../style/var';
|
||||||
|
|
||||||
|
.van-coupon-cell {
|
||||||
|
&--selected {
|
||||||
|
color: @text-color;
|
||||||
|
}
|
||||||
|
}
|
@ -1,81 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="b({ disabled })">
|
|
||||||
<div :class="b('content')">
|
|
||||||
<div :class="b('head')">
|
|
||||||
<h2 v-html="faceAmount" />
|
|
||||||
<p v-text="conditionMessage" />
|
|
||||||
</div>
|
|
||||||
<div :class="b('body')">
|
|
||||||
<h2 v-text="data.name" />
|
|
||||||
<p v-text="validPeriod" />
|
|
||||||
<checkbox
|
|
||||||
v-if="chosen"
|
|
||||||
:value="true"
|
|
||||||
:class="b('corner')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<p
|
|
||||||
v-if="disabled && data.reason"
|
|
||||||
v-text="data.reason"
|
|
||||||
:class="b('reason')"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../utils/create';
|
|
||||||
import Checkbox from '../checkbox';
|
|
||||||
|
|
||||||
function padZero(num) {
|
|
||||||
return (num < 10 ? '0' : '') + num;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDate(timeStamp) {
|
|
||||||
const date = new Date(timeStamp * 1000);
|
|
||||||
return `${date.getFullYear()}.${padZero(date.getMonth() + 1)}.${padZero(date.getDate())}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatDiscount(discount) {
|
|
||||||
return (discount / 10).toFixed(discount % 10 === 0 ? 0 : 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatAmount(amount) {
|
|
||||||
return (amount / 100).toFixed(amount % 100 === 0 ? 0 : amount % 10 === 0 ? 1 : 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'coupon-item',
|
|
||||||
|
|
||||||
props: {
|
|
||||||
data: Object,
|
|
||||||
chosen: Boolean,
|
|
||||||
disabled: Boolean,
|
|
||||||
currency: String
|
|
||||||
},
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Checkbox
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
validPeriod() {
|
|
||||||
return `${this.$t('valid')}:${getDate(this.data.startAt)} - ${getDate(this.data.endAt)}`;
|
|
||||||
},
|
|
||||||
|
|
||||||
faceAmount() {
|
|
||||||
return this.data.denominations !== 0
|
|
||||||
? `<span>${this.currency}</span> ${formatAmount(this.data.denominations)}`
|
|
||||||
: this.data.discount !== 0
|
|
||||||
? this.$t('discount', formatDiscount(this.data.discount))
|
|
||||||
: '';
|
|
||||||
},
|
|
||||||
|
|
||||||
conditionMessage() {
|
|
||||||
let condition = this.data.originCondition;
|
|
||||||
condition = condition % 100 === 0 ? Math.round(condition / 100) : (condition / 100).toFixed(2);
|
|
||||||
return this.data.originCondition === 0 ? this.$t('unlimited') : this.$t('condition', condition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
207
packages/coupon-list/index.js
Normal file
207
packages/coupon-list/index.js
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
import { use } from '../utils';
|
||||||
|
import Tab from '../tab';
|
||||||
|
import Tabs from '../tabs';
|
||||||
|
import Field from '../field';
|
||||||
|
import Button from '../button';
|
||||||
|
import Coupon from '../coupon';
|
||||||
|
|
||||||
|
const [sfc, bem, t] = use('coupon-list');
|
||||||
|
const EMPTY_IMAGE = 'https://img.yzcdn.cn/v2/image/wap/trade/new_order/empty@2x.png';
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
model: {
|
||||||
|
prop: 'code'
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
code: String,
|
||||||
|
coupons: Array,
|
||||||
|
disabledCoupons: Array,
|
||||||
|
closeButtonText: String,
|
||||||
|
inputPlaceholder: String,
|
||||||
|
exchangeButtonText: String,
|
||||||
|
exchangeButtonLoading: Boolean,
|
||||||
|
exchangeButtonDisabled: Boolean,
|
||||||
|
exchangeMinLength: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
chosenCoupon: {
|
||||||
|
type: Number,
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
displayedCouponIndex: {
|
||||||
|
type: Number,
|
||||||
|
default: -1
|
||||||
|
},
|
||||||
|
showExchangeBar: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
showCloseButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
currency: {
|
||||||
|
type: String,
|
||||||
|
default: '¥'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
tab: 0,
|
||||||
|
winHeight: window.innerHeight,
|
||||||
|
currentCode: this.code || ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
buttonDisabled() {
|
||||||
|
return (
|
||||||
|
!this.exchangeButtonLoading &&
|
||||||
|
(this.exchangeButtonDisabled ||
|
||||||
|
!this.currentCode ||
|
||||||
|
this.currentCode.length < this.exchangeMinLength)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
title() {
|
||||||
|
return `${t('enable')} (${this.coupons.length})`;
|
||||||
|
},
|
||||||
|
|
||||||
|
disabledTitle() {
|
||||||
|
return `${t('disabled')} (${this.disabledCoupons.length})`;
|
||||||
|
},
|
||||||
|
|
||||||
|
listStyle() {
|
||||||
|
return {
|
||||||
|
height: this.winHeight - (this.showExchangeBar ? 140 : 94) + 'px'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
code(code) {
|
||||||
|
this.currentCode = code;
|
||||||
|
},
|
||||||
|
|
||||||
|
currentCode(code) {
|
||||||
|
this.$emit('input', code);
|
||||||
|
},
|
||||||
|
|
||||||
|
displayedCouponIndex(val) {
|
||||||
|
this.scrollToShowCoupon(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.scrollToShowCoupon(this.displayedCouponIndex);
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onClickExchangeButton() {
|
||||||
|
this.$emit('exchange', this.currentCode);
|
||||||
|
|
||||||
|
// auto clear currentCode when not use v-model
|
||||||
|
if (!this.code) {
|
||||||
|
this.currentCode = '';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// scroll to show specific coupon
|
||||||
|
scrollToShowCoupon(index) {
|
||||||
|
if (index === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const { card, list } = this.$refs;
|
||||||
|
|
||||||
|
/* istanbul ignore next */
|
||||||
|
if (list && card && card[index]) {
|
||||||
|
list.scrollTop = card[index].$el.offsetTop - 100;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
const ExchangeBar = this.showExchangeBar && (
|
||||||
|
<Field
|
||||||
|
v-model={this.currentCode}
|
||||||
|
clearable
|
||||||
|
border={false}
|
||||||
|
class={bem('field')}
|
||||||
|
placeholder={this.inputPlaceholder || t('placeholder')}
|
||||||
|
maxlength="20"
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
slot="button"
|
||||||
|
size="small"
|
||||||
|
type="danger"
|
||||||
|
class={bem('exchange')}
|
||||||
|
text={this.exchangeButtonText || t('exchange')}
|
||||||
|
loading={this.exchangeButtonLoading}
|
||||||
|
disabled={this.buttonDisabled}
|
||||||
|
onClick={this.onClickExchangeButton}
|
||||||
|
/>
|
||||||
|
</Field>
|
||||||
|
);
|
||||||
|
|
||||||
|
const Empty = (
|
||||||
|
<div class={bem('empty')}>
|
||||||
|
<img src={EMPTY_IMAGE} />
|
||||||
|
<p>{t('empty')}</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const onChange = index => () => this.$emit('change', index);
|
||||||
|
|
||||||
|
const CouponTab = (
|
||||||
|
<Tab title={this.title}>
|
||||||
|
<div class={bem('list')} style={this.listStyle}>
|
||||||
|
{this.coupons.map((coupon, index) => (
|
||||||
|
<Coupon
|
||||||
|
ref="card"
|
||||||
|
key={coupon.id}
|
||||||
|
coupon={coupon}
|
||||||
|
currency={this.currency}
|
||||||
|
chosen={index === this.chosenCoupon}
|
||||||
|
nativeOnClick={onChange(index)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
{!this.coupons.length && Empty}
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
);
|
||||||
|
|
||||||
|
const DisabledCouponTab = (
|
||||||
|
<Tab title={this.disabledTitle}>
|
||||||
|
<div class={bem('list')} style={this.listStyle}>
|
||||||
|
{this.disabledCoupons.map(coupon => (
|
||||||
|
<Coupon disabled key={coupon.id} coupon={coupon} currency={this.currency} />
|
||||||
|
))}
|
||||||
|
{!this.disabledCoupons.length && Empty}
|
||||||
|
</div>
|
||||||
|
</Tab>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={bem()}>
|
||||||
|
{ExchangeBar}
|
||||||
|
<Tabs v-model={this.tab} class={bem('tab')} line-width={120}>
|
||||||
|
{CouponTab}
|
||||||
|
{DisabledCouponTab}
|
||||||
|
</Tabs>
|
||||||
|
<Button
|
||||||
|
v-show={this.showCloseButton}
|
||||||
|
size="large"
|
||||||
|
class={bem('close')}
|
||||||
|
text={this.closeButtonText || t('close')}
|
||||||
|
onClick={onChange(-1)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,148 +1,47 @@
|
|||||||
@import '../style/var';
|
@import '../style/var';
|
||||||
@import '../style/mixins/ellipsis';
|
|
||||||
|
|
||||||
.van-coupon {
|
.van-coupon-list {
|
||||||
&-cell--selected {
|
height: 100%;
|
||||||
color: @text-color;
|
position: relative;
|
||||||
|
background-color: @background-color;
|
||||||
|
|
||||||
|
&__field {
|
||||||
|
padding: 7px 15px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&-list {
|
&__exchange {
|
||||||
height: 100%;
|
height: 32px;
|
||||||
position: relative;
|
line-height: 30px;
|
||||||
background-color: @background-color;
|
|
||||||
|
|
||||||
&__field {
|
|
||||||
padding: 7px 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__exchange {
|
|
||||||
height: 32px;
|
|
||||||
line-height: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__list {
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 15px 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__close {
|
|
||||||
left: 0;
|
|
||||||
bottom: 0;
|
|
||||||
position: absolute;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__empty {
|
|
||||||
padding-top: 100px;
|
|
||||||
text-align: center;
|
|
||||||
|
|
||||||
p {
|
|
||||||
color: @gray-dark;
|
|
||||||
margin: 15px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 80px;
|
|
||||||
height: 84px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&-item {
|
&__list {
|
||||||
overflow: hidden;
|
overflow-y: auto;
|
||||||
border-radius: 4px;
|
padding: 15px 0;
|
||||||
margin: 0 15px 15px;
|
box-sizing: border-box;
|
||||||
background-color: @white;
|
-webkit-overflow-scrolling: touch;
|
||||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
|
}
|
||||||
|
|
||||||
&:active {
|
&__close {
|
||||||
background-color: @active-color;
|
left: 0;
|
||||||
}
|
bottom: 0;
|
||||||
|
position: absolute;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
&__content {
|
&__empty {
|
||||||
display: flex;
|
padding-top: 100px;
|
||||||
height: 100px;
|
text-align: center;
|
||||||
padding: 24px 0 0 15px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
h2 {
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.ellipsis();
|
|
||||||
}
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
height: 34px;
|
|
||||||
font-weight: 500;
|
|
||||||
line-height: 34px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-size: 12px;
|
|
||||||
line-height: 16px;
|
|
||||||
color: @gray-dark;
|
color: @gray-dark;
|
||||||
|
margin: 15px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__head {
|
img {
|
||||||
min-width: 90px;
|
width: 80px;
|
||||||
|
height: 84px;
|
||||||
h2 {
|
|
||||||
color: @red;
|
|
||||||
font-size: 24px;
|
|
||||||
|
|
||||||
span {
|
|
||||||
font-size: 50%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__body {
|
|
||||||
flex: 1;
|
|
||||||
position: relative;
|
|
||||||
border-radius: 0 4px 4px 0;
|
|
||||||
|
|
||||||
h2 {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__corner {
|
|
||||||
top: 16px;
|
|
||||||
right: 15px;
|
|
||||||
position: absolute;
|
|
||||||
|
|
||||||
.van-icon {
|
|
||||||
border-color: @red;
|
|
||||||
background-color: @red;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__reason {
|
|
||||||
padding: 7px 15px;
|
|
||||||
border-top: 1px dashed @border-color;
|
|
||||||
background-color: @background-color-light;
|
|
||||||
}
|
|
||||||
|
|
||||||
&--disabled {
|
|
||||||
&:active {
|
|
||||||
background-color: @white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.van-coupon-item__content {
|
|
||||||
height: 90px;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
h2,
|
|
||||||
span {
|
|
||||||
color: @gray-dark;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,218 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div :class="b()">
|
|
||||||
<field
|
|
||||||
v-if="showExchangeBar"
|
|
||||||
v-model="currentCode"
|
|
||||||
clearable
|
|
||||||
:border="false"
|
|
||||||
:class="b('field')"
|
|
||||||
:placeholder="inputPlaceholder || $t('placeholder')"
|
|
||||||
:maxlength="20"
|
|
||||||
>
|
|
||||||
<van-button
|
|
||||||
slot="button"
|
|
||||||
size="small"
|
|
||||||
type="danger"
|
|
||||||
:class="b('exchange')"
|
|
||||||
:text="exchangeButtonText || $t('exchange')"
|
|
||||||
:loading="exchangeButtonLoading"
|
|
||||||
:disabled="buttonDisabled"
|
|
||||||
@click="onClickExchangeButton"
|
|
||||||
/>
|
|
||||||
</field>
|
|
||||||
<tabs
|
|
||||||
v-model="tab"
|
|
||||||
:class="b('tab')"
|
|
||||||
:line-width="120"
|
|
||||||
>
|
|
||||||
<tab :title="title">
|
|
||||||
<div
|
|
||||||
:class="b('list')"
|
|
||||||
:style="listStyle"
|
|
||||||
>
|
|
||||||
<coupon-item
|
|
||||||
ref="card"
|
|
||||||
v-for="(item, index) in coupons"
|
|
||||||
:key="item.id || item.name"
|
|
||||||
:data="item"
|
|
||||||
:currency="currency"
|
|
||||||
:chosen="index === chosenCoupon"
|
|
||||||
@click.native="$emit('change', index)"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="!coupons.length"
|
|
||||||
:class="b('empty')"
|
|
||||||
>
|
|
||||||
<img src="https://img.yzcdn.cn/v2/image/wap/trade/new_order/empty@2x.png">
|
|
||||||
<p v-text="$t('empty')" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</tab>
|
|
||||||
<tab :title="disabledTitle">
|
|
||||||
<div
|
|
||||||
:class="b('list')"
|
|
||||||
:style="listStyle"
|
|
||||||
>
|
|
||||||
<coupon-item
|
|
||||||
disabled
|
|
||||||
v-for="item in disabledCoupons"
|
|
||||||
:key="item.id || item.name"
|
|
||||||
:data="item"
|
|
||||||
:currency="currency"
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
v-if="!disabledCoupons.length"
|
|
||||||
:class="b('empty')"
|
|
||||||
>
|
|
||||||
<img src="https://img.yzcdn.cn/v2/image/wap/trade/new_order/empty@2x.png">
|
|
||||||
<p v-text="$t('empty')" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</tab>
|
|
||||||
</tabs>
|
|
||||||
<van-button
|
|
||||||
v-show="showCloseButton"
|
|
||||||
size="large"
|
|
||||||
:class="b('close')"
|
|
||||||
:text="closeButtonText || $t('close')"
|
|
||||||
@click="$emit('change', -1)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import create from '../utils/create';
|
|
||||||
import CouponItem from './Item';
|
|
||||||
import Field from '../field';
|
|
||||||
import VanButton from '../button';
|
|
||||||
import Tab from '../tab';
|
|
||||||
import Tabs from '../tabs';
|
|
||||||
|
|
||||||
export default create({
|
|
||||||
name: 'coupon-list',
|
|
||||||
|
|
||||||
components: {
|
|
||||||
Tab,
|
|
||||||
Tabs,
|
|
||||||
Field,
|
|
||||||
VanButton,
|
|
||||||
CouponItem
|
|
||||||
},
|
|
||||||
|
|
||||||
model: {
|
|
||||||
prop: 'code'
|
|
||||||
},
|
|
||||||
|
|
||||||
props: {
|
|
||||||
code: String,
|
|
||||||
coupons: Array,
|
|
||||||
disabledCoupons: Array,
|
|
||||||
closeButtonText: String,
|
|
||||||
inputPlaceholder: String,
|
|
||||||
exchangeButtonText: String,
|
|
||||||
exchangeButtonLoading: Boolean,
|
|
||||||
exchangeButtonDisabled: Boolean,
|
|
||||||
exchangeMinLength: {
|
|
||||||
type: Number,
|
|
||||||
default: 1
|
|
||||||
},
|
|
||||||
chosenCoupon: {
|
|
||||||
type: Number,
|
|
||||||
default: -1
|
|
||||||
},
|
|
||||||
displayedCouponIndex: {
|
|
||||||
type: Number,
|
|
||||||
default: -1
|
|
||||||
},
|
|
||||||
showExchangeBar: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
showCloseButton: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
currency: {
|
|
||||||
type: String,
|
|
||||||
default: '¥'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
tab: 0,
|
|
||||||
winHeight: window.innerHeight,
|
|
||||||
currentCode: this.code || ''
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
|
||||||
buttonDisabled() {
|
|
||||||
return (
|
|
||||||
!this.exchangeButtonLoading &&
|
|
||||||
(this.exchangeButtonDisabled || !this.currentCode ||
|
|
||||||
this.currentCode.length < this.exchangeMinLength)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
title() {
|
|
||||||
return `${this.$t('enable')} (${this.coupons.length})`;
|
|
||||||
},
|
|
||||||
|
|
||||||
disabledTitle() {
|
|
||||||
return `${this.$t('disabled')} (${this.disabledCoupons.length})`;
|
|
||||||
},
|
|
||||||
|
|
||||||
listStyle() {
|
|
||||||
return {
|
|
||||||
height: this.winHeight - (this.showExchangeBar ? 140 : 94) + 'px'
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
watch: {
|
|
||||||
code(code) {
|
|
||||||
this.currentCode = code;
|
|
||||||
},
|
|
||||||
|
|
||||||
currentCode(code) {
|
|
||||||
this.$emit('input', code);
|
|
||||||
},
|
|
||||||
|
|
||||||
displayedCouponIndex(val) {
|
|
||||||
this.scrollToShowCoupon(val);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.scrollToShowCoupon(this.displayedCouponIndex);
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onClickExchangeButton() {
|
|
||||||
this.$emit('exchange', this.currentCode);
|
|
||||||
|
|
||||||
// auto clear currentCode when not use v-model
|
|
||||||
if (!this.code) {
|
|
||||||
this.currentCode = '';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// scroll to show specific coupon
|
|
||||||
scrollToShowCoupon(index) {
|
|
||||||
if (index === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
const { card, list } = this.$refs;
|
|
||||||
|
|
||||||
/* istanbul ignore next */
|
|
||||||
if (list && card && card[index]) {
|
|
||||||
list.scrollTop = card[index].$el.offsetTop - 100;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,4 +1,4 @@
|
|||||||
import CouponList from '../../coupon-list';
|
import CouponList from '..';
|
||||||
import CouponCell from '../../coupon-cell';
|
import CouponCell from '../../coupon-cell';
|
||||||
import { mount } from '../../../test/utils';
|
import { mount } from '../../../test/utils';
|
||||||
|
|
||||||
|
74
packages/coupon/index.js
Normal file
74
packages/coupon/index.js
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
import { use } from '../utils';
|
||||||
|
import Checkbox from '../checkbox';
|
||||||
|
|
||||||
|
const [sfc, bem, t] = use('coupon');
|
||||||
|
|
||||||
|
function padZero(num) {
|
||||||
|
return (num < 10 ? '0' : '') + num;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDate(timeStamp) {
|
||||||
|
const date = new Date(timeStamp * 1000);
|
||||||
|
return `${date.getFullYear()}.${padZero(date.getMonth() + 1)}.${padZero(date.getDate())}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDiscount(discount) {
|
||||||
|
return (discount / 10).toFixed(discount % 10 === 0 ? 0 : 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatAmount(amount) {
|
||||||
|
return (amount / 100).toFixed(amount % 100 === 0 ? 0 : amount % 10 === 0 ? 1 : 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default sfc({
|
||||||
|
props: {
|
||||||
|
coupon: Object,
|
||||||
|
chosen: Boolean,
|
||||||
|
disabled: Boolean,
|
||||||
|
currency: {
|
||||||
|
type: String,
|
||||||
|
default: '¥'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
validPeriod() {
|
||||||
|
return `${t('valid')}:${getDate(this.coupon.startAt)} - ${getDate(this.coupon.endAt)}`;
|
||||||
|
},
|
||||||
|
|
||||||
|
faceAmount() {
|
||||||
|
return this.coupon.denominations
|
||||||
|
? `<span>${this.currency}</span> ${formatAmount(this.coupon.denominations)}`
|
||||||
|
: this.coupon.discount
|
||||||
|
? t('discount', formatDiscount(this.coupon.discount))
|
||||||
|
: '';
|
||||||
|
},
|
||||||
|
|
||||||
|
conditionMessage() {
|
||||||
|
let condition = this.coupon.originCondition;
|
||||||
|
condition = condition % 100 === 0 ? Math.round(condition / 100) : (condition / 100).toFixed(2);
|
||||||
|
return condition === 0 ? t('unlimited') : t('condition', condition);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render(h) {
|
||||||
|
const { coupon, disabled } = this;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={bem({ disabled: this.disabled })}>
|
||||||
|
<div class={bem('content')}>
|
||||||
|
<div class={bem('head')}>
|
||||||
|
<h2 domPropsInnerHTML={this.faceAmount} />
|
||||||
|
<p>{this.conditionMessage}</p>
|
||||||
|
</div>
|
||||||
|
<div class={bem('body')}>
|
||||||
|
<h2>{coupon.name}</h2>
|
||||||
|
<p>{this.validPeriod}</p>
|
||||||
|
{this.chosen && <Checkbox value={true} class={bem('corner')} />}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{(disabled && coupon.reason) && <p class={bem('reason')}>{coupon.reason}</p>}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
96
packages/coupon/index.less
Normal file
96
packages/coupon/index.less
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
@import '../style/var';
|
||||||
|
@import '../style/mixins/ellipsis';
|
||||||
|
|
||||||
|
.van-coupon {
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin: 0 15px 15px;
|
||||||
|
background-color: @white;
|
||||||
|
box-shadow: 0 0 4px rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: @active-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__content {
|
||||||
|
display: flex;
|
||||||
|
height: 100px;
|
||||||
|
padding: 24px 0 0 15px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h2 {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.ellipsis();
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
height: 34px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 34px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 16px;
|
||||||
|
color: @gray-dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__head {
|
||||||
|
min-width: 90px;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
color: @red;
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__body {
|
||||||
|
flex: 1;
|
||||||
|
position: relative;
|
||||||
|
border-radius: 0 4px 4px 0;
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__corner {
|
||||||
|
top: 16px;
|
||||||
|
right: 15px;
|
||||||
|
position: absolute;
|
||||||
|
|
||||||
|
.van-icon {
|
||||||
|
border-color: @red;
|
||||||
|
background-color: @red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__reason {
|
||||||
|
padding: 7px 15px;
|
||||||
|
border-top: 1px dashed @border-color;
|
||||||
|
background-color: @background-color-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--disabled {
|
||||||
|
&:active {
|
||||||
|
background-color: @white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.van-coupon-item__content {
|
||||||
|
height: 90px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p,
|
||||||
|
h2,
|
||||||
|
span {
|
||||||
|
color: @gray-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -19,6 +19,7 @@ import CollapseItem from './collapse-item';
|
|||||||
import ContactCard from './contact-card';
|
import ContactCard from './contact-card';
|
||||||
import ContactEdit from './contact-edit';
|
import ContactEdit from './contact-edit';
|
||||||
import ContactList from './contact-list';
|
import ContactList from './contact-list';
|
||||||
|
import Coupon from './coupon';
|
||||||
import CouponCell from './coupon-cell';
|
import CouponCell from './coupon-cell';
|
||||||
import CouponList from './coupon-list';
|
import CouponList from './coupon-list';
|
||||||
import DatetimePicker from './datetime-picker';
|
import DatetimePicker from './datetime-picker';
|
||||||
@ -93,6 +94,7 @@ const components = [
|
|||||||
ContactCard,
|
ContactCard,
|
||||||
ContactEdit,
|
ContactEdit,
|
||||||
ContactList,
|
ContactList,
|
||||||
|
Coupon,
|
||||||
CouponCell,
|
CouponCell,
|
||||||
CouponList,
|
CouponList,
|
||||||
DatetimePicker,
|
DatetimePicker,
|
||||||
@ -178,6 +180,7 @@ export {
|
|||||||
ContactCard,
|
ContactCard,
|
||||||
ContactEdit,
|
ContactEdit,
|
||||||
ContactList,
|
ContactList,
|
||||||
|
Coupon,
|
||||||
CouponCell,
|
CouponCell,
|
||||||
CouponList,
|
CouponList,
|
||||||
DatetimePicker,
|
DatetimePicker,
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
/* base */
|
/* base */
|
||||||
@import './style/base';
|
@import './style/base';
|
||||||
|
|
||||||
// /* common components */
|
/* common components */
|
||||||
@import './col/index';
|
@import './col/index';
|
||||||
@import './row/index';
|
@import './row/index';
|
||||||
@import './badge/index';
|
@import './badge/index';
|
||||||
@ -63,6 +63,8 @@
|
|||||||
@import './contact-card/index';
|
@import './contact-card/index';
|
||||||
@import './contact-list/index';
|
@import './contact-list/index';
|
||||||
@import './contact-edit/index';
|
@import './contact-edit/index';
|
||||||
|
@import './coupon/index';
|
||||||
|
@import './coupon-cell/index';
|
||||||
@import './coupon-list/index';
|
@import './coupon-list/index';
|
||||||
@import './goods-action/index';
|
@import './goods-action/index';
|
||||||
@import './goods-action-big-btn/index';
|
@import './goods-action-big-btn/index';
|
||||||
|
@ -28,6 +28,12 @@ export default {
|
|||||||
vanSubmitBar: {
|
vanSubmitBar: {
|
||||||
label: 'Total:'
|
label: 'Total:'
|
||||||
},
|
},
|
||||||
|
vanCoupon: {
|
||||||
|
valid: 'Valid',
|
||||||
|
unlimited: 'Unlimited',
|
||||||
|
discount: discount => `${discount * 10}% off`,
|
||||||
|
condition: condition => `At least ${condition}`
|
||||||
|
},
|
||||||
vanCouponCell: {
|
vanCouponCell: {
|
||||||
title: 'Coupon',
|
title: 'Coupon',
|
||||||
tips: 'Select coupon',
|
tips: 'Select coupon',
|
||||||
@ -41,12 +47,6 @@ export default {
|
|||||||
disabled: 'Unavailable',
|
disabled: 'Unavailable',
|
||||||
placeholder: 'Coupon code'
|
placeholder: 'Coupon code'
|
||||||
},
|
},
|
||||||
vanCouponItem: {
|
|
||||||
valid: 'Valid',
|
|
||||||
unlimited: 'Unlimited',
|
|
||||||
discount: discount => `${discount * 10}% off`,
|
|
||||||
condition: condition => `At least ${condition}`
|
|
||||||
},
|
|
||||||
vanAddressEdit: {
|
vanAddressEdit: {
|
||||||
area: 'Area',
|
area: 'Area',
|
||||||
postal: 'Postal',
|
postal: 'Postal',
|
||||||
|
@ -28,6 +28,12 @@ export default {
|
|||||||
vanSubmitBar: {
|
vanSubmitBar: {
|
||||||
label: '合计:'
|
label: '合计:'
|
||||||
},
|
},
|
||||||
|
vanCoupon: {
|
||||||
|
valid: '有效期',
|
||||||
|
unlimited: '无使用门槛',
|
||||||
|
discount: discount => `${discount}折`,
|
||||||
|
condition: (condition) => `满${condition}元可用`
|
||||||
|
},
|
||||||
vanCouponCell: {
|
vanCouponCell: {
|
||||||
title: '优惠券',
|
title: '优惠券',
|
||||||
tips: '使用优惠',
|
tips: '使用优惠',
|
||||||
@ -41,12 +47,6 @@ export default {
|
|||||||
disabled: '不可使用优惠券',
|
disabled: '不可使用优惠券',
|
||||||
placeholder: '请输入优惠码'
|
placeholder: '请输入优惠码'
|
||||||
},
|
},
|
||||||
vanCouponItem: {
|
|
||||||
valid: '有效期',
|
|
||||||
unlimited: '无使用门槛',
|
|
||||||
discount: discount => `${discount}折`,
|
|
||||||
condition: (condition) => `满${condition}元可用`
|
|
||||||
},
|
|
||||||
vanAddressEdit: {
|
vanAddressEdit: {
|
||||||
area: '地区',
|
area: '地区',
|
||||||
postal: '邮政编码',
|
postal: '邮政编码',
|
||||||
|
@ -28,6 +28,12 @@ export default {
|
|||||||
vanSubmitBar: {
|
vanSubmitBar: {
|
||||||
label: '合計:'
|
label: '合計:'
|
||||||
},
|
},
|
||||||
|
vanCoupon: {
|
||||||
|
valid: '有效期',
|
||||||
|
unlimited: '無使用門檻',
|
||||||
|
discount: discount => `${discount}折`,
|
||||||
|
condition: (condition) => `滿${condition}元可用`
|
||||||
|
},
|
||||||
vanCouponCell: {
|
vanCouponCell: {
|
||||||
title: '優惠券',
|
title: '優惠券',
|
||||||
tips: '使用優惠',
|
tips: '使用優惠',
|
||||||
@ -41,12 +47,6 @@ export default {
|
|||||||
disabled: '不可使用優惠券',
|
disabled: '不可使用優惠券',
|
||||||
placeholder: '請輸入優惠碼'
|
placeholder: '請輸入優惠碼'
|
||||||
},
|
},
|
||||||
vanCouponItem: {
|
|
||||||
valid: '有效期',
|
|
||||||
unlimited: '無使用門檻',
|
|
||||||
discount: discount => `${discount}折`,
|
|
||||||
condition: (condition) => `滿${condition}元可用`
|
|
||||||
},
|
|
||||||
vanAddressEdit: {
|
vanAddressEdit: {
|
||||||
area: '地區',
|
area: '地區',
|
||||||
postal: '郵政編碼',
|
postal: '郵政編碼',
|
||||||
|
@ -28,6 +28,12 @@ export default {
|
|||||||
vanSubmitBar: {
|
vanSubmitBar: {
|
||||||
label: '合計:'
|
label: '合計:'
|
||||||
},
|
},
|
||||||
|
vanCoupon: {
|
||||||
|
valid: '有效期限',
|
||||||
|
unlimited: '無使用門檻',
|
||||||
|
discount: discount => `${discount}折`,
|
||||||
|
condition: (condition) => `滿${condition}元可用`
|
||||||
|
},
|
||||||
vanCouponCell: {
|
vanCouponCell: {
|
||||||
title: '優惠券',
|
title: '優惠券',
|
||||||
tips: '使用優惠',
|
tips: '使用優惠',
|
||||||
@ -41,12 +47,6 @@ export default {
|
|||||||
disabled: '不可使用優惠券',
|
disabled: '不可使用優惠券',
|
||||||
placeholder: '請輸入優惠代碼'
|
placeholder: '請輸入優惠代碼'
|
||||||
},
|
},
|
||||||
vanCouponItem: {
|
|
||||||
valid: '有效期限',
|
|
||||||
unlimited: '無使用門檻',
|
|
||||||
discount: discount => `${discount}折`,
|
|
||||||
condition: (condition) => `滿${condition}元可用`
|
|
||||||
},
|
|
||||||
vanAddressEdit: {
|
vanAddressEdit: {
|
||||||
area: '地區',
|
area: '地區',
|
||||||
postal: '郵遞區號',
|
postal: '郵遞區號',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user