<template>
<view :class="['uni-container', isDarkMode ? 'theme-dark' : 'theme-light']" style="flex: 1;padding: 0px;">
<view ref="navbar" class="custom-navbar">
<!-- #ifdef APP || WEB -->
<view class="status-bar"></view>
<!-- #endif -->
<!-- #ifdef MP -->
<view class="status-bar" :style="{ height: statusBarHeight + 'px' }"></view>
<!-- #endif -->
<view class="custom-navbar-inner">
<view class="left-content">
<view ref="backRef" class="left-content-inner" @click="back">
<uni-icons style="margin-left: -1px;" type="back" :color="iconColor"></uni-icons>
</view>
</view>
<view class="custom-navbar-content">
<text ref="textRef" class="content-inner-text">
标题
</text>
</view>
</view>
</view>
<scroll-view class="list-container" direction="vertical" refresher-enabled=true @refresherrefresh="onRefresherrefresh" :refresher-triggered="refresherTriggered" @scroll="contentScroll">
<view class="banner">
<image class="banner-img" :src="banner.cover"></image>
<text class="banner-title">{{ banner.title }}</text>
</view>
<view class="list-tab" ref="tabRef">
<view class="th-item">
<text v-for="(name,index) in th_item" :key="index" @click="clickTH(index)" class="th-item-text">
{{name}}
</text>
</view>
</view>
<template v-for="(value, index) in listData" :key="index">
<view class="uni-list-cell" hover-class="uni-list-cell-hover">
<view class="uni-media-list">
<image v-if="isWideScreen" class="uni-media-list-logo" :src="value.cover"></image>
<image v-else class="uni-media-list-logo" :src="value.cover"></image>
<view class="uni-media-list-body">
<text class="uni-media-list-text-top">{{ value.title }}</text>
<view class="uni-media-list-text-bottom">
<text class="uni-media-list-text">{{ value.author_name }}</text>
<text class="uni-media-list-text">{{ value.published_at }}</text>
</view>
</view>
</view>
</view>
</template>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import { state } from '@/store/index.uts'
type Banner = {
cover : string | null,
title : string | null,
post_id : string | null
}
type Item = {
author_name : string,
cover : string,
id : number,
post_id : string,
published_at : string,
title : string
}
const th_item = ref(["排序", "筛选"])
const refresherTriggered = ref(false)
const banner = ref({} as Banner)
const listData = ref([] as Item[])
const last_id = ref('')
const isWideScreen = ref(false) // 是否为宽屏模式
const currentIndex = ref(0) // 当前选中的列表项索引
const post_id = ref('')
const cover = ref('')
const title = ref('')
const firstDetailContent = ref('') // 并行预加载的第一个详情内容
// 导航栏相关
const navbar = ref<UniElement | null>(null);
const backRef = ref<UniElement | null>(null);
const textRef = ref<UniElement | null>(null);
const iconColor = ref('#999')
const navbarHeight = ref(0)
const tabRef = ref<UniElement | null>(null);
const tabTop = ref(0)
function getBanner() {
let data = {
column: 'id,post_id,title,author_name,cover,published_at' //需要的字段名
};
uni.request<Banner>({
url: 'https://unidemo.dcloud.net.cn/api/banner/36kr',
data: data,
success: data => {
refresherTriggered.value = false
if (data.statusCode == 200) {
const result = data.data
if (result != null) {
banner.value = result;
}
}
},
fail: (e) => {
console.log('fail', e);
}
});
}
function getList() {
let url = "https://unidemo.dcloud.net.cn/api/news?column=id,post_id,title,author_name,cover,published_at";
uni.request<Item[]>({
url: url,
method: "GET",
success: (res) => {
if (res.statusCode == 200) {
const result = res.data
//因本接口没有更多分页数据,所以重新赋值。正常有分页的列表应该如下面push方式增加数组项
// listData.value.push(...result)
// last_id.value = listData.value[0].id + "";
if (result != null) {
listData.value = result
// 宽屏模式下,自动选中并展示第一个新闻详情
}
refresherTriggered.value = false;
}
},
fail: (res) => {
console.log('fail', res);
refresherTriggered.value = false
}
});
}
function clickTH(index : number) {
uni.showModal({
content: "点击表头项:" + index,
showCancel: false
});
}
function onRefresherrefresh() {
refresherTriggered.value = true
getBanner();
getList();
}
function contentScroll(event : UniScrollEvent) {
const top = event.detail.scrollTop
const THRESHOLD = 100
let transparent_count = parseFloat((top / THRESHOLD).toFixed(2))
if (transparent_count >= 1) {
transparent_count = 1
iconColor.value = '#fff'
} else {
iconColor.value = '#999'
}
navbar.value!.style.setProperty('background-color', `rgba(0, 122, 255,${transparent_count})`)
backRef.value!.style.setProperty('background-color', `rgba(255, 255, 255,${1 - transparent_count})`)
textRef.value!.style.setProperty('opacity', transparent_count.toString())
if (tabTop.value <= top) {
const stickyTran = top - tabTop.value
tabRef.value!.style.setProperty('transform', 'translateY(' + stickyTran + 'px)');
}
}
async function calcStickyTop() {
const stickyRect = await (tabRef.value! as UniElement).getBoundingClientRectAsync()!;
const navbarRect = await (navbar.value! as UniElement).getBoundingClientRectAsync()!;
navbarHeight.value = navbarRect.height
tabTop.value = stickyRect.top - navbarHeight.value
}
const back = () => {
uni.navigateBack({});
}
const isDarkMode = computed(() => state.isDarkMode)
onLoad(() => {
getBanner()
getList()
})
onReady(() => {
// const top = tabRef.value!.getBoundingClientRect().top
// navbarHeight.value = navbar.value!.offsetHeight
// tabTop.value = top - navbarHeight.value
calcStickyTop()
})
</script>
<style>
.list-container {
position: relative;
width: 100%;
background-color: var(--list-background-color, #ffffff);
flex: 1;
}
.th-item {
width: 100%;
height: 44px;
background-color: var(--list-background-color, #ffffff);
flex-direction: row;
justify-content: center;
align-items: center;
}
.th-item-text {
margin-right: 20px;
color: var(--text-color, #333333);
}
.banner {
height: 180px;
overflow: hidden;
position: relative;
background-color: var(--background-color, #f8f8f8);
}
.banner-img {
width: 100%;
}
.banner-title {
max-height: 42px;
overflow: hidden;
position: absolute;
left: 15px;
bottom: 15px;
width: 90%;
font-size: 16px;
font-weight: 400;
line-height: 21px;
color: white;
}
.uni-media-list {
padding: 11px 15px;
box-sizing: border-box;
display: flex;
width: 100%;
flex-direction: row;
}
.uni-media-list-logo {
width: 90px;
height: 70px;
}
.uni-media-list-body {
flex: 1;
padding-left: 7px;
justify-content: space-around;
}
.uni-media-list-text-top {
/* height: 37px; */
font-size: 14px;
/* lines: 2; */
overflow: hidden;
color: var(--text-color, #333333);
}
.uni-media-list-text-bottom {
display: flex;
flex-direction: row;
justify-content: space-between;
}
.uni-media-list-text {
color: #9D9D9F;
font-size: 13px;
}
/* 导航栏样式 */
.status-bar {
height: var(--status-bar-height);
}
.custom-navbar {
position: absolute;
top: 0;
left: 0;
right: 0;
z-index: 10;
background-color: rgba(0, 122, 255, 0);
}
.custom-navbar-inner {
position: relative;
flex-direction: row;
justify-content: space-between;
height: 45px;
}
.left-content {
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 100%;
}
.left-content-inner {
display: flex;
justify-content: center;
align-items: center;
width: 28px;
height: 28px;
background-color: #fff;
border-radius: 40px;
}
.custom-navbar-content {
position: absolute;
height: 100%;
top: 0;
bottom: 0;
left: 45px;
right: 45px;
flex-direction: row;
justify-content: center;
align-items: center;
}
.content-inner-text {
opacity: 0;
color: #f5f5f5;
}
.list-tab {
position: relative;
z-index: 9;
}
</style>