2026-07-29 11:25:29 +08:00

359 lines
8.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<full-page>
<view class="claim-page">
<view v-if="tip" class="tip">{{ tip }}</view>
<view v-else class="tip">加载中...</view>
</view>
<!-- 海报领取弹窗 -->
<view v-if="posterVisible" class="poster-mask">
<view class="poster-dialog">
<image
class="poster-image"
:src="issue?.posterUrl || ''"
mode="widthFix"
/>
<view class="poster-info">
<view class="poster-title">{{ issue?.activityName || "体验券" }}</view>
<view class="poster-desc">{{ projectText }}</view>
<view class="poster-desc">{{ validText }}</view>
</view>
<view class="poster-btn" :class="{ disabled: claiming }" @click="acceptCoupon">
{{ claiming ? "领取中..." : "接受" }}
</view>
</view>
</view>
</full-page>
</template>
<script setup>
import { computed, ref } from "vue";
import { storeToRefs } from "pinia";
import { onLoad, onShow } from "@dcloudio/uni-app";
import useAccount from "@/store/account";
import api from "@/utils/api";
import { confirm, hideLoading, loading, toast } from "@/utils/widget";
import { normalizeCorpId } from "@/utils/api-base-config";
import FullPage from "@/components/full-page.vue";
const { account } = storeToRefs(useAccount());
const { login } = useAccount();
const corpId = ref("");
const issueId = ref("");
const memberId = ref("");
const issue = ref(null);
const tip = ref("");
const claiming = ref(false);
const autoPrompted = ref(false);
const posterVisible = ref(false);
const projectText = computed(() => {
const projects = Array.isArray(issue.value?.projectSnapshot) ? issue.value.projectSnapshot : [];
if (!projects.length) return "未配置项目";
return projects.map((p) => `${p.projectName || "项目"}×${p.usageCount || 1}`).join("、");
});
const validText = computed(() => {
const rule = issue.value?.projectValidRuleSnapshot || {};
if (issue.value?.projectExpireTime) {
return `有效期至 ${formatDate(issue.value.projectExpireTime)}`;
}
if (rule.type === "fixedDate" && rule.fixedDate) {
return `有效期至 ${formatDate(rule.fixedDate)}`;
}
if (rule.type === "daysAfterClaim") {
return `领取后 ${rule.days || 0} 天有效`;
}
return "有效期以活动规则为准";
});
function formatDate(ts) {
const d = new Date(Number(ts));
if (Number.isNaN(d.getTime())) return "-";
const y = d.getFullYear();
const m = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
return `${y}-${m}-${day}`;
}
function buildClaimContent() {
const name = issue.value?.activityName || "体验券";
return `您收到一张【${name}】体验券,包含${projectText.value},是否存入您的权益卡包?`;
}
async function ensureLogin() {
if (!account.value) await login();
if (!account.value) {
tip.value = "请先登录后再领取体验券";
uni.navigateTo({ url: "/pages/login/login" });
return false;
}
return true;
}
async function loadIssueDetail() {
const res = await api(
"getExperienceCouponIssueDetail",
{ corpId: corpId.value, issueId: issueId.value },
false
);
if (!res?.success || !res.data) {
tip.value = res?.message || "体验券不存在或已失效";
return false;
}
issue.value = res.data;
if (!memberId.value && res.data.customerId) {
memberId.value = res.data.customerId;
}
return true;
}
async function hasBoundArchive() {
const openid = account.value?.openid || "";
if (!openid || !corpId.value) return false;
if (memberId.value) {
try {
const detail = await api(
"getCustomerByCustomerId",
{ corpId: corpId.value, customerId: memberId.value },
false
);
const customer = detail?.data || null;
if (customer && String(customer.miniAppId || "") === String(openid)) {
return true;
}
} catch (e) {
console.warn("getCustomerByCustomerId failed", e);
}
}
try {
const countRes = await api(
"getWxAppCustomerCount",
{ miniAppId: openid, corpId: corpId.value },
false
);
return Number(countRes?.data || 0) > 0;
} catch (e) {
console.warn("getWxAppCustomerCount failed", e);
return false;
}
}
function goBindArchive() {
uni.navigateTo({
url: `/pages/archive/archive-manage?corpId=${encodeURIComponent(corpId.value || "")}`,
});
}
function isExpiredIssue() {
const expireAt = Number(issue.value?.projectExpireTime || 0);
if (expireAt && Date.now() > expireAt) return true;
const rule = issue.value?.projectValidRuleSnapshot || {};
if (rule.type === "fixedDate" && rule.fixedDate && Date.now() > Number(rule.fixedDate)) {
return true;
}
return false;
}
async function acceptCoupon() {
if (claiming.value || !issue.value) return;
try {
await confirm(buildClaimContent(), {
title: "确认接受",
confirmText: "确认接受",
cancelText: "暂不领取",
});
} catch {
return;
}
claiming.value = true;
loading("领取中...");
try {
const res = await api("claimExperienceCoupon", {
corpId: corpId.value,
issueId: issueId.value,
claimUserId: account.value?.openid || "",
});
hideLoading();
if (!res?.success) {
tip.value = res?.message || "领取失败";
toast(tip.value);
return;
}
toast("领取成功");
posterVisible.value = false;
uni.redirectTo({
url: `/pages/experience-coupon/my-rights?corpId=${encodeURIComponent(corpId.value)}&customerId=${encodeURIComponent(memberId.value || "")}&name=${encodeURIComponent(issue.value.customerName || "")}`,
});
} catch (e) {
hideLoading();
tip.value = e?.message || "领取失败";
toast(tip.value);
} finally {
claiming.value = false;
}
}
async function promptClaim() {
if (autoPrompted.value) return;
autoPrompted.value = true;
if (!issue.value) return;
if (issue.value.status === "claimed") {
tip.value = "该体验券已领取";
uni.redirectTo({
url: `/pages/experience-coupon/my-rights?corpId=${encodeURIComponent(corpId.value)}&customerId=${encodeURIComponent(memberId.value || "")}`,
});
return;
}
if (issue.value.status === "void") {
tip.value = "该体验券已作废";
return;
}
if (issue.value.status && issue.value.status !== "issued") {
tip.value = "当前状态不可领取";
return;
}
if (isExpiredIssue()) {
tip.value = "体验券已过期,无法领取";
return;
}
const bound = await hasBoundArchive();
if (!bound) {
tip.value = "请先绑定档案后再领取体验券";
try {
await confirm("请先绑定档案后再领取体验券", {
title: "提示",
confirmText: "去绑定",
cancelText: "取消",
});
goBindArchive();
} catch {
// ignore
}
return;
}
if (!issue.value.posterUrl) {
tip.value = "该体验券暂无海报,无法领取";
return;
}
tip.value = "";
posterVisible.value = true;
}
async function bootstrap() {
loading("加载中...");
try {
const okLogin = await ensureLogin();
if (!okLogin) return;
const okDetail = await loadIssueDetail();
if (!okDetail) return;
await promptClaim();
} finally {
hideLoading();
}
}
onLoad((options = {}) => {
corpId.value = normalizeCorpId(options.corpId || "");
issueId.value = options.issueId || options.id || "";
memberId.value = options.memberId || options.customerId || "";
if (!corpId.value || !issueId.value) {
tip.value = "领取参数不完整";
toast(tip.value);
return;
}
bootstrap();
});
onShow(() => {
if (issue.value && issue.value.status === "issued" && tip.value.includes("绑定")) {
autoPrompted.value = false;
promptClaim();
}
});
</script>
<style scoped>
.claim-page {
min-height: 100%;
padding: 30rpx;
box-sizing: border-box;
background: #f6fafa;
}
.tip {
margin-top: 120rpx;
text-align: center;
font-size: 28rpx;
color: #999;
}
.poster-mask {
position: fixed;
inset: 0;
z-index: 1000;
background: rgba(0, 0, 0, 0.65);
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
box-sizing: border-box;
}
.poster-dialog {
width: 100%;
max-width: 620rpx;
background: #fff;
border-radius: 20rpx;
overflow: hidden;
}
.poster-image {
width: 100%;
display: block;
background: #f5f7fa;
}
.poster-info {
padding: 24rpx 28rpx 8rpx;
}
.poster-title {
font-size: 30rpx;
font-weight: 600;
color: #222;
margin-bottom: 8rpx;
}
.poster-desc {
font-size: 24rpx;
color: #888;
line-height: 1.4;
}
.poster-btn {
margin: 24rpx 28rpx 32rpx;
height: 80rpx;
border-radius: 40rpx;
background: #065bd6;
color: #fff;
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.poster-btn.disabled {
opacity: 0.6;
}
</style>