index.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. import { use } from '../utils';
  2. import Tab from '../tab';
  3. import Tabs from '../tabs';
  4. import Field from '../field';
  5. import Button from '../button';
  6. import Coupon from '../coupon';
  7. const [sfc, bem, t] = use('coupon-list');
  8. const EMPTY_IMAGE = 'https://img.yzcdn.cn/vant/coupon-empty.png';
  9. export default sfc({
  10. model: {
  11. prop: 'code'
  12. },
  13. props: {
  14. code: String,
  15. coupons: Array,
  16. disabledCoupons: Array,
  17. closeButtonText: String,
  18. inputPlaceholder: String,
  19. exchangeButtonText: String,
  20. exchangeButtonLoading: Boolean,
  21. exchangeButtonDisabled: Boolean,
  22. exchangeMinLength: {
  23. type: Number,
  24. default: 1
  25. },
  26. chosenCoupon: {
  27. type: Number,
  28. default: -1
  29. },
  30. displayedCouponIndex: {
  31. type: Number,
  32. default: -1
  33. },
  34. showExchangeBar: {
  35. type: Boolean,
  36. default: true
  37. },
  38. showCloseButton: {
  39. type: Boolean,
  40. default: true
  41. },
  42. currency: {
  43. type: String,
  44. default: '¥'
  45. }
  46. },
  47. data() {
  48. return {
  49. tab: 0,
  50. winHeight: window.innerHeight,
  51. currentCode: this.code || ''
  52. };
  53. },
  54. computed: {
  55. buttonDisabled() {
  56. return (
  57. !this.exchangeButtonLoading &&
  58. (this.exchangeButtonDisabled ||
  59. !this.currentCode ||
  60. this.currentCode.length < this.exchangeMinLength)
  61. );
  62. },
  63. title() {
  64. return `${t('enable')} (${this.coupons.length})`;
  65. },
  66. disabledTitle() {
  67. return `${t('disabled')} (${this.disabledCoupons.length})`;
  68. },
  69. listStyle() {
  70. return {
  71. height: this.winHeight - (this.showExchangeBar ? 140 : 94) + 'px'
  72. };
  73. }
  74. },
  75. watch: {
  76. code(code) {
  77. this.currentCode = code;
  78. },
  79. currentCode(code) {
  80. this.$emit('input', code);
  81. },
  82. displayedCouponIndex(val) {
  83. this.scrollToShowCoupon(val);
  84. }
  85. },
  86. mounted() {
  87. this.scrollToShowCoupon(this.displayedCouponIndex);
  88. },
  89. methods: {
  90. onClickExchangeButton() {
  91. this.$emit('exchange', this.currentCode);
  92. // auto clear currentCode when not use vModel
  93. if (!this.code) {
  94. this.currentCode = '';
  95. }
  96. },
  97. // scroll to show specific coupon
  98. scrollToShowCoupon(index) {
  99. if (index === -1) {
  100. return;
  101. }
  102. this.$nextTick(() => {
  103. const { card, list } = this.$refs;
  104. /* istanbul ignore next */
  105. if (list && card && card[index]) {
  106. list.scrollTop = card[index].$el.offsetTop - 100;
  107. }
  108. });
  109. },
  110. renderEmpty() {
  111. return (
  112. <div class={bem('empty')}>
  113. <img src={EMPTY_IMAGE} />
  114. <p>{t('empty')}</p>
  115. </div>
  116. );
  117. },
  118. renderExchangeButton() {
  119. return (
  120. <Button
  121. size="small"
  122. type="danger"
  123. class={bem('exchange')}
  124. text={this.exchangeButtonText || t('exchange')}
  125. loading={this.exchangeButtonLoading}
  126. disabled={this.buttonDisabled}
  127. onClick={this.onClickExchangeButton}
  128. />
  129. );
  130. }
  131. },
  132. render(h) {
  133. const ExchangeBar = this.showExchangeBar && (
  134. <Field
  135. vModel={this.currentCode}
  136. clearable
  137. border={false}
  138. class={bem('field')}
  139. placeholder={this.inputPlaceholder || t('placeholder')}
  140. maxlength="20"
  141. scopedSlots={{
  142. button: this.renderExchangeButton
  143. }}
  144. />
  145. );
  146. const onChange = index => () => this.$emit('change', index);
  147. const CouponTab = (
  148. <Tab title={this.title}>
  149. <div class={bem('list')} style={this.listStyle}>
  150. {this.coupons.map((coupon, index) => (
  151. <Coupon
  152. ref="card"
  153. key={coupon.id}
  154. coupon={coupon}
  155. currency={this.currency}
  156. chosen={index === this.chosenCoupon}
  157. nativeOnClick={onChange(index)}
  158. />
  159. ))}
  160. {!this.coupons.length && this.renderEmpty()}
  161. </div>
  162. </Tab>
  163. );
  164. const DisabledCouponTab = (
  165. <Tab title={this.disabledTitle}>
  166. <div class={bem('list')} style={this.listStyle}>
  167. {this.disabledCoupons.map(coupon => (
  168. <Coupon disabled key={coupon.id} coupon={coupon} currency={this.currency} />
  169. ))}
  170. {!this.disabledCoupons.length && this.renderEmpty()}
  171. </div>
  172. </Tab>
  173. );
  174. return (
  175. <div class={bem()}>
  176. {ExchangeBar}
  177. <Tabs vModel={this.tab} class={bem('tab')} line-width={120}>
  178. {CouponTab}
  179. {DisabledCouponTab}
  180. </Tabs>
  181. <Button
  182. vShow={this.showCloseButton}
  183. size="large"
  184. class={bem('close')}
  185. text={this.closeButtonText || t('close')}
  186. onClick={onChange(-1)}
  187. />
  188. </div>
  189. );
  190. }
  191. });