9433cfb9创建于 2025年12月31日历史提交
<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>