+
每种一件 · 一次兑完 · 免邮
+
当前余额:¥{{ $filters.unitPrice(item.balance) }}—
@@ -112,6 +120,10 @@ const router = useRouter();
const statusTab = ref("AVAILABLE");
const loading = ref(false);
const total = ref(0);
+const pendingCount = ref(null);
+const loadFailed = ref(false);
+let listRequestId = 0;
+let pendingCountRequestId = 0;
const list = ref([]);
const params = reactive({
pageNumber: 1,
@@ -130,7 +142,6 @@ const bindRules = {
cardSecret: [{ required: true, message: "请输入兑换码", trigger: "blur" }],
};
const noticeModal = ref(false);
-const serviceHotline = ref("12221111");
const activatingId = ref("");
const bindFormRef = ref(null);
@@ -153,11 +164,30 @@ const statusChipLabel = computed(() => {
const map = { AVAILABLE: "可用", UNAVAILABLE: "不可用", PENDING_ACTIVATION: "待激活" };
return map[statusTab.value] || "";
});
+const emptyDescription = computed(() => ({
+ AVAILABLE: "暂无可用礼品卡,可输入卡号和兑换码添加",
+ UNAVAILABLE: "暂无不可用礼品卡",
+ PENDING_ACTIVATION: "暂无待激活礼品卡",
+}[statusTab.value]));
onMounted(() => {
getList();
+ refreshPendingCount();
});
+/** 复用会员分页接口的 total 获取全部待激活数量,独立于当前标签和每页条数。 */
+async function refreshPendingCount() {
+ const requestId = ++pendingCountRequestId;
+ try {
+ const res = await getGiftCardCashMemberCardPage({ memberCardStatus: "PENDING_ACTIVATION", pageNumber: 1, pageSize: 1 });
+ if (requestId === pendingCountRequestId && res.success && res.result) {
+ pendingCount.value = Number(res.result.total) || 0;
+ }
+ } catch {
+ // 统计失败时保留已知数量,不将未知数量显示成零。
+ }
+}
+
function formatYuan(val) {
if (val == null || val === "") {
return "—";
@@ -195,21 +225,32 @@ function onStatusTab(name) {
}
function getList() {
+ const requestId = ++listRequestId;
loading.value = true;
+ loadFailed.value = false;
const p = { ...params };
if (p.memberCardStatus) {
p.memberCardStatus = String(p.memberCardStatus);
}
getGiftCardCashMemberCardPage(p)
.then((res) => {
+ if (requestId !== listRequestId) return;
loading.value = false;
if (res.success && res.result) {
list.value = res.result.records || [];
total.value = res.result.total || 0;
+ if (p.memberCardStatus === "PENDING_ACTIVATION") {
+ ++pendingCountRequestId;
+ pendingCount.value = Number(total.value);
+ }
+ } else {
+ loadFailed.value = true;
}
})
.catch(() => {
+ if (requestId !== listRequestId) return;
loading.value = false;
+ loadFailed.value = true;
});
}
@@ -250,6 +291,7 @@ function submitBind() {
resetBindForm();
params.pageNumber = 1;
getList();
+ refreshPendingCount();
}
})
.catch(() => {
@@ -274,6 +316,7 @@ function onCardPrimaryAction(item) {
if (res.success) {
Message.success(res.message || "激活成功");
getList();
+ refreshPendingCount();
}
})
.catch((err) => {
@@ -285,7 +328,7 @@ function onCardPrimaryAction(item) {
return;
}
if (statusTab.value === "AVAILABLE") {
- router.push({ path: "/goodsList" });
+ router.push(item.cardType === "PICKUP" ? { name: "PickupCard", query: { id: resolveMemberCardId(item) } } : { path: "/goodsList" });
}
}
@@ -308,195 +351,125 @@ function isActivating(item) {
+.gcc-notice-text { line-height: 1.7; color: #515a6e; margin-bottom: 16px; }
+.gcc-notice-footer { text-align: right; }
+
\ No newline at end of file
diff --git a/buyer/src/pages/home/userCenter/PickupCard.vue b/buyer/src/pages/home/userCenter/PickupCard.vue
new file mode 100644
index 000000000..d4d64302b
--- /dev/null
+++ b/buyer/src/pages/home/userCenter/PickupCard.vue
@@ -0,0 +1,194 @@
+
+
+
+
+
+ 提货卡兑换
{{ detail.giftCardName || '选择你的好礼' }}
+ 提货权益
+
+
重新加载
+
+ 查看订单
+
+ {{ ruleText }}每种一件一次兑完免邮
+
+ 选择兑换商品
共 {{ detail.goods?.length || 0 }} 种商品 · 已选 {{ selected.length }} / {{ detail.pickupCount }} 种
+
+
+
+ 暂无图片
+
+
{{ goods.goodsName }}
+
{{ specsText(goods.specs) }}
+
{{ goods.storeName }}
+
暂不可兑换
+
+
+
+
+
+
+
+
+
+
+ 还没有收货地址,请先添加地址后刷新。
+
+
+ 兑换成功后取消不退权益,不支持退款或退货。
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/buyer/src/router/index.js b/buyer/src/router/index.js
index c036ce603..13b7d87cd 100644
--- a/buyer/src/router/index.js
+++ b/buyer/src/router/index.js
@@ -200,6 +200,12 @@ export const router = createRouter({
component: MoneyManagement,
meta: { title: "我的余额" },
},
+ {
+ path: "PickupCard",
+ name: "PickupCard",
+ component: () => import("@/pages/home/userCenter/PickupCard.vue"),
+ meta: { title: "提货卡兑换" },
+ },
{
path: "MyGiftCards",
name: "MyGiftCards",
diff --git a/manager/src/api/promotion.js b/manager/src/api/promotion.js
index 7196bd34b..c220dbbc4 100644
--- a/manager/src/api/promotion.js
+++ b/manager/src/api/promotion.js
@@ -319,6 +319,11 @@ export const giftCardCashBatchCreate = (activityId, params) => {
);
};
+/** 导出指定制卡批次的全部卡号、卡密,不受页面筛选和分页限制。 */
+export const exportGiftCardCashCreateBatch = (createBatchId) => {
+ return getRequest(`/promotion/giftCardCash/createBatch/${encodeURIComponent(createBatchId)}/export`, {}, "blob");
+};
+
// 下载批量发卡 Excel 模板
export const downloadGiftCardCashIssueTemplate = () => {
return getRequest("/promotion/giftCardCash/issue/template", {}, "blob");
@@ -350,4 +355,4 @@ export const getGiftCardCashUsageRecordPage = (params) => {
// 卡密凭证分页(平台库存+会员资产)
export const getGiftCardCashCredentialPage = (params) => {
return getRequest("/promotion/giftCardCash/credential", params);
-};
\ No newline at end of file
+};
diff --git a/manager/src/utils/specs.js b/manager/src/utils/specs.js
new file mode 100644
index 000000000..0e24ea504
--- /dev/null
+++ b/manager/src/utils/specs.js
@@ -0,0 +1,44 @@
+/**
+ * 商品规格展示:兼容简要规格、键值 JSON 和旧数组结构,过滤图片数据,不展示原始 JSON。
+ * @author Bulbasaur
+ * @since 2026-11-06
+ */
+
+/** 将规格中的简单值转为文字,图片对象等复杂数据不参与规格展示。 */
+function specValueText(value) {
+ if (typeof value === "string") return value.trim();
+ if (typeof value === "number" && Number.isFinite(value)) return String(value);
+ if (typeof value === "boolean") return String(value);
+ if (Array.isArray(value)) return value.filter(item => !item || typeof item !== "object").map(specValueText).filter(Boolean).join("、");
+ return "";
+}
+
+/**
+ * 生成可读的 SKU 规格,优先复用接口已有的 simpleSpecs;无有效规格时显示默认规格。
+ * @param {string|object|Array|null} specs SKU 原始规格
+ * @param {string|null} simpleSpecs 接口提供的简要规格
+ * @returns {string} 排除 images 后的规格值,以斜线分隔
+ */
+export function formatSkuSpecs(specs, simpleSpecs) {
+ if (typeof simpleSpecs === "string" && simpleSpecs.trim()) return simpleSpecs.trim();
+ let parsed = specs;
+ if (typeof parsed === "string") {
+ const text = parsed.trim();
+ if (!text) return "默认规格";
+ try {
+ parsed = JSON.parse(text);
+ } catch {
+ return /^[\[{]/.test(text) ? "默认规格" : text;
+ }
+ }
+ let values;
+ if (Array.isArray(parsed)) {
+ values = parsed.filter(item => item && typeof item === "object" && item.specName !== "images")
+ .map(item => specValueText(item.specValue));
+ } else if (parsed && typeof parsed === "object") {
+ values = Object.entries(parsed).filter(([name]) => name !== "images").map(([, value]) => specValueText(value));
+ } else {
+ values = [specValueText(parsed)];
+ }
+ return values.filter(Boolean).join(" / ") || "默认规格";
+}
diff --git a/manager/src/views/promotions/gift-card-cash/gift-card-cash-activity-add.vue b/manager/src/views/promotions/gift-card-cash/gift-card-cash-activity-add.vue
index fc32fd562..1746f8d5c 100644
--- a/manager/src/views/promotions/gift-card-cash/gift-card-cash-activity-add.vue
+++ b/manager/src/views/promotions/gift-card-cash/gift-card-cash-activity-add.vue
@@ -1,38 +1,28 @@
-
-
-