828 lines
24 KiB
Vue
828 lines
24 KiB
Vue
<template>
|
|
<view class="index">
|
|
<view class="top-bar" :style="{ paddingTop: statusBarHeight + 'px' }">
|
|
<view class="top-bar__content">
|
|
<view class="top-bar__logo">
|
|
<image src="/static/images/logo.png" mode="aspectFit" class="top-bar__logo-img" />
|
|
</view>
|
|
<view class="top-bar__search" @tap="goSearch">
|
|
<UIcon name="search" size="30" color="#8C95A5" />
|
|
<text class="top-bar__search-text">搜索赛事、球队、球员</text>
|
|
</view>
|
|
<AiAssistantEntry size="compact" variant="orb" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="channel-bar">
|
|
<scroll-view scroll-x class="channel-bar__scroll" show-scrollbar="false">
|
|
<view class="channel-bar__list">
|
|
<view v-for="item in primaryChannels" :key="item.channelKey" class="channel-bar__item"
|
|
:class="{ 'channel-bar__item--active': currentChannel === item.index }" @tap="switchChannel(item.index)">
|
|
<text>{{ item.name }}</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
<view class="channel-bar__more" @tap="goChannelManage">
|
|
<text>更多</text>
|
|
<UIcon name="arrow-down" size="22" color="#667085" />
|
|
</view>
|
|
</view>
|
|
|
|
<scroll-view scroll-y class="feed-wrapper" @scrolltolower="loadMore" :lower-threshold="100">
|
|
<view class="home-content">
|
|
<view v-if="focusMatches.length" class="focus-section">
|
|
<view class="section-heading">
|
|
<text class="section-heading__title">今日焦点</text>
|
|
<view class="section-heading__action" @tap="goAllMatches">
|
|
<text>全部赛程</text>
|
|
<UIcon name="arrow-right" size="22" color="#1468F5" />
|
|
</view>
|
|
</view>
|
|
<view class="focus-section__list">
|
|
<view v-for="(match, index) in focusMatches" :key="match.id" class="focus-match"
|
|
@tap="goMatch(match.id)">
|
|
<view class="focus-match__head">
|
|
<text class="focus-match__league" :class="{ 'focus-match__league--warm': index % 2 }">
|
|
{{ match.league_name || '赛事' }}
|
|
</text>
|
|
<text class="focus-match__time">{{ formatFocusMeta(match) }}</text>
|
|
</view>
|
|
<view class="focus-match__teams">
|
|
<view class="focus-match__team">
|
|
<image v-if="match.home_icon" :src="match.home_icon" mode="aspectFit"
|
|
class="focus-match__badge" referrerpolicy="no-referrer" />
|
|
<view v-else class="focus-match__badge focus-match__badge--fallback">
|
|
<text>{{ getTeamInitial(match.home_team) }}</text>
|
|
</view>
|
|
<text class="focus-match__team-name">{{ match.home_team || '主队' }}</text>
|
|
</view>
|
|
<text class="focus-match__versus">VS</text>
|
|
<view class="focus-match__team">
|
|
<image v-if="match.away_icon" :src="match.away_icon" mode="aspectFit"
|
|
class="focus-match__badge" referrerpolicy="no-referrer" />
|
|
<view v-else class="focus-match__badge focus-match__badge--fallback">
|
|
<text>{{ getTeamInitial(match.away_team) }}</text>
|
|
</view>
|
|
<text class="focus-match__team-name">{{ match.away_team || '客队' }}</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<navigator v-if="featuredArticle" :url="`/pages/news_detail/news_detail?id=${featuredArticle.id}`"
|
|
class="featured-card">
|
|
<image v-if="featuredArticle.image && !featuredImageFailed" class="featured-card__image"
|
|
:src="featuredArticle.image" mode="aspectFill" referrerpolicy="no-referrer"
|
|
@error="featuredImageFailed = true" />
|
|
<view class="featured-card__shade" />
|
|
<view class="featured-card__content">
|
|
<text class="featured-card__tag">{{ getArticleCategoryName(featuredArticle) }}</text>
|
|
<view class="featured-card__copy">
|
|
<text class="featured-card__title">{{ featuredArticle.title }}</text>
|
|
<text v-if="getArticleSummary(featuredArticle)" class="featured-card__desc">
|
|
{{ getArticleSummary(featuredArticle) }}
|
|
</text>
|
|
</view>
|
|
<view class="featured-card__meta">
|
|
<text>{{ getArticleAuthor(featuredArticle) }}</text>
|
|
<text>{{ formatRelativeTime(featuredArticle.create_time) }}</text>
|
|
</view>
|
|
</view>
|
|
</navigator>
|
|
|
|
<view class="latest-section">
|
|
<view class="section-heading latest-section__heading">
|
|
<text class="section-heading__title">最新资讯</text>
|
|
<view class="latest-section__filter">
|
|
<UIcon name="filter" size="28" color="#7A8494" />
|
|
<text>筛选</text>
|
|
</view>
|
|
</view>
|
|
<view class="latest-section__list">
|
|
<FeedCard v-for="item in regularFeedList" :key="item.id" :item="item" />
|
|
</view>
|
|
</view>
|
|
|
|
<view v-if="loading" class="feed-loading">
|
|
<ULoading mode="circle" />
|
|
<text class="feed-loading__text">加载中...</text>
|
|
</view>
|
|
<view v-if="noMore && feedList.length" class="feed-bottom">
|
|
<text>没有更多了</text>
|
|
</view>
|
|
<view v-if="!loading && !feedList.length" class="feed-empty">
|
|
<text>暂无内容</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- #ifdef MP -->
|
|
<MpPrivacyPopup />
|
|
<!-- #endif -->
|
|
|
|
<GlobalPopup />
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
|
import { computed, ref } from 'vue'
|
|
import { getArticleList, getArticleCate } from '@/api/news'
|
|
import { getMatchList } from '@/api/match'
|
|
import { resolveShortLink } from '@/api/share'
|
|
import { getLocalMatchDate } from '@/utils/match-time'
|
|
import { useUserStore } from '@/stores/user'
|
|
import AiAssistantEntry from '@/components/ai-assistant-entry/ai-assistant-entry.vue'
|
|
import FeedCard from '@/components/feed-card/feed-card.vue'
|
|
import GlobalPopup from '@/components/global-popup/global-popup.vue'
|
|
import UIcon from '@/uni_modules/vk-uview-ui/components/u-icon/u-icon.vue'
|
|
import ULoading from '@/uni_modules/vk-uview-ui/components/u-loading/u-loading.vue'
|
|
|
|
const userStore = useUserStore()
|
|
// #ifdef MP
|
|
import MpPrivacyPopup from './component/mp-privacy-popup.vue'
|
|
// #endif
|
|
|
|
const statusBarHeight = ref(0)
|
|
uni.getSystemInfo({
|
|
success: (res) => {
|
|
// #ifdef APP-PLUS || MP
|
|
statusBarHeight.value = res.statusBarHeight || 44
|
|
// #endif
|
|
}
|
|
})
|
|
|
|
const RECOMMEND_CHANNEL = {
|
|
id: '__recommend__',
|
|
name: '推荐',
|
|
specialType: 'recommend',
|
|
channelKey: '__recommend__'
|
|
}
|
|
|
|
const MY_CHANNEL = {
|
|
id: '__mychannel__',
|
|
name: '我的频道',
|
|
specialType: 'mychannel',
|
|
channelKey: '__mychannel__'
|
|
}
|
|
|
|
const articleChannels = ref<any[]>([])
|
|
const allChannels = ref<any[]>([])
|
|
const currentChannel = ref(0)
|
|
const feedList = ref<any[]>([])
|
|
const focusMatches = ref<any[]>([])
|
|
const featuredImageFailed = ref(false)
|
|
const loading = ref(false)
|
|
const noMore = ref(false)
|
|
const pageNo = ref(1)
|
|
const pageSize = 10
|
|
|
|
const primaryChannels = computed(() => {
|
|
return allChannels.value
|
|
.map((item, index) => ({ ...item, index }))
|
|
.filter((item) => item.specialType !== 'mychannel')
|
|
.slice(0, 5)
|
|
})
|
|
|
|
const featuredArticle = computed(() => {
|
|
return feedList.value.find((item) => item.image) || feedList.value[0] || null
|
|
})
|
|
|
|
const regularFeedList = computed(() => {
|
|
const featuredId = featuredArticle.value?.id
|
|
return feedList.value.filter((item) => item.id !== featuredId)
|
|
})
|
|
|
|
const buildAllChannels = (channels: any[] = []) => {
|
|
const merged = [{ ...RECOMMEND_CHANNEL }]
|
|
if (userStore.isLogin) {
|
|
merged.push({ ...MY_CHANNEL })
|
|
}
|
|
const dynamicChannels = (channels || []).map((item: any) => ({
|
|
...item,
|
|
specialType: 'category',
|
|
channelKey: `cate_${item.id}`
|
|
}))
|
|
return [...merged, ...dynamicChannels]
|
|
}
|
|
|
|
const syncAllChannels = (channels: any[] = articleChannels.value) => {
|
|
const previousKey = allChannels.value[currentChannel.value]?.channelKey || RECOMMEND_CHANNEL.channelKey
|
|
allChannels.value = buildAllChannels(channels)
|
|
const nextIndex = allChannels.value.findIndex((item: any) => item.channelKey === previousKey)
|
|
currentChannel.value = nextIndex >= 0 ? nextIndex : 0
|
|
}
|
|
|
|
const findChannelIndexById = (id: any) => {
|
|
return allChannels.value.findIndex((item: any) => {
|
|
return item.specialType === 'category' && String(item.id) === String(id)
|
|
})
|
|
}
|
|
|
|
const loadChannels = async () => {
|
|
try {
|
|
articleChannels.value = (await getArticleCate()) || []
|
|
} catch (error) {
|
|
console.log('获取分类失败=>', error)
|
|
articleChannels.value = []
|
|
} finally {
|
|
syncAllChannels()
|
|
}
|
|
}
|
|
|
|
const loadFocusMatches = async () => {
|
|
try {
|
|
const data = await getMatchList()
|
|
const priorityMatches = [...(data?.live || []), ...(data?.upcoming || [])]
|
|
focusMatches.value = priorityMatches.slice(0, 2)
|
|
} catch (error) {
|
|
console.log('获取首页焦点赛程失败=>', error)
|
|
focusMatches.value = []
|
|
}
|
|
}
|
|
|
|
const switchChannel = (index: number) => {
|
|
if (currentChannel.value === index) return
|
|
currentChannel.value = index
|
|
reloadFeed()
|
|
}
|
|
|
|
const reloadFeed = () => {
|
|
pageNo.value = 1
|
|
noMore.value = false
|
|
feedList.value = []
|
|
featuredImageFailed.value = false
|
|
fetchFeedList()
|
|
}
|
|
|
|
const fetchFeedList = async () => {
|
|
if (loading.value || noMore.value) return
|
|
loading.value = true
|
|
try {
|
|
const params: Record<string, any> = {
|
|
page_no: pageNo.value,
|
|
page_size: pageSize
|
|
}
|
|
const currentChannelItem = allChannels.value[currentChannel.value]
|
|
if (!currentChannelItem || currentChannelItem.specialType === 'recommend') {
|
|
params.is_recommend = 1
|
|
} else if (currentChannelItem.specialType === 'mychannel') {
|
|
params.is_mychannel = 1
|
|
} else if (currentChannelItem.id) {
|
|
params.cid = currentChannelItem.id
|
|
}
|
|
const { lists, count } = await getArticleList(params)
|
|
const newList = lists || []
|
|
if (pageNo.value === 1) {
|
|
feedList.value = newList
|
|
featuredImageFailed.value = false
|
|
} else {
|
|
feedList.value = [...feedList.value, ...newList]
|
|
}
|
|
if (feedList.value.length >= count || newList.length < pageSize) {
|
|
noMore.value = true
|
|
}
|
|
pageNo.value++
|
|
} catch (error) {
|
|
console.log('首页资讯加载失败=>', error)
|
|
uni.showToast({ title: '加载失败,请稍后重试', icon: 'none' })
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const getArticleSummary = (item: any) => {
|
|
return String(item?.desc || item?.content || '')
|
|
.replace(/<[^>]+>/g, ' ')
|
|
.replace(/ /g, ' ')
|
|
.replace(/\s+/g, ' ')
|
|
.trim()
|
|
}
|
|
|
|
const getArticleAuthor = (item: any) => {
|
|
return String(item?.author || 'SB 体育头条').replace(/@懂球帝/g, '').trim() || 'SB 体育头条'
|
|
}
|
|
|
|
const getArticleCategoryName = (item: any) => {
|
|
return articleChannels.value.find((channel) => String(channel.id) === String(item?.cid))?.name || '体育'
|
|
}
|
|
|
|
const formatRelativeTime = (time: string) => {
|
|
if (!time) return ''
|
|
const date = new Date(String(time).replace(/-/g, '/'))
|
|
const delta = Date.now() - date.getTime()
|
|
if (Number.isNaN(date.getTime()) || delta < 0) return String(time)
|
|
const minute = 60 * 1000
|
|
if (delta < minute) return '刚刚'
|
|
if (delta < 60 * minute) return `${Math.floor(delta / minute)}分钟前`
|
|
if (delta < 24 * 60 * minute) return `${Math.floor(delta / (60 * minute))}小时前`
|
|
if (delta < 7 * 24 * 60 * minute) return `${Math.floor(delta / (24 * 60 * minute))}天前`
|
|
return `${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
|
}
|
|
|
|
const formatFocusMeta = (match: any) => {
|
|
const date = getLocalMatchDate(match?.match_time, match)
|
|
const time = date
|
|
? `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
|
: '待定'
|
|
return `${match?.round_name || '赛程'} ${time}`
|
|
}
|
|
|
|
const getTeamInitial = (name: string) => String(name || '?').trim().slice(0, 1)
|
|
|
|
const loadMore = () => fetchFeedList()
|
|
|
|
const goSearch = () => uni.navigateTo({ url: '/pages/search/search' })
|
|
const goChannelManage = () => uni.navigateTo({ url: '/pages/channel/channel-manage' })
|
|
const goAllMatches = () => uni.switchTab({ url: '/packages_match/pages/worldcup' })
|
|
const goMatch = (id: number | string) => {
|
|
if (id) uni.navigateTo({ url: `/pages/match_detail/match_detail?id=${id}` })
|
|
}
|
|
|
|
const handleRedirect = async (options: any) => {
|
|
let slc = options?.slc || ''
|
|
// #ifdef H5
|
|
if (!slc) {
|
|
const urlParams = new URLSearchParams(window.location.search)
|
|
slc = urlParams.get('slc') || ''
|
|
}
|
|
// #endif
|
|
if (!slc) return
|
|
|
|
try {
|
|
const res = await resolveShortLink({
|
|
code: slc,
|
|
fingerprint: generateFingerprint(),
|
|
referer: typeof document === 'undefined' ? '' : document.referrer || ''
|
|
})
|
|
if (res.params?.invite_code) uni.setStorageSync('invite_code', res.params.invite_code)
|
|
if (res.path && res.page_type !== 'invite') {
|
|
setTimeout(() => uni.navigateTo({ url: res.path }), 300)
|
|
}
|
|
} catch (error) {
|
|
console.error('短链接解析失败', error)
|
|
}
|
|
}
|
|
|
|
const generateFingerprint = (): string => {
|
|
// #ifdef H5
|
|
try {
|
|
const canvas = document.createElement('canvas')
|
|
const context = canvas.getContext('2d')
|
|
if (context) {
|
|
context.textBaseline = 'top'
|
|
context.font = '14px Arial'
|
|
context.fillText('fp', 2, 2)
|
|
}
|
|
const raw = `${navigator.userAgent}${navigator.language}${screen.width}x${screen.height}${screen.colorDepth}${new Date().getTimezoneOffset()}${canvas.toDataURL()}`
|
|
let hash = 0
|
|
for (let index = 0; index < raw.length; index++) {
|
|
hash = (hash << 5) - hash + raw.charCodeAt(index)
|
|
hash |= 0
|
|
}
|
|
return Math.abs(hash).toString(16)
|
|
} catch {
|
|
return ''
|
|
}
|
|
// #endif
|
|
// #ifndef H5
|
|
return ''
|
|
// #endif
|
|
}
|
|
|
|
let channelsLoaded = false
|
|
onLoad(async (options: any) => {
|
|
handleRedirect(options)
|
|
await Promise.all([loadChannels(), loadFocusMatches()])
|
|
fetchFeedList()
|
|
})
|
|
|
|
onShow(async () => {
|
|
syncAllChannels()
|
|
if (!channelsLoaded && articleChannels.value.length === 0) {
|
|
await loadChannels()
|
|
fetchFeedList()
|
|
}
|
|
if (channelsLoaded && allChannels.value[currentChannel.value]?.specialType === 'mychannel') {
|
|
reloadFeed()
|
|
}
|
|
channelsLoaded = true
|
|
})
|
|
|
|
const onChannelChanged = (data: any) => {
|
|
if (data.channels) {
|
|
articleChannels.value = data.channels
|
|
syncAllChannels()
|
|
}
|
|
if (data.selectIndex >= 0) {
|
|
const selectedChannel = articleChannels.value[data.selectIndex]
|
|
const selectedIndex = findChannelIndexById(selectedChannel?.id)
|
|
switchChannel(selectedIndex >= 0 ? selectedIndex : 0)
|
|
} else {
|
|
reloadFeed()
|
|
}
|
|
}
|
|
|
|
uni.$on('channelChanged', onChannelChanged)
|
|
onUnload(() => uni.$off('channelChanged', onChannelChanged))
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.index {
|
|
height: 100vh;
|
|
background: #f6f7f9;
|
|
overflow: hidden;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.top-bar {
|
|
flex-shrink: 0;
|
|
z-index: 10;
|
|
background: #fff;
|
|
|
|
&__content {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 16rpx;
|
|
padding: 16rpx 24rpx 14rpx;
|
|
}
|
|
|
|
&__logo {
|
|
width: 76rpx;
|
|
height: 76rpx;
|
|
flex-shrink: 0;
|
|
|
|
&-img {
|
|
width: 100%;
|
|
height: 100%;
|
|
border-radius: 18rpx;
|
|
}
|
|
}
|
|
|
|
&__search {
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 72rpx;
|
|
padding: 0 24rpx;
|
|
gap: 14rpx;
|
|
border-radius: 40rpx;
|
|
display: flex;
|
|
align-items: center;
|
|
background: #f3f4f6;
|
|
|
|
&-text {
|
|
color: #8c95a5;
|
|
font-size: 27rpx;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
}
|
|
}
|
|
|
|
.channel-bar {
|
|
height: 86rpx;
|
|
display: flex;
|
|
align-items: stretch;
|
|
flex-shrink: 0;
|
|
background: #fff;
|
|
border-bottom: 1rpx solid #edf0f5;
|
|
|
|
&__scroll {
|
|
flex: 1;
|
|
min-width: 0;
|
|
height: 86rpx;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
&__list {
|
|
display: inline-flex;
|
|
align-items: stretch;
|
|
min-width: 100%;
|
|
height: 100%;
|
|
padding: 0 12rpx;
|
|
gap: 12rpx;
|
|
box-sizing: border-box;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
&__item {
|
|
flex: 0 0 116rpx;
|
|
width: 116rpx;
|
|
position: relative;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
color: #252b36;
|
|
font-size: 29rpx;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
|
|
text {
|
|
display: block;
|
|
max-width: 100%;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
&--active {
|
|
color: #1468f5;
|
|
font-weight: 700;
|
|
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
left: 50%;
|
|
bottom: 0;
|
|
width: 48rpx;
|
|
height: 6rpx;
|
|
border-radius: 999rpx;
|
|
background: #1468f5;
|
|
transform: translateX(-50%);
|
|
}
|
|
}
|
|
}
|
|
|
|
&__more {
|
|
width: 104rpx;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
gap: 2rpx;
|
|
flex-shrink: 0;
|
|
color: #252b36;
|
|
font-size: 28rpx;
|
|
background: #fff;
|
|
}
|
|
}
|
|
|
|
.feed-wrapper {
|
|
flex: 1;
|
|
min-height: 0;
|
|
background: #f6f7f9;
|
|
}
|
|
|
|
.home-content {
|
|
padding: 16rpx 24rpx 42rpx;
|
|
}
|
|
|
|
.focus-section,
|
|
.latest-section {
|
|
background: #fff;
|
|
border-radius: 22rpx;
|
|
}
|
|
|
|
.focus-section {
|
|
padding: 20rpx;
|
|
box-shadow: 0 8rpx 30rpx rgba(29, 45, 78, 0.04);
|
|
|
|
&__list {
|
|
display: flex;
|
|
gap: 14rpx;
|
|
}
|
|
}
|
|
|
|
.section-heading {
|
|
height: 54rpx;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 14rpx;
|
|
|
|
&__title {
|
|
color: #151b26;
|
|
font-size: 34rpx;
|
|
font-weight: 700;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
&__action {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 2rpx;
|
|
color: #1468f5;
|
|
font-size: 25rpx;
|
|
}
|
|
}
|
|
|
|
.focus-match {
|
|
flex: 1;
|
|
min-width: 0;
|
|
padding: 16rpx 14rpx 18rpx;
|
|
border: 1rpx solid #e7eaf0;
|
|
border-radius: 16rpx;
|
|
background: #fff;
|
|
|
|
&__head {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10rpx;
|
|
min-width: 0;
|
|
}
|
|
|
|
&__league {
|
|
max-width: 92rpx;
|
|
padding: 4rpx 10rpx;
|
|
border-radius: 8rpx;
|
|
color: #235bde;
|
|
background: #eaf0ff;
|
|
font-size: 21rpx;
|
|
font-weight: 600;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
|
|
&--warm {
|
|
color: #ef6321;
|
|
background: #fff0e8;
|
|
}
|
|
}
|
|
|
|
&__time {
|
|
min-width: 0;
|
|
flex: 1;
|
|
color: #7d8797;
|
|
font-size: 21rpx;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
&__teams {
|
|
display: grid;
|
|
grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
margin-top: 16rpx;
|
|
}
|
|
|
|
&__team {
|
|
min-width: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
gap: 8rpx;
|
|
}
|
|
|
|
&__badge {
|
|
width: 58rpx;
|
|
height: 58rpx;
|
|
|
|
&--fallback {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 50%;
|
|
background: #edf2ff;
|
|
color: #1468f5;
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
}
|
|
}
|
|
|
|
&__team-name {
|
|
width: 100%;
|
|
color: #202633;
|
|
font-size: 23rpx;
|
|
line-height: 1.25;
|
|
text-align: center;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
&__versus {
|
|
color: #161d29;
|
|
font-size: 28rpx;
|
|
font-weight: 700;
|
|
}
|
|
}
|
|
|
|
.featured-card {
|
|
position: relative;
|
|
display: block;
|
|
height: 400rpx;
|
|
margin-top: 18rpx;
|
|
overflow: hidden;
|
|
border-radius: 22rpx;
|
|
background: linear-gradient(135deg, #164b91 0%, #142839 100%);
|
|
box-shadow: 0 10rpx 30rpx rgba(20, 37, 65, 0.1);
|
|
|
|
&__image,
|
|
&__shade {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
&__shade {
|
|
background: linear-gradient(180deg, rgba(8, 18, 29, 0.08) 20%, rgba(7, 15, 24, 0.82) 100%);
|
|
}
|
|
|
|
&__content {
|
|
position: relative;
|
|
z-index: 1;
|
|
height: 100%;
|
|
box-sizing: border-box;
|
|
padding: 24rpx;
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
&__tag {
|
|
align-self: flex-start;
|
|
padding: 6rpx 18rpx;
|
|
border-radius: 10rpx;
|
|
color: #fff;
|
|
background: #1468f5;
|
|
font-size: 24rpx;
|
|
line-height: 1.25;
|
|
}
|
|
|
|
&__copy {
|
|
margin-top: auto;
|
|
}
|
|
|
|
&__title {
|
|
display: -webkit-box;
|
|
overflow: hidden;
|
|
color: #fff;
|
|
font-size: 36rpx;
|
|
font-weight: 700;
|
|
line-height: 1.32;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
}
|
|
|
|
&__desc {
|
|
display: -webkit-box;
|
|
overflow: hidden;
|
|
margin-top: 10rpx;
|
|
color: rgba(255, 255, 255, 0.92);
|
|
font-size: 25rpx;
|
|
line-height: 1.5;
|
|
-webkit-line-clamp: 2;
|
|
-webkit-box-orient: vertical;
|
|
}
|
|
|
|
&__meta {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 28rpx;
|
|
margin-top: 16rpx;
|
|
color: rgba(255, 255, 255, 0.88);
|
|
font-size: 23rpx;
|
|
line-height: 1.2;
|
|
}
|
|
}
|
|
|
|
.latest-section {
|
|
margin-top: 18rpx;
|
|
overflow: hidden;
|
|
|
|
&__heading {
|
|
padding: 20rpx 20rpx 8rpx;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
&__filter {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6rpx;
|
|
color: #7a8494;
|
|
font-size: 24rpx;
|
|
}
|
|
}
|
|
|
|
.feed-loading {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 12rpx;
|
|
padding: 30rpx 0;
|
|
|
|
&__text {
|
|
color: #98a2b3;
|
|
font-size: 24rpx;
|
|
}
|
|
}
|
|
|
|
.feed-bottom,
|
|
.feed-empty {
|
|
color: #98a2b3;
|
|
font-size: 24rpx;
|
|
text-align: center;
|
|
}
|
|
|
|
.feed-bottom {
|
|
padding: 30rpx 0;
|
|
}
|
|
|
|
.feed-empty {
|
|
padding: 100rpx 0;
|
|
}
|
|
</style>
|