213 lines
5.3 KiB
Vue
213 lines
5.3 KiB
Vue
<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">
|
|
@import "../../styles/adaptive-frame-profiles.scss";
|
|
.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-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: clamp(19px, 34rpx, 24px);
|
|
font-weight: 700;
|
|
}
|
|
.invite-hero text:last-child {
|
|
margin-top: 12rpx;
|
|
color: $ink-muted;
|
|
font-size: clamp(14px, 22rpx, 17px);
|
|
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: clamp(16px, 26rpx, 20px);
|
|
}
|
|
.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: clamp(16px, 28rpx, 20px);
|
|
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, 0.26);
|
|
border-radius: 999rpx;
|
|
color: $brand-red;
|
|
font-size: clamp(13px, 20rpx, 16px);
|
|
line-height: 1.3;
|
|
}
|
|
.promotion-copy {
|
|
margin-top: 12rpx;
|
|
color: $ink-muted;
|
|
font-size: clamp(14px, 22rpx, 17px);
|
|
line-height: 1.55;
|
|
overflow-wrap: anywhere;
|
|
}
|
|
@media (max-width: 340px) {
|
|
.page-content {
|
|
padding-right: 22rpx;
|
|
padding-left: 22rpx;
|
|
}
|
|
}
|
|
</style>
|