index.vue 4.56 KB
<template>
  <view class="index-page">
    <view class="page-content">
      <view
        v-if="banner.image_url"
        class="home-banner"
        :class="{ clickable: hasLink(banner) }"
        @tap="handleLinkedItemTap(banner)"
      >
        <image class="home-banner__image" :src="banner.image_url" mode="aspectFill" />
      </view>

      <view class="nav-panel">
        <view
          v-for="item in nav_list"
          :key="item.id"
          class="nav-entry"
          :class="{ clickable: hasLink(item) }"
          @tap="handleLinkedItemTap(item)"
        >
          <view class="nav-entry__icon-wrap">
            <i
              class="nav-entry__icon"
              :class="getIconClass(item)"
            ></i>
          </view>
          <text class="nav-entry__title">{{ item.title }}</text>
        </view>
      </view>

      <view class="image-link-list">
        <view
          v-for="item in image_links"
          :key="item.id"
          class="image-link-card"
          :class="{ clickable: hasLink(item) }"
          @tap="handleLinkedItemTap(item)"
        >
          <image class="image-link-card__image" :src="item.image_url" mode="aspectFill" />
        </view>
      </view>
    </view>

    <AppTabbar current="home" />
  </view>
</template>

<script setup>
import { ref } from 'vue'
import Taro, { useLoad } from '@tarojs/taro'
import AppTabbar from '@/components/AppTabbar.vue'
import { getHomeContentAPI } from '@/api'
import { buildWebviewPreviewUrl } from '@/utils/webview'

const default_icon = 'icon-jingxiuying'
const banner = ref({})
const nav_list = ref([])
const image_links = ref([])

const hasLink = (item) => !!(item?.page_url || item?.link_url)

const getIconClass = (item) => {
  const icon = item?.icon || default_icon
  const font_class = icon.startsWith('fa-') ? 'fa' : 'iconfont'
  return [font_class, icon]
}

const handleLinkedItemTap = (item) => {
  if (!item) return

  if (item.page_url) {
    Taro.navigateTo({
      url: item.page_url,
    })
    return
  }

  if (item.link_url) {
    Taro.navigateTo({
      url: buildWebviewPreviewUrl(item.link_url, item.link_title || item.title || ''),
    })
  }
}

const fetchHomeContent = async () => {
  const response = await getHomeContentAPI()

  if (response?.code !== 1) {
    Taro.showToast({
      title: response?.msg || '获取首页内容失败',
      icon: 'none',
    })
    return
  }

  const data = response?.data || {}
  banner.value = data.banner || {}
  nav_list.value = data.nav_list || []
  image_links.value = data.image_links || []
}

useLoad(() => {
  fetchHomeContent()
})
</script>

<style lang="less">
.index-page {
  min-height: 100vh;
  background: #f1f1f1;

  .page-content {
    padding: 32rpx 32rpx 0;
    box-sizing: border-box;
  }

  .home-banner {
    margin: -32rpx -32rpx 0;
    height: 420rpx;
    overflow: hidden;
    background: #e5e7eb;
  }

  .home-banner__image,
  .image-link-card__image {
    display: block;
    width: 100%;
    height: 100%;
  }

  .nav-panel {
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    margin: 32rpx 0 0;
    overflow: hidden;
    border-radius: 16rpx;
    background: #fff;
    box-shadow: 0 16rpx 40rpx rgba(31, 41, 55, 0.06);
  }

  .nav-entry {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-width: 0;
    min-height: 184rpx;
    padding: 28rpx 8rpx 24rpx;
    box-sizing: border-box;
    color: #98271d;
  }

  .nav-entry::after {
    position: absolute;
    top: 0;
    right: 0;
    width: 1rpx;
    height: 100%;
    background: #ececec;
    content: '';
  }

  .nav-entry:nth-child(3n)::after {
    display: none;
  }

  .nav-entry:nth-child(n + 4)::before {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 1rpx;
    background: #ececec;
    content: '';
  }

  .nav-entry__icon-wrap {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 76rpx;
    height: 76rpx;
    line-height: 1;
  }

  .nav-entry__icon {
    font-size: 64rpx;
    color: #98271d;
  }

  .nav-entry__title {
    display: block;
    width: 100%;
    margin-top: 18rpx;
    font-size: 28rpx;
    line-height: 1.25;
    text-align: center;
    color: #98271d;
    word-break: break-all;
  }

  .image-link-list {
    display: flex;
    flex-direction: column;
    gap: 32rpx;
    margin-top: 40rpx;
    padding: 0 4rpx 40rpx;
  }

  .image-link-card {
    position: relative;
    height: 252rpx;
    overflow: hidden;
    border-radius: 12rpx;
    background: #d1d5db;
  }

  .clickable {
    cursor: pointer;
  }
}
</style>