Files
jiapuapp/pages/profile/m08-promotion.vue
T
2026-07-24 18:03:14 +08:00

72 lines
4.7 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>
<view class="promotion-page">
<ModulePageBackground module="profile" />
<view class="page-layer"><PageHeader title="推广中心" custom-back @back="requestBack" /></view>
<view class="page-content page-layer">
<view class="invite-hero">
<image src="/static/assets/foundation/transparent/brand-seal.png" mode="aspectFit" />
<text>家谱服务推荐</text>
<text>以下内容由服务端配置不生成邀请码也不会伪造推广信息</text>
</view>
<view v-if="state === 'loading'" class="state-card"><AppLoading text="正在读取推广内容" /></view>
<view v-else-if="state === 'error'" class="state-card">
<text>暂时无法读取推广内容</text><AppButton block type="secondary" label="重新加载" @click="loadPromotions" />
</view>
<view v-else-if="state === 'empty'" class="state-card"><text>当前没有可展示的推广内容</text></view>
<view v-else class="promotion-list">
<view v-for="item in promotions" :key="item.id" class="promotion-card">
<text class="promotion-title">{{ item.title }}</text>
<text v-if="item.platform" class="promotion-platform">{{ item.platform }}</text>
<text v-if="item.description" class="promotion-copy">{{ item.description }}</text>
</view>
</view>
</view>
</view>
</template>
<script setup>
import { ref } from "vue";
import { onBackPress, onLoad, onShow, onUnload } from "@dcloudio/uni-app";
import AppButton from "@/components/AppButton.vue";
import AppLoading from "@/components/AppLoading.vue";
import ModulePageBackground from "@/components/ModulePageBackground.vue";
import PageHeader from "@/components/PageHeader.vue";
import { appApi, createRequestController, isRequestCancelled } from "@/utils/api.js";
import { handleBackPress, runBackGuard } from "@/utils/navigation.js";
const promotions = ref([]);
const state = ref("loading");
const controller = createRequestController();
let active = true;
const platformLabels = Object.freeze({ app: "APP 应用", pc: "管理后台", wechat: "微信小程序" });
const formatPlatform = (value) => platformLabels[String(value || "").trim().toLowerCase()] || String(value || "").trim();
const loadPromotions = async () => {
controller.abort();
state.value = "loading";
try {
const rows = await appApi.getPromotions({ requestController: controller });
if (!active) return;
promotions.value = rows.map((item) => ({
id: String(item.promotionId),
title: String(item.promotionTitle || "未命名推广"),
description: String(item.promotionDesc || ""),
platform: formatPlatform(item.platform)
}));
state.value = promotions.value.length ? "ready" : "empty";
} catch (error) {
if (!active || isRequestCancelled(error)) return;
state.value = "error";
}
};
const requestBack = () => runBackGuard({});
onLoad(loadPromotions);
onShow(() => { if (state.value !== "loading") loadPromotions(); });
onUnload(() => { active = false; controller.abort(); });
onBackPress((event) => handleBackPress(event, requestBack));
</script>
<style scoped lang="scss">
@use "../../styles/adaptive-frame-profiles.scss" as adaptive;
.promotion-page{display:flex;min-height:100vh;flex-direction:column;background:$paper}.page-layer{z-index:1}.page-content{flex:1;padding:28rpx 30rpx 72rpx}.invite-hero,.state-card,.promotion-card{@include adaptive.adaptive-profile-content}.invite-hero{display:flex;min-height:250rpx;flex-direction:column;align-items:center;justify-content:center;padding:38rpx 48rpx;text-align:center}.invite-hero image{width:90rpx;height:102rpx}.invite-hero text{display:block}.invite-hero text:nth-child(2){margin-top:12rpx;color:$ink;font-family:STKaiti,KaiTi,serif;font-size:34rpx;font-weight:700}.invite-hero text:last-child{margin-top:12rpx;color:$ink-muted;font-size:22rpx;line-height:1.55}.state-card{display:flex;min-height:210rpx;flex-direction:column;align-items:center;justify-content:center;margin-top:20rpx;padding:38rpx;text-align:center;color:$ink;font-size:26rpx}.state-card .app-button{width:100%;margin-top:22rpx}.promotion-list{display:flex;flex-direction:column;gap:16rpx;margin-top:20rpx}.promotion-card{position:relative;display:flex;min-height:142rpx;flex-direction:column;padding:30rpx 34rpx}.promotion-title{color:$ink;font-size:28rpx;font-weight:700;overflow-wrap:anywhere}.promotion-platform{align-self:flex-start;margin-top:10rpx;padding:3rpx 12rpx;border:1rpx solid rgba(181,46,34,.26);border-radius:999rpx;color:$brand-red;font-size:20rpx;line-height:1.3}.promotion-copy{margin-top:12rpx;color:$ink-muted;font-size:22rpx;line-height:1.55;overflow-wrap:anywhere}@media(max-width:340px){.page-content{padding-right:22rpx;padding-left:22rpx}}
</style>