feat: 完成前端业务闭环与后端联调
This commit is contained in:
@@ -11,6 +11,31 @@
|
||||
<text>家谱服务推荐</text>
|
||||
<text>这里会展示家谱相关服务和活动。</text>
|
||||
</view>
|
||||
<text v-if="operationFeedback" class="promotion-feedback" role="status">{{ operationFeedback }}</text>
|
||||
<view class="referral-card">
|
||||
<view class="referral-card__heading">
|
||||
<text>邀请家人注册</text>
|
||||
<text>推荐关系由注册接口一次性确认,前端不会在注册后补绑。</text>
|
||||
</view>
|
||||
<AppLoading v-if="referralState === 'loading'" text="正在读取我的推荐码" />
|
||||
<view v-else-if="referralState === 'error'" class="referral-card__state">
|
||||
<text>{{ referralError || "推荐码暂时无法读取。" }}</text>
|
||||
<AppButton compact type="secondary" label="重试" @click="loadReferralProfile" />
|
||||
</view>
|
||||
<view v-else-if="referralProfile.enabled" class="referral-card__content">
|
||||
<text class="referral-card__code">{{ referralProfile.referralCode }}</text>
|
||||
<text>已成功邀请 {{ referralProfile.referredUserCount }} 人</text>
|
||||
<text>{{ referralProfile.shareDescription || "家人通过此链接注册后,系统会记录推荐关系。" }}</text>
|
||||
<view class="referral-card__actions">
|
||||
<AppButton compact type="secondary" label="复制推荐码" @click="copyReferralCode" />
|
||||
<AppButton compact type="secondary" label="复制推荐链接" @click="copyReferralLink" />
|
||||
<AppButton compact label="分享给家人" @click="shareReferral" />
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="referral-card__state">
|
||||
<text>{{ referralProfile.disabledReason || "推荐功能暂未开放。" }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="promotionListState === 'loading'" class="promotion-state-card">
|
||||
<AppLoading text="正在读取推广内容" />
|
||||
</view>
|
||||
@@ -24,7 +49,6 @@
|
||||
<text>后续正式发布的家谱服务推荐会在这里展示。</text>
|
||||
</view>
|
||||
<view v-else class="promotion-list">
|
||||
<text v-if="operationFeedback" class="promotion-feedback" role="status">{{ operationFeedback }}</text>
|
||||
<view
|
||||
v-for="item in promotions"
|
||||
:key="item.id"
|
||||
@@ -42,7 +66,7 @@
|
||||
/>
|
||||
<text class="promotion-card__title">{{ item.title }}</text>
|
||||
<text v-if="item.description" class="promotion-card__description">{{ item.description }}</text>
|
||||
<view v-if="item.targetUrl" class="promotion-card__actions">
|
||||
<view v-if="item.targetUrl" class="promotion-card__actions" @click.stop>
|
||||
<AppButton compact type="secondary" label="复制链接" @click.stop="copyPromotionLink(item)" />
|
||||
<AppButton compact label="分享给家人" @click.stop="sharePromotion(item)" />
|
||||
</view>
|
||||
@@ -63,6 +87,7 @@ import {
|
||||
createRequestController,
|
||||
isRequestCancelled
|
||||
} from "@/services/api/request-controller.js";
|
||||
import { referralApi } from "@/services/api/referral-service.js";
|
||||
import { siteContentApi } from "@/services/api/site-content-service.js";
|
||||
import { getRequestErrorMessage } from "@/services/api/request-error-message.js";
|
||||
import { goBack, handleBackPress, openSiteContentTarget } from "@/utils/navigation/gateway.js";
|
||||
@@ -71,9 +96,39 @@ const promotions = ref([]);
|
||||
const promotionListState = ref("loading");
|
||||
const promotionListError = ref("");
|
||||
const operationFeedback = ref("");
|
||||
const referralState = ref("loading");
|
||||
const referralError = ref("");
|
||||
const referralProfile = ref({
|
||||
enabled: false,
|
||||
disabledReason: "",
|
||||
referralCode: "",
|
||||
shareTitle: "",
|
||||
shareDescription: "",
|
||||
shareUrl: "",
|
||||
referredUserCount: 0,
|
||||
});
|
||||
const promotionListRequestController = createRequestController();
|
||||
const referralRequestController = createRequestController();
|
||||
let pageActive = true;
|
||||
|
||||
const loadReferralProfile = async () => {
|
||||
referralRequestController.abort();
|
||||
referralState.value = "loading";
|
||||
referralError.value = "";
|
||||
try {
|
||||
const profile = await referralApi.getMyReferralProfile({
|
||||
requestController: referralRequestController,
|
||||
});
|
||||
if (!pageActive) return;
|
||||
referralProfile.value = profile;
|
||||
referralState.value = "ready";
|
||||
} catch (error) {
|
||||
if (!pageActive || isRequestCancelled(error)) return;
|
||||
referralError.value = getRequestErrorMessage(error, "推荐码暂时无法读取,请稍后重试。");
|
||||
referralState.value = "error";
|
||||
}
|
||||
};
|
||||
|
||||
const loadPromotions = async () => {
|
||||
promotionListRequestController.abort();
|
||||
promotionListState.value = "loading";
|
||||
@@ -117,6 +172,39 @@ const copyPromotionLink = (item) => {
|
||||
fail: () => { operationFeedback.value = "复制失败,请稍后再试。"; },
|
||||
});
|
||||
};
|
||||
const copyText = (content, successMessage) => {
|
||||
if (!content || typeof uni?.setClipboardData !== "function") {
|
||||
operationFeedback.value = "当前设备暂时不能复制。";
|
||||
return;
|
||||
}
|
||||
uni.setClipboardData({
|
||||
data: content,
|
||||
success: () => { operationFeedback.value = successMessage; },
|
||||
fail: () => { operationFeedback.value = "复制失败,请稍后再试。"; },
|
||||
});
|
||||
};
|
||||
const copyReferralCode = () =>
|
||||
copyText(referralProfile.value.referralCode, "推荐码已复制,可以发给家人。");
|
||||
const copyReferralLink = () =>
|
||||
copyText(referralProfile.value.shareUrl, "推荐链接已复制,可以发给家人。");
|
||||
const shareReferral = () => {
|
||||
const profile = referralProfile.value;
|
||||
if (!profile.enabled || !profile.shareUrl) return;
|
||||
const content = [profile.shareTitle, profile.shareDescription, profile.shareUrl]
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
// #ifdef APP-PLUS
|
||||
if (typeof plus?.share?.sendWithSystem === "function") {
|
||||
plus.share.sendWithSystem(
|
||||
{ type: "text", content },
|
||||
() => { operationFeedback.value = "已打开系统分享。"; },
|
||||
() => { operationFeedback.value = "已取消分享。"; },
|
||||
);
|
||||
return;
|
||||
}
|
||||
// #endif
|
||||
copyReferralLink();
|
||||
};
|
||||
const sharePromotion = (item) => {
|
||||
if (!item?.targetUrl) return;
|
||||
const content = [item.title, item.description, item.targetUrl].filter(Boolean).join("\n");
|
||||
@@ -142,11 +230,15 @@ const sharePromotion = (item) => {
|
||||
};
|
||||
|
||||
const requestBack = () => goBack();
|
||||
onLoad(loadPromotions);
|
||||
onLoad(() => {
|
||||
void loadReferralProfile();
|
||||
void loadPromotions();
|
||||
});
|
||||
onBackPress((event) => handleBackPress(event, requestBack));
|
||||
onUnload(() => {
|
||||
pageActive = false;
|
||||
promotionListRequestController.abort();
|
||||
referralRequestController.abort();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -163,9 +255,10 @@ onUnload(() => {
|
||||
}
|
||||
.page-content {
|
||||
flex: 1;
|
||||
padding: 28rpx 30rpx 72rpx;
|
||||
padding: 28rpx 30rpx calc(72rpx + env(safe-area-inset-bottom));
|
||||
}
|
||||
.promotion-hero,
|
||||
.referral-card,
|
||||
.promotion-state-card,
|
||||
.promotion-list {
|
||||
@include adaptive-profile-content;
|
||||
@@ -211,8 +304,25 @@ onUnload(() => {
|
||||
padding: 38rpx;
|
||||
text-align: center;
|
||||
}
|
||||
.referral-card {
|
||||
margin-top: 20rpx;
|
||||
padding: 28rpx;
|
||||
border: 1rpx solid rgba(128, 89, 49, 0.26);
|
||||
border-radius: 12rpx;
|
||||
background: rgba(255, 252, 245, 0.82);
|
||||
}
|
||||
.referral-card text { display: block; }
|
||||
.referral-card__heading text:first-child { color: $ink; font-size: clamp(17px, 29rpx, 21px); font-weight: 700; }
|
||||
.referral-card__heading text:last-child,
|
||||
.referral-card__content > text:last-of-type,
|
||||
.referral-card__state > text { margin-top: 8rpx; color: $ink-muted; font-size: clamp(13px, 22rpx, 16px); line-height: 1.55; }
|
||||
.referral-card__content,
|
||||
.referral-card__state { margin-top: 22rpx; }
|
||||
.referral-card__code { color: #9e251b; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: clamp(22px, 40rpx, 30px); font-weight: 700; letter-spacing: 3rpx; }
|
||||
.referral-card__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; margin-top: 22rpx; gap: 12rpx; }
|
||||
.referral-card__actions .app-button { width: auto; min-width: 150rpx; }
|
||||
.promotion-state-card .app-button { margin-top: 28rpx; }
|
||||
.promotion-list { display: grid; gap: 16rpx; }
|
||||
.promotion-list { display: grid; gap: 18rpx; margin-top: 20rpx; }
|
||||
.promotion-card {
|
||||
overflow: hidden;
|
||||
padding: 26rpx 28rpx;
|
||||
@@ -230,7 +340,7 @@ onUnload(() => {
|
||||
.promotion-card text { display: block; }
|
||||
.promotion-card__title { color: $ink; font-size: clamp(16px, 28rpx, 20px); font-weight: 700; }
|
||||
.promotion-card__description { margin-top: 10rpx; color: $ink-muted; font-size: clamp(14px, 23rpx, 17px); line-height: 1.6; }
|
||||
.promotion-feedback { display: block; padding: 16rpx 20rpx; border: 1rpx solid rgba(66, 107, 88, .32); border-radius: 10rpx; background: rgba(66, 107, 88, .08); color: #426b58; font-size: clamp(14px, 22rpx, 17px); }
|
||||
.promotion-feedback { display: block; margin-top: 16rpx; padding: 16rpx 20rpx; border: 1rpx solid rgba(66, 107, 88, .32); border-radius: 10rpx; background: rgba(66, 107, 88, .08); color: #426b58; font-size: clamp(14px, 22rpx, 17px); line-height: 1.5; overflow-wrap: anywhere; }
|
||||
.promotion-card__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; margin-top: 16rpx; gap: 12rpx; }
|
||||
.promotion-card__actions .app-button { width: auto; min-width: 142rpx; }
|
||||
@media (max-width: 340px) {
|
||||
@@ -238,5 +348,10 @@ onUnload(() => {
|
||||
padding-right: 22rpx;
|
||||
padding-left: 22rpx;
|
||||
}
|
||||
|
||||
.promotion-card__actions .app-button {
|
||||
min-width: 0;
|
||||
flex: 1 1 220rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user