Masters.vue 6.35 KB
<template>
  <div class="masters-container">
    <!-- 一行一个 Item(单列) -->
    <section class="single-list">
      <div
        class="item-card"
        v-for="(item, i) in singleItems"
        :key="`single-${i}`"
        @click="goDetail(item)"
      >
        <div class="item-image">
          <img
            :src="item.image"
            :alt="`${item.role} ${item.name}`"
            :aria-label="`${item.role}:${item.name}`"
            loading="lazy"
          />
        </div>
        <div class="item-caption">
          <div class="item-role">{{ item.role }}</div>
          <div class="item-name">
            <sup class="name-sup">上</sup>{{ item.name.charAt(0) }}<sup class="name-sup">下</sup>{{ item.name.slice(1) }}
          </div>
        </div>
      </div>
    </section>

    <!-- 一行两个 Item(双列) -->
    <section class="grid-two">
      <div
        class="item-card small"
        v-for="(item, i) in gridItems"
        :key="`grid-${i}`"
        @click="goDetail(item)"
      >
        <div class="item-image">
          <img
            :src="item.image"
            :alt="`${item.role} ${item.name}`"
            :aria-label="`${item.role}:${item.name}`"
            loading="lazy"
          />
        </div>
        <div class="item-caption">
          <div class="item-role">{{ item.role }}</div>
          <div class="item-name small">
            <sup class="name-sup">上</sup>{{ item.name.charAt(0) }}<sup class="name-sup">下</sup>{{ item.name.slice(1) }}
          </div>
        </div>
      </div>
    </section>

    <!-- 一行两个 Item(双列) -->
    <section class="grid-two grid-two--spaced">
      <div
        class="item-card small"
        v-for="(item, i) in gridItems2"
        :key="`grid2-${i}`"
        @click="goDetail(item)"
      >
        <div class="item-image">
          <img
            :src="item.image"
            :alt="`${item.role} ${item.name}`"
            :aria-label="`${item.role}:${item.name}`"
            loading="lazy"
          />
        </div>
        <div class="item-caption">
          <div class="item-role">{{ item.role }}</div>
          <div class="item-name small">
            <sup class="name-sup">上</sup>{{ item.name.charAt(0) }}<sup class="name-sup">下</sup>{{ item.name.slice(1) }}
          </div>
        </div>
      </div>
    </section>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { useTitle } from '@vueuse/core';

// 导入接口
import { getSSQZAPI } from '@/api/index.js'

const router = useRouter()

useTitle('三師七證')

// 三师
const singleItems = ref([])

// 七证
const gridItems = ref([])

// 引礼师
// 说明:展示位置已迁移到独立的“引礼师”页面(/yinlishi)。
// 这里先保留旧字段结构与取值注释,避免后续回查旧接口映射时丢失上下文。
const gridItems2 = ref([])

const goDetail = (item) => {
  router.push(`/masters/${item.id}`)
}

const pid = ref(router.currentRoute.value.query.pid)

/**
 * 安全处理图片 URL,添加处理参数并处理空值
 * @param {string} photo - 原始图片 URL
 * @returns {string} - 处理后的图片 URL
 */
const processImageUrl = (photo) => {
  if (!photo) return '/assets/default-avatar.png'
  return `${photo}?imageMogr2/thumbnail/400x/strip/quality/70`
}

/**
 * 安全处理大师数据项
 * @param {object} item - API 返回的数据项
 * @returns {object} - 处理后的数据项
 */
const processMasterItem = (item) => ({
  id: item?.id || '',
  role: item?.post_excerpt || '',
  name: item?.post_title || '未知',
  image: processImageUrl(item?.photo)
})

onMounted(async () => {
  try {
    // 调用接口获取三师七证数据
    const { code, list } = await getSSQZAPI({ pid: pid.value })

    // 验证响应数据
    if (!code || !list || !Array.isArray(list)) {
      console.error('Invalid API response: missing or invalid data')
      return
    }

    // 安全地获取数据列表,使用可选链和默认值
    const singleItemsData = list[0]?.list || []
    const gridItemsData = list[1]?.list || []
    const gridItems2Data = list[2]?.list || []

    // 处理数据
    singleItems.value = singleItemsData.map(processMasterItem)
    gridItems.value = gridItemsData.map(processMasterItem)
    gridItems2.value = gridItems2Data.map(processMasterItem)
  } catch (error) {
    console.error('Failed to fetch masters data:', error)
    // 可以在这里添加用户提示,例如使用 showFailToast
  }
})
</script>

<style scoped>
/* 页面容器 */
.masters-container {
  padding: 1.5rem;
  background: #F2EBDB;
}

/* 单列列表区 */
.single-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 1rem;
}

/* 双列网格区 */
.grid-two {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 0.75rem;
}

.grid-two--spaced {
  margin-top: 1rem;
}

/* 卡片 */
.item-card {
  position: relative;
  border: 2px solid #6B4102;
  overflow: hidden;
  transition: transform 0.2s ease;
  padding: 0.5rem;
}

.item-card:hover {
  transform: translateY(-0.125rem);
}

.item-card.small {
  /* 双列卡片视觉上更紧凑 */
  border: 1px solid rgba(107, 65, 2, 0.8);
}

/* 图片区域 - 采用固定纵向比例 */
.item-image {
  width: 100%;
  aspect-ratio: 3 / 4;
  background: #f5f5f5;
}

.item-image img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* 底部说明条 */
.item-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(107, 65, 2, 0.8);
  color: #fff;
  padding: 0.75rem;
  text-align: center;
}

.item-role {
  font-size: 0.75rem;
  opacity: 0.95;
}

.item-name {
  font-size: 1.5rem;
  font-weight: 600;
  margin-top: 0.25rem;
}

.item-name.small {
  font-size: 1.25rem;
}

/* 姓名上标样式 */
.name-sup {
  font-size: 0.6em;
}


/* 响应式调整 */
@media (max-width: 48rem) {
  .item-caption {
    padding: 0.625rem;
    margin: 0.5rem;
  }
}

@media (max-width: 30rem) {
  .masters-container {
    padding: 0.75rem;
  }
  .item-caption {
    padding: 0.5rem;
    margin: 0.5rem;
  }
  .item-name {
    font-size: 1.25rem;
  }
  .item-name.small {
    font-size: 1rem;
  }
}

@media (max-width: 20rem) {
  .grid-two {
    gap: 0.5rem;
  }
  .item-caption {
    padding: 0.375rem;
    margin: 0.5rem;
  }
  .item-name {
    font-size: 1rem;
  }
  .item-name.small {
    font-size: 0.875rem;
  }
}
</style>