Coupon クーポン - Vant 4
🎫 Coupon クーポンセレクター
🎁 紹介
誰がクーポンを嫌いでしょうか?Coupon クーポンコンポーネントは親切な買い物アシスタントのように、アプリケーションに完全なクーポンエコシステムをもたらします!🛍️
クーポンの華やかな表示から便利な交換機能、そして賢い選択メカニズムまで、このコンポーネントはクーポンの執事のように、ユーザーが割引を楽しみながら、満足感あふれる体験を提供します。クーポンを使うたびに、小さなサプライズボックスを開いているような感じです!✨
📦 インポート
以下の方法でコンポーネントをグローバルに登録します。詳細な登録方法についてはコンポーネントの登録を参照してください。
js
import { createApp } from'vue';
import { CouponCell, CouponList } from'vant';
const app = createApp();
app.use(CouponCell);
app.use(CouponList);🎯 コードデモ
🔧 基本的な使い方 - クーポン旅を始めよう
驚きに満ちた宝箱を開けるように!シンプルな設定で、ユーザーに機能が完全で体験が流れるようなクーポンシステムを提示できます。選択から交換まで、すべてのステップが買い物の楽しさに満ちています。
html
js
import { ref } from'vue'; exportdefault { setup() { const coupon = { available: 1, condition: '条件なし\n最大12元割引', reason: '', value: 150, name: 'クーポン名', startAt: 1489104000, endAt: 1514592000, valueDesc: '1.5', unitDesc: '元', }; const coupons = ref([coupon]); const showList = ref(false); const chosenCoupon = ref(-1); constonChange = (index) => { showList.value = false; chosenCoupon.value = index; }; constonExchange = (code) => { coupons.value.push(coupon); }; return { coupons, showList, onChange, onExchange, chosenCoupon, disabledCoupons: [coupon], }; }, };📖 API
CouponCell プロパティ
| パラメータ | 説明 | タイプ | デフォルト値 |
|---|---|---|---|
| title | セルのタイトル | string | クーポン |
| chosen-coupon | 現在選択されているクーポンのインデックス | *number | number[]* |
| coupons | 利用可能なクーポンリスト | Coupon[] | [] |
| editable | クーポンの切り替えが可能かどうか | boolean | true |
| border | 内側の枠線を表示するかどうか | boolean | true |
| currency | 通貨記号 | string | ¥ |
CouponList プロパティ
| パラメータ | 説明 | タイプ | デフォルト値 |
|---|---|---|---|
| v-model:code | 現在入力されている交換コード | string | - |
| chosen-coupon | 現在選択されているクーポンのインデックス、複数選択をサポート(タイプは []) | *number | number[]* |
| coupons | 利用可能なクーポンリスト | CouponInfo[] | [] |
| disabled-coupons | 利用できないクーポンリスト | CouponInfo[] | [] |
| enabled-title | 利用可能なクーポンリストのタイトル | string | 利用可能なクーポン |
| disabled-title | 利用できないクーポンリストのタイトル | string | 利用できないクーポン |
| exchange-button-text | 交換ボタンのテキスト | string | 交換 |
| exchange-button-loading | 交換ボタンの読み込みアニメーションを表示するかどうか | boolean | false |
| exchange-button-disabled | 交換ボタンを無効にするかどうか | boolean | false |
| exchange-min-length | 交換コードの最小長さ | number | 1 |
| displayed-coupon-index | 特定のクーポン位置にスクロール | number | - |
| show-close-button | リストの下部ボタンを表示するかどうか | boolean | true |
| close-button-text | リストの下部ボタンのテキスト | string | クーポンを使用しない |
| input-placeholder | 入力ボックスのプレースホルダーテキスト | string | クーポンコードを入力してください |
| show-exchange-bar | 交換バーを表示するかどうか | boolean | true |
| currency | 通貨記号 | string | ¥ |
| empty-image | リストが空の時のプレースホルダー画像 | string | - |
| show-count | 利用可能/利用不可の数を表示するかどうか | boolean | true |
CouponList イベント
| イベント名 | 説明 | コールバックパラメータ |
|---|---|---|
| change | クーポン切り替えコールバック | index, 選択されたクーポンのインデックス |
| exchange | クーポン交換コールバック | code, 交換コード |
CouponList Slots
| 名称 | 说明 |
|---|---|
| list-footer | 优惠券列表底部 |
| disabled-list-footer | 不可用优惠券列表底部 |
| list-button | 自定义底部按钮 |
CouponInfo データ構造
| キー名 | 説明 | タイプ |
|---|---|---|
| id | クーポンid | string |
| name | クーポン名 | string |
| condition | 条件 | string |
| startAt | カードの有効開始時刻 (タイムスタンプ, 単位は秒) | number |
| endAt | カードの失効日 (タイムスタンプ, 単位は秒) | number |
| description | 説明情報、クーポンが利用可能な時に表示 | string |
| reason | 利用不可の理由、クーポンが利用不可の時に表示 | string |
| value | 割引券の割引金額、単位は分 | number |
| valueDesc | 割引券の割引金額のテキスト | string |
| unitDesc | 単位のテキスト | string |
タイプ定義
コンポーネントは以下のタイプ定義をエクスポートします:
ts
importtype { CouponCellProps, CouponListProps, CouponInfo } from'vant';テーマカスタマイズ
スタイル変数
コンポーネントは以下の CSS 変数を提供しており、カスタムスタイルに使用できます。使用方法については ConfigProvider コンポーネント を参照してください。
| 名称 | デフォルト値 | 説明 |
|---|---|---|
| --van-coupon-margin | 0 var(--van-padding-sm) var(--van-padding-sm) | - |
| --van-coupon-content-height | 84px | - |
| --van-coupon-content-padding | 14px 0 | - |
| --van-coupon-content-text-color | var(--van-text-color) | - |
| --van-coupon-background | var(--van-background-2) | - |
| --van-coupon-active-background | var(--van-active-color) | - |
| --van-coupon-radius | var(--van-radius-lg) | - |
| --van-coupon-shadow | 0 0 4px rgba(0, 0, 0, 0.1) | - |
| --van-coupon-head-width | 96px | - |
| --van-coupon-amount-color | var(--van-danger-color) | - |
| --van-coupon-amount-font-size | 30px | - |
| --van-coupon-currency-font-size | 40% | - |
| --van-coupon-name-font-size | var(--van-font-size-md) | - |
| --van-coupon-disabled-text-color | var(--van-text-color-2) | - |
| --van-coupon-description-padding | var(--van-padding-xs) var(--van-padding-md) | - |
| --van-coupon-description-border-color | var(--van-border-color) | - |
| --van-coupon-checkbox-color | var(--van-danger-color) | - |
| --van-coupon-list-background | var(--van-background) | - |
| --van-coupon-list-field-padding | 5px 0 5px var(--van-padding-md) | - |
| --van-coupon-list-exchange-button-height | 32px | - |
| --van-coupon-list-close-button-height | 40px | - |
| --van-coupon-list-empty-tip-color | var(--van-text-color-2) | - |
| --van-coupon-list-empty-tip-font-size | var(--van-font-size-md) | - |
| --van-coupon-list-empty-tip-line-height | var(--van-line-height-md) | - |
| --van-coupon-cell-selected-text-color | var(--van-text-color) | - |
🌟 最佳实践
优惠券数据管理
javascript
// 优惠券状态管理
const useCouponStore = () => {
const coupons = ref([]);
const selectedCoupon = ref(-1);
// 获取可用优惠券
const getAvailableCoupons = async (orderAmount) => {
const response = await fetch(`/api/coupons?amount=${orderAmount}`);
const data = await response.json();
return data.filter(coupon => {
// 检查使用条件
if (coupon.minAmount && orderAmount < coupon.minAmount) {
return false;
}
// 检查有效期
const now = Date.now() / 1000;
return coupon.startAt <= now && coupon.endAt >= now;
});
};
// 计算优惠金额
const calculateDiscount = (coupon, orderAmount) => {
if (coupon.type === 'percentage') {
return Math.min(orderAmount * coupon.value / 100, coupon.maxDiscount || Infinity);
}
return Math.min(coupon.value / 100, orderAmount);
};
return {
coupons,
selectedCoupon,
getAvailableCoupons,
calculateDiscount
};
};用户体验优化
javascript
// 智能推荐最优优惠券
const recommendBestCoupon = (coupons, orderAmount) => {
return coupons
.map(coupon => ({
...coupon,
discount: calculateDiscount(coupon, orderAmount)
}))
.sort((a, b) => b.discount - a.discount)[0];
};
// 优惠券到期提醒
const checkExpiringCoupons = (coupons) => {
const threeDaysLater = Date.now() / 1000 + 3 * 24 * 60 * 60;
return coupons.filter(coupon =>
coupon.endAt <= threeDaysLater && coupon.endAt > Date.now() / 1000
);
};
// 动画效果增强
const animateCouponSelection = (index) => {
const couponElement = document.querySelector(`[data-coupon-index="${index}"]`);
if (couponElement) {
couponElement.classList.add('coupon-selected-animation');
setTimeout(() => {
couponElement.classList.remove('coupon-selected-animation');
}, 300);
}
};💡 使用技巧
多种优惠券类型支持
javascript
// クーポンタイプ列挙
const CouponType = {
FIXED: 'fixed', // 固定金額
PERCENTAGE: 'percentage', // パーセンテージ割引
SHIPPING: 'shipping', // 送料無料
GIFT: 'gift' // プレゼント
};
// クーポンファクトリ関数
const createCoupon = (type, config) => {
const baseCoupon = {
id: generateId(),
name: config.name,
startAt: config.startAt,
endAt: config.endAt,
condition: config.condition || '条件なし',
type
};
switch (type) {
case CouponType.FIXED:
return {
...baseCoupon,
value: config.amount * 100, // 转换为分
valueDesc: config.amount.toString(),
unitDesc: '元'
};
case CouponType.PERCENTAGE:
return {
...baseCoupon,
value: config.percentage,
valueDesc: config.percentage.toString(),
unitDesc: '折',
maxDiscount: config.maxDiscount * 100
};
case CouponType.SHIPPING:
return {
...baseCoupon,
value: 0,
valueDesc: '送料無料',
unitDesc: ''
};
default:
return baseCoupon;
}
};优惠券兑换码生成
javascript
// 交換コード生成器
const generateCouponCode = (length = 8) => {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars.charAt(Math.floor(Math.random() * chars.length));
}
return result;
};
// 交換コード検証
const validateCouponCode = (code) => {
const rules = [
{ test: /^[A-Z0-9]+$/, message: '大文字英字と数字のみを含む' },
{ test: code => code.length >= 6, message: '長さは最低6文字' },
{ test: code => code.length <= 12, message: '長さは12文字を超えない' }
];
for (const rule of rules) {
const isValid = typeof rule.test === 'function'
? rule.test(code)
: rule.test.test(code);
if (!isValid) {
return { valid: false, message: rule.message };
}
}
return { valid: true };
};🔧 常见问题解决
优惠券叠加使用
javascript
// Q: 如何实现多张优惠券叠加使用?
// A: クーポンの組み合わせロジックを実装する
const applyCoupons = (coupons, orderAmount) => {
// 優先順位でソート(固定金額 > パーセンテージ > 送料無料)
const sortedCoupons = coupons.sort((a, b) => {
const priority = { fixed: 3, percentage: 2, shipping: 1 };
return priority[b.type] - priority[a.type];
});
let totalDiscount = 0;
let currentAmount = orderAmount;
for (const coupon of sortedCoupons) {
const discount = calculateDiscount(coupon, currentAmount);
totalDiscount += discount;
currentAmount -= discount;
// 過度の割引を防止
if (currentAmount <= 0) {
currentAmount = 0.01; // 最小金額を保持
break;
}
}
return {
totalDiscount,
finalAmount: Math.max(0.01, orderAmount - totalDiscount)
};
};クーポン状態の同期
javascript
// Q: クーポンの複数ページ間での状態同期をどのように処理しますか?
// A: グローバル状態管理を使用する
// Pinia store
export const useCouponStore = defineStore('coupon', {
state: () => ({
availableCoupons: [],
selectedCoupons: [],
lastSyncTime: 0
}),
actions: {
async syncCoupons() {
const now = Date.now();
// 頻繁な同期を避ける
if (now - this.lastSyncTime < 30000) return;
try {
const response = await api.getCoupons();
this.availableCoupons = response.data;
this.lastSyncTime = now;
} catch (error) {
console.error('クーポンの同期に失敗しました:', error);
}
},
selectCoupon(coupon) {
const index = this.selectedCoupons.findIndex(c => c.id === coupon.id);
if (index > -1) {
this.selectedCoupons.splice(index, 1);
} else {
this.selectedCoupons.push(coupon);
}
}
}
});クーポンの有効期限処理
javascript
// 期限切れクーポンの自動クリーンアップ
const cleanExpiredCoupons = () => {
const now = Date.now() / 1000;
coupons.value = coupons.value.filter(coupon => {
if (coupon.endAt < now) {
// 期限切れ通知を送信
showNotify({
type: 'warning',
message: `クーポン「${coupon.name}」の有効期限が切れました`
});
return false;
}
return true;
});
};
// 定期的なチェック
setInterval(cleanExpiredCoupons, 60000); // 1分ごとにチェック🎨 设计灵感
创意优惠券样式
css
/* 撕边效果优惠券 */
.coupon-torn {
position: relative;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 8px;
}
.coupon-torn::before {
content: '';
position: absolute;
top: 50%;
right: -5px;
width: 10px;
height: 10px;
background: white;
border-radius: 50%;
transform: translateY(-50%);
box-shadow:
0 -15px 0 white,
0 15px 0 white,
0 -30px 0 white,
0 30px 0 white;
}
/* 金色VIP优惠券 */
.coupon-vip {
background: linear-gradient(45deg, #ffd700, #ffed4e);
border: 2px solid #ffa000;
box-shadow: 0 4px 20px rgba(255, 215, 0, 0.3);
position: relative;
overflow: hidden;
}
.coupon-vip::after {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(
45deg,
transparent,
rgba(255, 255, 255, 0.3),
transparent
);
animation: shine 3s infinite;
}
@keyframes shine {
0% { transform: translateX(-100%) translateY(-100%) rotate(45deg); }
100% { transform: translateX(100%) translateY(100%) rotate(45deg); }
}
/* 节日主题优惠券 */
.coupon-festival {
background:
radial-gradient(circle at 20% 80%, #ff6b6b 0%, transparent 50%),
radial-gradient(circle at 80% 20%, #4ecdc4 0%, transparent 50%),
radial-gradient(circle at 40% 40%, #45b7d1 0%, transparent 50%);
animation: festival-glow 2s ease-in-out infinite alternate;
}
@keyframes festival-glow {
from { box-shadow: 0 0 20px rgba(255, 107, 107, 0.5); }
to { box-shadow: 0 0 30px rgba(78, 205, 196, 0.5); }
}交互动画效果
css
/* 优惠券选中动画 */
.coupon-selected-animation {
animation: coupon-select 0.3s ease-out;
}
@keyframes coupon-select {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
/* 优惠券翻转效果 */
.coupon-flip {
transition: transform 0.6s;
transform-style: preserve-3d;
}
.coupon-flip:hover {
transform: rotateY(180deg);
}
.coupon-flip .front,
.coupon-flip .back {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
}
.coupon-flip .back {
transform: rotateY(180deg);
}🚀 高级功能扩展
智能优惠券推荐
vue
<template>
<div class="smart-coupon-recommender">
<div class="recommendation-header">
<h3>🎯 为您推荐</h3>
<span class="save-amount">预计节省 ¥{{ recommendedSavings }}</span>
</div>
<div class="recommended-coupons">
<div
v-for="coupon in recommendedCoupons"
:key="coupon.id"
class="recommended-coupon"
@click="selectRecommendedCoupon(coupon)"
>
<div class="coupon-badge">推荐</div>
<van-coupon-cell
:coupons="[coupon]"
:chosen-coupon="0"
/>
<div class="recommendation-reason">
{{ coupon.recommendReason }}
</div>
</div>
</div>
</div>
</template>
<script setup>
const recommendCoupons = (coupons, orderInfo) => {
return coupons
.map(coupon => ({
...coupon,
savings: calculateSavings(coupon, orderInfo),
recommendReason: getRecommendReason(coupon, orderInfo)
}))
.filter(coupon => coupon.savings > 0)
.sort((a, b) => b.savings - a.savings)
.slice(0, 3);
};
const getRecommendReason = (coupon, orderInfo) => {
if (coupon.type === 'shipping' && orderInfo.needsShipping) {
return '送料無料、配送コストを節約します';
}
if (coupon.savings > orderInfo.amount * 0.1) {
return '超値割引、10%以上節約';
}
return '現在の注文に適しており、すぐに利用可能';
};
</script>📚 拡張読み物
技术文档
- Vue 3 组合式API - 现代Vue开发
- Pinia 状态管理 - 轻量级状态管理
- Web Storage API - ローカルストレージ
电商设计
- クーポンデザイン心理学 - ユーザ心理分析
- eコマースプロモーション戦略 - マーケティング戦略ガイド
- モバイルクーポンUX - モバイル体験
関連コンポーネント
- Card カード - カードコンテナコンポーネント
- Cell セル - リストセル
- List リスト - 長いリストコンポーネント
- Popup ポップアップ - ポップアップコンポーネント
- Field 入力ボックス - フォーム入力
- Button ボタン - ボタンコンポーネント
- Badge バッジ - バッジプロンプト
- Tag タグ - タグコンポーネント