713 lines
19 KiB
Vue
713 lines
19 KiB
Vue
<template>
|
||
<view class="page-container">
|
||
<view class="nav-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
||
<view class="nav-content">
|
||
<view class="nav-left" @tap="goBack">
|
||
<text class="back-icon"></text>
|
||
</view>
|
||
<text class="nav-title">PK详情</text>
|
||
<view class="nav-right" @tap="goToPkStation">
|
||
<text class="nav-right-text">PK站</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view :style="{ height: statusBarHeight + 44 + 'px' }"></view>
|
||
|
||
<view class="content-wrap">
|
||
<view v-if="detailData" class="hero-card">
|
||
<view class="hero-badge">PK Detail</view>
|
||
<text class="hero-title">{{ detailData.topicTitle }}</text>
|
||
<text class="hero-desc">{{ insightText }}</text>
|
||
|
||
<view class="summary-row">
|
||
<view class="summary-pill" :class="selectedTone ? `summary-pill--${selectedTone}` : ''">
|
||
<text class="summary-pill-label">我的选择</text>
|
||
<text class="summary-pill-value">{{ selectedItemLabel }}</text>
|
||
</view>
|
||
<view class="summary-meta">
|
||
<text class="summary-meta-item">{{ detailData.voteCount }} 人参与</text>
|
||
<text class="summary-dot"></text>
|
||
<text class="summary-meta-item">{{ detailData.votedAtText }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-if="detailData" class="battle-card">
|
||
<view class="battle-stage">
|
||
<view class="fighter-card left" :class="{ selected: detailData.selfChooseSide === 'left' }">
|
||
<view class="fighter-badge" v-if="detailData.selfChooseSide === 'left'">我支持</view>
|
||
<image class="fighter-avatar" :src="detailData.left.avatar" mode="aspectFill" />
|
||
<text class="fighter-name">{{ detailData.left.name }}</text>
|
||
<text class="fighter-votes">{{ detailData.leftVoteCount }} 票</text>
|
||
</view>
|
||
|
||
<view class="battle-middle">
|
||
<view class="vs-badge">VS</view>
|
||
<text class="battle-middle-text">{{ leadText }}</text>
|
||
</view>
|
||
|
||
<view class="fighter-card right" :class="{ selected: detailData.selfChooseSide === 'right' }">
|
||
<view class="fighter-badge fighter-badge--right" v-if="detailData.selfChooseSide === 'right'">我支持</view>
|
||
<image class="fighter-avatar" :src="detailData.right.avatar" mode="aspectFill" />
|
||
<text class="fighter-name">{{ detailData.right.name }}</text>
|
||
<text class="fighter-votes">{{ detailData.rightVoteCount }} 票</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="support-bar">
|
||
<view class="support-fill left-fill" :style="{ width: `${detailData.leftPercent}%` }"></view>
|
||
<view class="support-fill right-fill" :style="{ width: `${detailData.rightPercent}%` }"></view>
|
||
</view>
|
||
|
||
<view class="support-row">
|
||
<text class="support-text left-text">{{ detailData.leftPercent }}% 支持 {{ detailData.left.name }}</text>
|
||
<text class="support-text right-text">{{ detailData.rightPercent }}% 支持 {{ detailData.right.name }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view v-else class="state-card">
|
||
<text class="state-title">暂时无法查看这场 PK</text>
|
||
<text class="state-desc">这场对决的信息不存在或已失效,请返回列表重新进入。</text>
|
||
<button class="state-btn" @tap="goBack">返回上一页</button>
|
||
</view>
|
||
|
||
<CommentSection
|
||
v-if="detailData"
|
||
ref="commentSectionRef"
|
||
variant="pk"
|
||
:request-key="commentRequestKey"
|
||
:visible="!!commentRequestKey"
|
||
:allow-compose="true"
|
||
compose-title="说说你的站队理由"
|
||
:compose-desc="`支持 ${selectedItemLabel} 的观点,会更容易引发讨论`"
|
||
compose-placeholder="写下你的 PK 观点..."
|
||
page-name="pk_detail"
|
||
pill-label="我站"
|
||
:pill-value="selectedItemLabel"
|
||
:pill-tone="selectedTone"
|
||
loading-desc="看看大家如何评价这场对决"
|
||
empty-title="还没有评论"
|
||
empty-desc="来写下你的判断,成为这场 PK 的第一位评论官"
|
||
footer-more-text="查看更多评论"
|
||
:list-handler="fetchPkCommentList"
|
||
:replies-handler="fetchPkCommentReplies"
|
||
:publish-handler="publishPkComment"
|
||
:normalize-comment="normalizePkComment"
|
||
:like-handler="likeComment"
|
||
@need-login="openLoginPopup('请先登录后再参与评论')"
|
||
/>
|
||
</view>
|
||
|
||
<LoginPopup ref="loginPopupRef" @logind="handleLoginSuccess" />
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { computed, ref } from "vue";
|
||
import { onLoad, onPullDownRefresh, onReachBottom } from "@dcloudio/uni-app";
|
||
import { getStatusBarHeight } from "@/utils/system";
|
||
import { fetchCommentList, fetchCommentReplyList, publishComment, likeComment } from "@/api/pk";
|
||
import { FILE_BASE_URL } from "@/utils/constants";
|
||
import { formatDate } from "@/utils/date";
|
||
import CommentSection from "@/components/CommentSection/CommentSection.vue";
|
||
import LoginPopup from "@/components/LoginPopup/LoginPopup.vue";
|
||
import { useUserStore } from "@/stores/user";
|
||
|
||
const DETAIL_CACHE_PREFIX = "pk_detail_cache_";
|
||
|
||
const userStore = useUserStore();
|
||
const statusBarHeight = ref(getStatusBarHeight() || 44);
|
||
const loginPopupRef = ref(null);
|
||
const commentSectionRef = ref(null);
|
||
const detailData = ref(null);
|
||
const pkId = ref("");
|
||
|
||
const isLoggedIn = computed(() => !!userStore.token || !!userStore.userInfo?.nickName);
|
||
const selectedTone = computed(() => {
|
||
if (!detailData.value?.selfChooseSide) return "";
|
||
return detailData.value.selfChooseSide === "left" ? "left" : "right";
|
||
});
|
||
const selectedItemLabel = computed(() => {
|
||
if (!detailData.value) return "";
|
||
return detailData.value.selfChooseName || "未记录";
|
||
});
|
||
const commentRequestKey = computed(() => {
|
||
if (!detailData.value?.id || !detailData.value?.selfChooseItemId) return "";
|
||
return `${detailData.value.id}_${detailData.value.selfChooseItemId}`;
|
||
});
|
||
const leadText = computed(() => {
|
||
if (!detailData.value) return "";
|
||
const leftPercent = Number(detailData.value.leftPercent || 0);
|
||
const rightPercent = Number(detailData.value.rightPercent || 0);
|
||
const leader = leftPercent >= rightPercent ? detailData.value.left.name : detailData.value.right.name;
|
||
const leaderPercent = leftPercent >= rightPercent ? leftPercent : rightPercent;
|
||
return `${leader} 当前 ${leaderPercent}% 领先`;
|
||
});
|
||
const insightText = computed(() => {
|
||
if (!detailData.value) return "这是一场你参与过的 PK 对决";
|
||
const myChoice = detailData.value.selfChooseName || "你的选择";
|
||
const myLeading =
|
||
(detailData.value.selfChooseSide === "left" && detailData.value.leftPercent >= detailData.value.rightPercent) ||
|
||
(detailData.value.selfChooseSide === "right" && detailData.value.rightPercent >= detailData.value.leftPercent);
|
||
return myLeading
|
||
? `你支持的 ${myChoice} 当前占优,来看看评论区大家的判断是否和你一致。`
|
||
: `你支持的 ${myChoice} 暂时落后,看看评论区里有没有和你同样坚定的声音。`;
|
||
});
|
||
|
||
const buildAvatarUrl = (avatar) => {
|
||
if (!avatar) return "https://api.dicebear.com/7.x/avataaars/svg?seed=fallback";
|
||
const normalizedAvatar = String(avatar).trim();
|
||
return normalizedAvatar.startsWith("http") ? normalizedAvatar : `${FILE_BASE_URL}${normalizedAvatar}`;
|
||
};
|
||
|
||
const clampPercent = (value) => {
|
||
const number = Number(value);
|
||
if (!Number.isFinite(number)) return 0;
|
||
return Math.max(0, Math.min(100, Math.round(number)));
|
||
};
|
||
|
||
const normalizePercentPair = (item = {}) => {
|
||
let leftPercent = clampPercent(item.leftPercent);
|
||
let rightPercent = clampPercent(item.rightPercent);
|
||
|
||
if (!leftPercent && !rightPercent) {
|
||
const leftVotes = Number(item.leftVoteCount || 0);
|
||
const rightVotes = Number(item.rightVoteCount || 0);
|
||
const totalVotes = leftVotes + rightVotes;
|
||
if (totalVotes > 0) {
|
||
leftPercent = Math.round((leftVotes / totalVotes) * 100);
|
||
rightPercent = 100 - leftPercent;
|
||
} else {
|
||
leftPercent = 50;
|
||
rightPercent = 50;
|
||
}
|
||
} else if (!leftPercent) {
|
||
leftPercent = 100 - rightPercent;
|
||
} else if (!rightPercent) {
|
||
rightPercent = 100 - leftPercent;
|
||
}
|
||
|
||
const total = leftPercent + rightPercent;
|
||
if (total && total !== 100) {
|
||
leftPercent = Math.round((leftPercent / total) * 100);
|
||
rightPercent = 100 - leftPercent;
|
||
}
|
||
|
||
return { leftPercent, rightPercent };
|
||
};
|
||
|
||
const normalizeDetail = (item = {}) => {
|
||
const { leftPercent, rightPercent } = normalizePercentPair(item);
|
||
const selfChooseSide =
|
||
item.selfChooseSide ||
|
||
(item.selfChooseItemId && item.selfChooseItemId === item.leftItemId
|
||
? "left"
|
||
: item.selfChooseItemId && item.selfChooseItemId === item.rightItemId
|
||
? "right"
|
||
: "");
|
||
const selfChooseName =
|
||
selfChooseSide === "right"
|
||
? (item.rightItemName || "右侧对象")
|
||
: selfChooseSide === "left"
|
||
? (item.leftItemName || "左侧对象")
|
||
: "未记录";
|
||
|
||
return {
|
||
id: item.id || "",
|
||
topicId: item.topicId || "",
|
||
topicTitle: item.topicTitle || "未知话题",
|
||
voteCount: Number(item.voteCount || 0),
|
||
leftVoteCount: Number(item.leftVoteCount || 0),
|
||
rightVoteCount: Number(item.rightVoteCount || 0),
|
||
selfChooseItemId: item.selfChooseItemId || "",
|
||
selfChooseSide,
|
||
selfChooseName,
|
||
leftPercent,
|
||
rightPercent,
|
||
votedAt: item.votedAt || "",
|
||
votedAtText: formatDate(item.votedAt, "YYYY-MM-DD HH:mm") || "刚刚参与",
|
||
left: {
|
||
id: item.leftItemId || "",
|
||
name: item.leftItemName || "左侧对象",
|
||
avatar: buildAvatarUrl(item.leftItemAvatar),
|
||
},
|
||
right: {
|
||
id: item.rightItemId || "",
|
||
name: item.rightItemName || "右侧对象",
|
||
avatar: buildAvatarUrl(item.rightItemAvatar),
|
||
},
|
||
};
|
||
};
|
||
|
||
const resolveChoiceMeta = (chooseItemId) => {
|
||
if (!chooseItemId || !detailData.value) {
|
||
return { choiceLabel: "", choiceTone: "" };
|
||
}
|
||
if (chooseItemId === detailData.value.left.id) {
|
||
return { choiceLabel: `支持 ${detailData.value.left.name}`, choiceTone: "left" };
|
||
}
|
||
if (chooseItemId === detailData.value.right.id) {
|
||
return { choiceLabel: `支持 ${detailData.value.right.name}`, choiceTone: "right" };
|
||
}
|
||
return { choiceLabel: "已站队", choiceTone: "" };
|
||
};
|
||
|
||
const normalizePkComment = (item = {}, rootComment = null) => {
|
||
const user = item.user || {};
|
||
const replyToUser = item.replyToUser || {};
|
||
const choiceMeta = resolveChoiceMeta(item.chooseItemId);
|
||
|
||
return {
|
||
id: item.id,
|
||
rootCommentId: item.rootCommentId || rootComment?.id || null,
|
||
parentCommentId: item.parentCommentId || rootComment?.id || null,
|
||
replyToCommentId: item.replyToCommentId || null,
|
||
name: user.nickname || "匿名用户",
|
||
avatar: buildAvatarUrl(user.avatar),
|
||
time: formatDate(item.createdAt) || "刚刚",
|
||
content: item.content || "",
|
||
likes: Number(item.likeCount || 0),
|
||
isLiked: !!item.isLiked,
|
||
userAction: "",
|
||
replyCount: Number(item.replyCount || 0),
|
||
replyToName: replyToUser.nickname || "",
|
||
choiceLabel: choiceMeta.choiceLabel,
|
||
choiceTone: choiceMeta.choiceTone,
|
||
chooseItemId: item.chooseItemId || "",
|
||
repliesLoading: false,
|
||
repliesLoaded: false,
|
||
repliesHasMore: false,
|
||
repliesPage: 1,
|
||
replies: [],
|
||
};
|
||
};
|
||
|
||
const openLoginPopup = (message = "请先登录后再操作") => {
|
||
loginPopupRef.value?.open();
|
||
if (message) {
|
||
uni.showToast({ title: message, icon: "none" });
|
||
}
|
||
};
|
||
|
||
const goBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
|
||
const goToPkStation = () => {
|
||
uni.switchTab({
|
||
url: "/pages/pk/index",
|
||
});
|
||
};
|
||
|
||
const fetchPkCommentList = ({ page, orderBy }) => {
|
||
return fetchCommentList({
|
||
pkId: pkId.value,
|
||
page,
|
||
orderBy,
|
||
});
|
||
};
|
||
|
||
const fetchPkCommentReplies = ({ rootCommentId, page }) => {
|
||
return fetchCommentReplyList({
|
||
pkId: pkId.value,
|
||
rootCommentId,
|
||
page,
|
||
});
|
||
};
|
||
|
||
const publishPkComment = ({ content, replyTarget, mode }) => {
|
||
return publishComment({
|
||
pkId: pkId.value,
|
||
chooseItemId: detailData.value?.selfChooseItemId,
|
||
content,
|
||
parentCommentId: mode === "reply" ? replyTarget?.parentCommentId : undefined,
|
||
replyToCommentId: mode === "reply" ? replyTarget?.replyToCommentId : undefined,
|
||
});
|
||
};
|
||
|
||
const hydrateDetail = (options = {}) => {
|
||
const cacheKey = options.cacheKey || `${DETAIL_CACHE_PREFIX}${options.pkId || ""}`;
|
||
const cached = cacheKey ? uni.getStorageSync(cacheKey) : null;
|
||
if (cached && typeof cached === "object") {
|
||
detailData.value = normalizeDetail(cached);
|
||
pkId.value = detailData.value.id;
|
||
return;
|
||
}
|
||
|
||
if (options.payload) {
|
||
try {
|
||
const parsed = JSON.parse(decodeURIComponent(options.payload));
|
||
detailData.value = normalizeDetail(parsed);
|
||
pkId.value = detailData.value.id;
|
||
} catch (error) {
|
||
detailData.value = null;
|
||
}
|
||
}
|
||
};
|
||
|
||
const handleLoginSuccess = async () => {
|
||
await commentSectionRef.value?.refreshComments?.();
|
||
};
|
||
|
||
onLoad((options) => {
|
||
pkId.value = options.pkId || "";
|
||
hydrateDetail(options);
|
||
});
|
||
|
||
onPullDownRefresh(async () => {
|
||
await commentSectionRef.value?.refreshComments?.();
|
||
uni.stopPullDownRefresh();
|
||
});
|
||
|
||
onReachBottom(() => {
|
||
commentSectionRef.value?.loadMoreComments?.();
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.page-container {
|
||
min-height: 100vh;
|
||
background:
|
||
radial-gradient(circle at top, rgba(111, 90, 255, 0.14), transparent 32%),
|
||
linear-gradient(180deg, #f7f8ff 0%, #f7f8fc 44%, #ffffff 100%);
|
||
}
|
||
|
||
.nav-bar {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 100;
|
||
background: rgba(247, 248, 255, 0.88);
|
||
backdrop-filter: blur(24rpx);
|
||
}
|
||
|
||
.nav-content {
|
||
height: 44px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 28rpx 0 24rpx;
|
||
}
|
||
|
||
.nav-left,
|
||
.nav-right {
|
||
min-width: 84rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.nav-right {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.back-icon {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23202433' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='19' y1='12' x2='5' y2='12'%3E%3C/line%3E%3Cpolyline points='12 19 5 12 12 5'%3E%3C/polyline%3E%3C/svg%3E");
|
||
background-size: cover;
|
||
}
|
||
|
||
.nav-title {
|
||
font-size: 34rpx;
|
||
font-weight: 800;
|
||
color: #202433;
|
||
}
|
||
|
||
.nav-right-text {
|
||
font-size: 24rpx;
|
||
color: #5c43f5;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.content-wrap {
|
||
padding: 24rpx 32rpx 40rpx;
|
||
}
|
||
|
||
.hero-card,
|
||
.battle-card,
|
||
.state-card {
|
||
background: rgba(255, 255, 255, 0.94);
|
||
border: 2rpx solid rgba(229, 232, 246, 0.92);
|
||
border-radius: 32rpx;
|
||
box-shadow: 0 18rpx 42rpx rgba(87, 92, 145, 0.08);
|
||
}
|
||
|
||
.hero-card {
|
||
padding: 32rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.hero-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
height: 44rpx;
|
||
padding: 0 18rpx;
|
||
border-radius: 999rpx;
|
||
background: rgba(92, 67, 245, 0.1);
|
||
color: #5c43f5;
|
||
font-size: 22rpx;
|
||
font-weight: 700;
|
||
margin-bottom: 18rpx;
|
||
}
|
||
|
||
.hero-title {
|
||
display: block;
|
||
font-size: 40rpx;
|
||
font-weight: 800;
|
||
color: #1f2432;
|
||
margin-bottom: 12rpx;
|
||
line-height: 1.4;
|
||
}
|
||
|
||
.hero-desc {
|
||
display: block;
|
||
font-size: 25rpx;
|
||
color: #7b8191;
|
||
line-height: 1.7;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.summary-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.summary-pill {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 8rpx;
|
||
padding: 12rpx 18rpx;
|
||
border-radius: 999rpx;
|
||
background: rgba(92, 67, 245, 0.1);
|
||
}
|
||
|
||
.summary-pill--right {
|
||
background: rgba(255, 141, 95, 0.12);
|
||
}
|
||
|
||
.summary-pill-label {
|
||
font-size: 22rpx;
|
||
color: #8a90a0;
|
||
}
|
||
|
||
.summary-pill-value {
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
color: #4d44f1;
|
||
}
|
||
|
||
.summary-pill--right .summary-pill-value {
|
||
color: #ff7a45;
|
||
}
|
||
|
||
.summary-meta {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12rpx;
|
||
flex-wrap: wrap;
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.summary-meta-item {
|
||
font-size: 22rpx;
|
||
color: #8a90a0;
|
||
}
|
||
|
||
.summary-dot {
|
||
width: 8rpx;
|
||
height: 8rpx;
|
||
border-radius: 50%;
|
||
background: #d1d5db;
|
||
}
|
||
|
||
.battle-card {
|
||
padding: 28rpx;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.battle-stage {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 12rpx;
|
||
margin-bottom: 22rpx;
|
||
}
|
||
|
||
.fighter-card {
|
||
position: relative;
|
||
width: 224rpx;
|
||
min-height: 276rpx;
|
||
border-radius: 28rpx;
|
||
padding: 30rpx 18rpx 24rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: linear-gradient(180deg, #fafbff 0%, #ffffff 100%);
|
||
border: 2rpx solid transparent;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.fighter-card.left {
|
||
border-color: rgba(92, 67, 245, 0.18);
|
||
}
|
||
|
||
.fighter-card.right {
|
||
border-color: rgba(255, 153, 102, 0.2);
|
||
}
|
||
|
||
.fighter-card.selected {
|
||
transform: translateY(-4rpx);
|
||
box-shadow: 0 14rpx 28rpx rgba(92, 67, 245, 0.12);
|
||
}
|
||
|
||
.fighter-badge {
|
||
position: absolute;
|
||
top: 16rpx;
|
||
left: 16rpx;
|
||
padding: 8rpx 14rpx;
|
||
border-radius: 999rpx;
|
||
background: rgba(92, 67, 245, 0.12);
|
||
color: #5c43f5;
|
||
font-size: 20rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.fighter-badge--right {
|
||
background: rgba(255, 141, 95, 0.14);
|
||
color: #ff7a45;
|
||
}
|
||
|
||
.fighter-avatar {
|
||
width: 120rpx;
|
||
height: 120rpx;
|
||
border-radius: 50%;
|
||
border: 6rpx solid #fff;
|
||
box-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
margin-bottom: 18rpx;
|
||
}
|
||
|
||
.fighter-name {
|
||
font-size: 30rpx;
|
||
color: #1f2432;
|
||
font-weight: 800;
|
||
text-align: center;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.fighter-votes {
|
||
font-size: 22rpx;
|
||
color: #858b9b;
|
||
}
|
||
|
||
.battle-middle {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.vs-badge {
|
||
width: 90rpx;
|
||
height: 90rpx;
|
||
border-radius: 50%;
|
||
background: linear-gradient(135deg, #6a5bff, #4d44f1);
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 28rpx;
|
||
font-weight: 900;
|
||
box-shadow: 0 16rpx 30rpx rgba(92, 67, 245, 0.24);
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.battle-middle-text {
|
||
font-size: 22rpx;
|
||
color: #8a90a0;
|
||
text-align: center;
|
||
}
|
||
|
||
.support-bar {
|
||
height: 18rpx;
|
||
border-radius: 999rpx;
|
||
overflow: hidden;
|
||
display: flex;
|
||
background: #eceff8;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.support-fill {
|
||
height: 100%;
|
||
}
|
||
|
||
.left-fill {
|
||
background: linear-gradient(90deg, #6557ff, #4d44f1);
|
||
}
|
||
|
||
.right-fill {
|
||
background: linear-gradient(90deg, #ffb15f, #ff8d5f);
|
||
}
|
||
|
||
.support-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.support-text {
|
||
font-size: 24rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.left-text {
|
||
color: #5c43f5;
|
||
}
|
||
|
||
.right-text {
|
||
color: #ff8d5f;
|
||
text-align: right;
|
||
}
|
||
|
||
.state-card {
|
||
padding: 64rpx 40rpx;
|
||
text-align: center;
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.state-title {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
color: #232734;
|
||
font-weight: 800;
|
||
margin-bottom: 14rpx;
|
||
}
|
||
|
||
.state-desc {
|
||
display: block;
|
||
font-size: 25rpx;
|
||
color: #8a90a0;
|
||
line-height: 1.7;
|
||
margin-bottom: 28rpx;
|
||
}
|
||
|
||
.state-btn {
|
||
width: 240rpx;
|
||
height: 84rpx;
|
||
line-height: 84rpx;
|
||
border-radius: 999rpx;
|
||
background: #111827;
|
||
color: #fff;
|
||
font-size: 28rpx;
|
||
font-weight: 700;
|
||
}
|
||
|
||
.state-btn::after {
|
||
border: none;
|
||
}
|
||
</style>
|