no message
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<!-- modal:隐私授权弹窗-->
|
||||
<view v-if="show" class="modal-box" @tap.stop>
|
||||
<view class="dialog" @tap.stop>
|
||||
<view class="title">隐私政策提示</view>
|
||||
<view class="content">
|
||||
欢迎使用{{
|
||||
appStore.getWebsiteConfig.shop_name
|
||||
}}小程序,请您在使用前点击
|
||||
<text
|
||||
class="text-[#243245]"
|
||||
hover-class="hover"
|
||||
@click="openContract"
|
||||
>
|
||||
{{ name }}
|
||||
</text>
|
||||
并仔细阅读,如您同意全部内容,请点击同意开始使用我们的服务。
|
||||
</view>
|
||||
<view class="btn-box">
|
||||
<button
|
||||
class="btn disagree"
|
||||
hover-class="hover"
|
||||
@click="disagreePrivacy"
|
||||
>
|
||||
不同意
|
||||
</button>
|
||||
<button
|
||||
class="btn bg-primary text-white"
|
||||
hover-class="hover"
|
||||
id="agree-btn"
|
||||
open-type="agreePrivacyAuthorization"
|
||||
@agreeprivacyauthorization="agreePrivacy"
|
||||
>
|
||||
同意
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
import { useAppStore } from '@/stores/app'
|
||||
const appStore = useAppStore()
|
||||
|
||||
const name = ref<string>('')
|
||||
const show = ref<boolean>(false)
|
||||
|
||||
interface PrivacyRes {
|
||||
errMsg: string
|
||||
privacyContractName: string
|
||||
needAuthorization: boolean
|
||||
}
|
||||
|
||||
if (wx.getPrivacySetting) {
|
||||
wx.getPrivacySetting({
|
||||
success(res: PrivacyRes) {
|
||||
console.log(res)
|
||||
name.value = res.privacyContractName
|
||||
show.value = res.needAuthorization
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const openContract = () => {
|
||||
wx.openPrivacyContract({
|
||||
success: () => {},
|
||||
fail: () => {}
|
||||
})
|
||||
}
|
||||
|
||||
const disagreeHandle = () => {
|
||||
// 用户点击拒绝后
|
||||
show.value = false
|
||||
}
|
||||
|
||||
const disagreePrivacy = () => {
|
||||
uni.$u.toast('同意隐私政策后可继续使用')
|
||||
// wx.exitMiniProgram()
|
||||
}
|
||||
|
||||
const agreePrivacy = () => {
|
||||
show.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.modal-box {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
.modal-box .dialog {
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
padding: 40rpx;
|
||||
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
|
||||
background: #ffffff;
|
||||
border-radius: 20rpx 20rpx 0 0;
|
||||
}
|
||||
|
||||
.modal-box .title {
|
||||
text-align: center;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.modal-box .content {
|
||||
display: block;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-top: 20rpx;
|
||||
text-align: justify;
|
||||
line-height: 1.6;
|
||||
padding: 10rpx 20rpx;
|
||||
}
|
||||
|
||||
.modal-box .btn-box {
|
||||
margin-top: 50rpx;
|
||||
padding: 0 30rpx;
|
||||
padding-bottom: 30rpx;
|
||||
display: flex;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-box .btn::after {
|
||||
border: none;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-box .btn-box .btn {
|
||||
width: 50%;
|
||||
height: 76rpx;
|
||||
line-height: 76rpx;
|
||||
margin: 0 10rpx;
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 60px;
|
||||
font-size: 28rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.modal-box .disagree {
|
||||
color: #0f0f0f;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,563 @@
|
||||
<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="28" color="#999" />
|
||||
<text class="top-bar__search-text">{{ searchPlaceholder }}</text>
|
||||
</view>
|
||||
<view class="top-bar__actions" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- Feed流内容 -->
|
||||
<scroll-view scroll-y class="feed-wrapper" :style="{ height: feedHeight + 'px' }" @scrolltolower="loadMore"
|
||||
:lower-threshold="100">
|
||||
<view class="assistant-entry" @tap="goAssistant">
|
||||
<view class="assistant-entry__icon">AI</view>
|
||||
<view class="assistant-entry__body">
|
||||
<text class="assistant-entry__title">世博头条 AI 助手</text>
|
||||
<text class="assistant-entry__desc">问资讯、赛事、社区、彩票和行情</text>
|
||||
</view>
|
||||
<u-icon name="arrow-right" size="28" color="#185dff" />
|
||||
</view>
|
||||
<!-- 频道Tab栏 -->
|
||||
<view class="channel-bar">
|
||||
<scroll-view scroll-x class="channel-bar__scroll" :scroll-left="channelScrollLeft"
|
||||
scroll-with-animation>
|
||||
<view class="channel-bar__list">
|
||||
<view v-for="(item, index) in allChannels" :key="item.channelKey" class="channel-bar__item"
|
||||
:class="{ 'channel-bar__item--active': currentChannel === index }"
|
||||
@tap="switchChannel(index)">
|
||||
<text>{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="channel-bar__more" @tap="goChannelManage">
|
||||
<UIcon name="list" size="38" color="#666" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<template v-for="item in feedList" :key="item.id">
|
||||
<FeedTranslateCard v-if="hasTranslatedContent(item)" :item="item" />
|
||||
<FeedVideoCard v-else-if="item.is_video" :item="item" />
|
||||
<FeedCard v-else :item="item" />
|
||||
</template>
|
||||
<view class="feed-loading" v-if="loading">
|
||||
<ULoading mode="circle" />
|
||||
<text class="feed-loading__text">加载中...</text>
|
||||
</view>
|
||||
<view class="feed-bottom" v-if="noMore && feedList.length">
|
||||
<text>没有更多了</text>
|
||||
</view>
|
||||
<view class="feed-empty" v-if="!loading && !feedList.length">
|
||||
<text>暂无内容</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- #ifdef MP -->
|
||||
<MpPrivacyPopup></MpPrivacyPopup>
|
||||
<!-- #endif -->
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onLoad, onShow, onUnload } from '@dcloudio/uni-app'
|
||||
import { ref, nextTick } from 'vue'
|
||||
import { getArticleList, getArticleCate } from '@/api/news'
|
||||
import { resolveShortLink } from '@/api/share'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import FeedCard from '@/components/feed-card/feed-card.vue'
|
||||
import FeedTranslateCard from '@/components/feed-translate-card/feed-translate-card.vue'
|
||||
import FeedVideoCard from '@/components/feed-video-card/feed-video-card.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)
|
||||
const feedHeight = ref(500)
|
||||
let windowHeight = 667
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
// #ifdef APP-PLUS || MP
|
||||
statusBarHeight.value = res.statusBarHeight || 44
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
statusBarHeight.value = 0
|
||||
// #endif
|
||||
windowHeight = res.windowHeight || 667
|
||||
const topBarH = statusBarHeight.value + 44
|
||||
const tabbarH = uni.upx2px(100)
|
||||
feedHeight.value = windowHeight - topBarH - tabbarH
|
||||
}
|
||||
})
|
||||
const calcFeedHeight = () => {
|
||||
const query = uni.createSelectorQuery()
|
||||
query.select('.top-bar').boundingClientRect()
|
||||
query.exec((rects: any[]) => {
|
||||
const topBarH = rects[0]?.height || (statusBarHeight.value + 44)
|
||||
const tabbarH = uni.upx2px(100)
|
||||
feedHeight.value = windowHeight - topBarH - tabbarH
|
||||
})
|
||||
}
|
||||
|
||||
const searchPlaceholder = ref('搜索乐博头条')
|
||||
|
||||
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 channelScrollLeft = ref(0)
|
||||
const feedList = ref<any[]>([])
|
||||
const loading = ref(false)
|
||||
const noMore = ref(false)
|
||||
const pageNo = ref(1)
|
||||
const pageSize = 10
|
||||
|
||||
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 {
|
||||
const allCate = await getArticleCate()
|
||||
articleChannels.value = allCate || []
|
||||
syncAllChannels()
|
||||
} catch (e) {
|
||||
console.log('获取分类失败=>', e)
|
||||
articleChannels.value = []
|
||||
syncAllChannels()
|
||||
}
|
||||
}
|
||||
|
||||
const switchChannel = (index: number) => {
|
||||
if (currentChannel.value === index) return
|
||||
currentChannel.value = index
|
||||
reloadFeed()
|
||||
}
|
||||
|
||||
const reloadFeed = () => {
|
||||
pageNo.value = 1
|
||||
noMore.value = false
|
||||
feedList.value = []
|
||||
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
|
||||
} else {
|
||||
feedList.value = [...feedList.value, ...newList]
|
||||
}
|
||||
if (feedList.value.length >= count || newList.length < pageSize) {
|
||||
noMore.value = true
|
||||
}
|
||||
pageNo.value++
|
||||
} catch (e) {
|
||||
console.log('feed加载失败=>', e)
|
||||
uni.showToast({ title: '加载失败,请稍后重试', icon: 'none' })
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const loadMore = () => {
|
||||
fetchFeedList()
|
||||
}
|
||||
|
||||
const hasTranslatedContent = (item: any) => {
|
||||
return !!String(item?.translated_content || item?.worldcup_translated_content || '').trim()
|
||||
}
|
||||
|
||||
const goSearch = () => {
|
||||
uni.navigateTo({ url: '/pages/search/search' })
|
||||
}
|
||||
|
||||
const goChannelManage = () => {
|
||||
uni.navigateTo({ url: '/pages/channel/channel-manage' })
|
||||
}
|
||||
|
||||
const goAssistant = () => {
|
||||
uni.navigateTo({ url: '/pages/ai_assistant/ai_assistant' })
|
||||
}
|
||||
|
||||
onLoad(async (options: any) => {
|
||||
// 处理短链接跳转参数
|
||||
handleRedirect(options)
|
||||
|
||||
await loadChannels()
|
||||
fetchFeedList()
|
||||
nextTick(() => {
|
||||
setTimeout(calcFeedHeight, 100)
|
||||
})
|
||||
})
|
||||
|
||||
const handleRedirect = async (options: any) => {
|
||||
// onLoad options 或 URL query 中获取 slc
|
||||
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 fingerprint = generateFingerprint()
|
||||
const res = await resolveShortLink({
|
||||
code: slc,
|
||||
fingerprint,
|
||||
referer: 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 (e) {
|
||||
console.error('短链接解析失败', e)
|
||||
}
|
||||
}
|
||||
|
||||
const generateFingerprint = (): string => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas')
|
||||
const ctx = canvas.getContext('2d')
|
||||
if (ctx) {
|
||||
ctx.textBaseline = 'top'
|
||||
ctx.font = '14px Arial'
|
||||
ctx.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 i = 0; i < raw.length; i++) {
|
||||
hash = (hash << 5) - hash + raw.charCodeAt(i)
|
||||
hash |= 0
|
||||
}
|
||||
return Math.abs(hash).toString(16)
|
||||
} catch {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
let channelsLoaded = false
|
||||
onShow(async () => {
|
||||
syncAllChannels()
|
||||
// iOS授权弹窗关闭后数据可能未加载,检测并重试
|
||||
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: #f5f5f5;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 顶部导航栏 */
|
||||
.top-bar {
|
||||
flex-shrink: 0;
|
||||
z-index: 100;
|
||||
background: #fff;
|
||||
|
||||
&__content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12rpx 24rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
&__logo {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
|
||||
&-img {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
&__search {
|
||||
flex: 1;
|
||||
height: 64rpx;
|
||||
background: #f5f5f5;
|
||||
border-radius: 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 24rpx;
|
||||
gap: 12rpx;
|
||||
|
||||
&-text {
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
&__actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__publish {
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #e5edff;
|
||||
border-radius: 16rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 频道Tab栏 */
|
||||
.assistant-entry {
|
||||
margin: 20rpx 24rpx 16rpx;
|
||||
padding: 22rpx 24rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #fff;
|
||||
border: 1rpx solid #dbe6f5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18rpx;
|
||||
|
||||
&__icon {
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #185dff;
|
||||
color: #fff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&__body {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
&__title {
|
||||
display: block;
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
&__desc {
|
||||
display: block;
|
||||
margin-top: 4rpx;
|
||||
font-size: 23rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
}
|
||||
|
||||
/* 频道Tab栏 */
|
||||
.channel-bar {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
height: 80rpx;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
|
||||
&__scroll {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
&__list {
|
||||
white-space: nowrap;
|
||||
height: 80rpx;
|
||||
padding: 0 8rpx;
|
||||
}
|
||||
|
||||
&__item {
|
||||
display: inline-block;
|
||||
padding: 0 16rpx;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
&--active {
|
||||
color: #0053d8;
|
||||
font-weight: bold;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 48rpx;
|
||||
height: 6rpx;
|
||||
background: #0053d8;
|
||||
border-radius: 3rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__more {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-left: 1rpx solid #f0f0f0;
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
/* Feed流 */
|
||||
.feed-wrapper {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.feed-loading {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 30rpx 0;
|
||||
gap: 12rpx;
|
||||
|
||||
&__text {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.feed-bottom {
|
||||
text-align: center;
|
||||
padding: 30rpx 0;
|
||||
font-size: 24rpx;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.feed-empty {
|
||||
text-align: center;
|
||||
padding: 100rpx 0;
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user