index.vue 4.64 KB
<template>
  <view class="page-container">
    <!-- 加载页 -->
    <up-loading-page
        :loading="loading"
        loading-text="加载中..."
        color="#577ee3"
        zIndex="1000"
    ></up-loading-page>

    <!-- 蓝色装饰块 -->
    <view class="blue-decor-block" v-show="!loading"></view>

    <!-- 内容容器 -->
    <view class="content-wrap">
      <!-- 空状态:过滤后无数据且非加载中时显示 -->
      <u-empty
          v-if="!filteredModuleList.length && !loading"
          mode="permission"
      ></u-empty>

      <!-- 菜单卡片列表:仅渲染有子节点的父模块 -->
      <view class="menu-card-wrap">
        <up-card
            :title-size="18"
            :border="false"
            :shadow="true"
            :foot-border-top="false"
            v-for="(parentModule, index) in filteredModuleList"
            :key="`$parentModule.id_${index}`"
            :title="parentModule.name"
            class="card-container"
        >
          <template #body>
            <view class="card-content">
              <view
                  v-for="(listItem, listIndex) in parentModule.children"
                  :key="listItem.id"
                  class="content-block"
                  @click="handleMenuClick(listItem)"
              >
                <u-image
                    :src="listItem.icon"
                    mode="aspectFit"
                    width="80rpx"
                    height="80rpx"
                    lazy-load
                    radius="16rpx"
                    class="block-icon"
                ></u-image>
                <text class="grid-text">{{ listItem.name }}</text>
              </view>
            </view>
          </template>
        </up-card>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'; // 新增 computed
import { onShow } from '@dcloudio/uni-app';
import { useUserStore } from '@/pinia/user';
import globalConfig from '@/common/config/global';
import cache from '@/common/utils/cache';

interface MenuItem {
  id: number;
  name: string;
  subtitle?: string;
  type: number;
  sort: number;
  parentId: number;
  icon: string;
  jumpUrl: string;
  miniAppId?: string;
  badgeText?: string;
  badgeColor?: string;
  statisticCount: number | null;
  hasStatistic: boolean;
  extra: any;
  children: MenuItem[];
}

const loading = ref(true);
const userStore = useUserStore();
const moduleList = ref<MenuItem[]>([]);

// 计算属性:过滤出有子节点的父模块(children 存在且长度 > 0)
const filteredModuleList = computed(() => {
  return moduleList.value.filter(item => {
    // 确保 children 是数组且长度大于 0
    return Array.isArray(item.children) && item.children.length > 0;
  });
});

onShow(async () => {
  try {
    loading.value = true;
    const rawMenuData = userStore.moduleListInfo || cache.get(globalConfig.cache.moduleListKey);
    moduleList.value = rawMenuData || [];
    loading.value = false;
    console.log('原始菜单数据:', moduleList.value);
    console.log('过滤后有子节点的父模块:', filteredModuleList.value);
  } catch (error) {
    console.error('获取菜单数据失败:', error);
    moduleList.value = [];
    loading.value = false;
  }
});

const handleMenuClick = (item: MenuItem) => {
  if (!item.jumpUrl) {
    uni.showToast({ title: '暂无跳转链接', icon: 'none', duration: 2000 });
    return;
  }
  uni.navigateTo({
    url: item.jumpUrl,
    fail: (err) => {
      console.error('页面跳转失败:', err);
      uni.showToast({ title: '页面路径错误', icon: 'none', duration: 2000 });
    }
  });
};
</script>

<style scoped lang="scss">
.page-container {
  position: relative;
  min-height: 100vh;
}

.blue-decor-block {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 200px;
  background-color: #577ee3;
  z-index: 1;
}

.content-wrap {
  position: relative;
  z-index: 2;
  padding: 120px 0 0;
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}

.menu-card-wrap {
  width: 100%;
}

.card-container {
  --u-card-content-padding: 0;
  border-radius: 8rpx;
  overflow: hidden;
}

.card-content {
  display: flex;
  flex-wrap: wrap;
  gap: 20rpx;
}

.content-block {
  width: calc((100% - 3 * 20rpx) / 4);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 14rpx 0;
  border-radius: 8rpx;
  touch-action: manipulation;
  transition: background-color 0.2s;
}

.content-block:active {
  background-color: #e9ecef;
}

.block-icon {
  display: block;
  margin: 0 auto;
}

.grid-text {
  font-size: 26rpx;
  color: #333;
  text-align: center;
  margin-top: 10rpx;
  display: block;
}
</style>