diff --git a/apps/mall/locales/marketing.ts b/apps/mall/locales/marketing.ts
index 3315b30..4290d8b 100644
--- a/apps/mall/locales/marketing.ts
+++ b/apps/mall/locales/marketing.ts
@@ -37,6 +37,24 @@ export default {
login: "Sign in",
register: "Create account",
noIntegralProducts: "No rewards available right now.",
+ addressLabel: "Ship to",
+ noAddress: "No saved address.",
+ addAddress: "Add one in your account",
+ addressRequired: "Add a shipping address before redeeming.",
+ qty: "Qty",
+ redeemSuccess: "Redeemed as {no}.",
+ redeemConflict: "Unable to redeem: not enough points or stock.",
+ redeemFailed: "Unable to redeem right now.",
+ redemptionHistory: "My redemptions",
+ orderNo: "Order no.",
+ reward: "Reward",
+ pointsCost: "Points",
+ createdAt: "Date",
+ redemptionStatus: {
+ pending_fulfillment: "Pending",
+ fulfilled: "Fulfilled",
+ cancelled: "Cancelled",
+ },
},
},
zh: {
@@ -77,6 +95,24 @@ export default {
login: "登录",
register: "注册",
noIntegralProducts: "当前暂无积分商品。",
+ addressLabel: "收货地址",
+ noAddress: "暂无收货地址。",
+ addAddress: "前往账户添加",
+ addressRequired: "请先添加收货地址。",
+ qty: "数量",
+ redeemSuccess: "兑换成功,订单号 {no}。",
+ redeemConflict: "兑换失败:积分或库存不足。",
+ redeemFailed: "兑换失败,请稍后重试。",
+ redemptionHistory: "我的兑换",
+ orderNo: "订单号",
+ reward: "兑换商品",
+ pointsCost: "积分",
+ createdAt: "日期",
+ redemptionStatus: {
+ pending_fulfillment: "待发货",
+ fulfilled: "已发货",
+ cancelled: "已取消",
+ },
},
},
} as const;
diff --git a/apps/mall/pages/integral.vue b/apps/mall/pages/integral.vue
index dd1ba10..15115d7 100644
--- a/apps/mall/pages/integral.vue
+++ b/apps/mall/pages/integral.vue
@@ -1,15 +1,35 @@
@@ -86,26 +165,86 @@ onMounted(() => {
{{ t("marketing.recommendation") }}
-
-
+
+
+
+
+ {{ error }}
+ {{ notice }}
+
+ {{ t("common.loading") }}
+
+
-
![]()
+
{{ pick(item.name, locale) }}
-
{{ t("marketing.points", { n: item.points }) }}
-
+
{{ t("marketing.points", { n: item.points_price }) }}
{{ t("marketing.stock", { n: item.stock }) }}
-
+
+
+
+ {{ t("marketing.redemptionHistory") }}
+
+
+
+
+ | {{ t("marketing.orderNo") }} |
+ {{ t("marketing.reward") }} |
+ {{ t("marketing.qty") }} |
+ {{ t("marketing.pointsCost") }} |
+ {{ t("common.status") }} |
+ {{ t("marketing.createdAt") }} |
+
+
+
+
+ | {{ order.order_no }} |
+ {{ order.items.map((line) => pick(line.name, locale)).join(", ") }} |
+ {{ order.items.reduce((n, line) => n + line.qty, 0) }} |
+ {{ order.total_points }} |
+ {{ t(`marketing.redemptionStatus.${order.status}`) }} |
+ {{ order.created_at.slice(0, 10) }} |
+
+
+
+
@@ -218,6 +357,50 @@ onMounted(() => {
font-size: 17px;
font-weight: 500;
}
+.redeem-bar {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 14px 18px;
+ border: 1px solid var(--mall-line);
+ border-top: none;
+ background: #fffafa;
+}
+.redeem-address {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ font-size: 13px;
+ color: var(--mall-muted);
+}
+.redeem-error {
+ margin: 12px 0 0;
+ padding: 10px 14px;
+ color: #b42318;
+ background: #fff1f0;
+ border: 1px solid #ffd6d2;
+}
+.redeem-notice {
+ margin: 12px 0 0;
+ padding: 10px 14px;
+ color: #067647;
+ background: #ecfdf3;
+ border: 1px solid #abefc6;
+}
+.qty-line {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin: 8px 0 11px;
+ color: var(--mall-faint);
+ font-size: 12px;
+}
+.qty-input {
+ width: 72px;
+}
+.history-heading {
+ margin-top: 26px;
+}
.integral-grid {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));