index.vue 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. <template>
  2. <view class="icon-list" :class="mode" :style="{ '--columns': getColumnCount() }">
  3. <view v-for="(item, index) in displayIcons" :key="index" class="icon-item" @click="_handleClick(item.urlMap?.internal_url)">
  4. <view class="icon-wrapper">
  5. <image :src="item.image || `http://iph.href.lu/48x48?text=${index}`" :style="{ borderRadius: `${radius}px` }" mode="aspectFill" />
  6. </view>
  7. <view class="icon-text">{{ item.text }}</view>
  8. </view>
  9. </view>
  10. </template>
  11. <script setup>
  12. import { computed } from 'vue';
  13. const props = defineProps({
  14. icons: {
  15. type: Array,
  16. default: () => [1, 2, 3, 4],
  17. },
  18. mode: {
  19. type: String,
  20. default: 'row4',
  21. validator: (value) => ['row4', 'row8', 'row5', 'row10'].includes(value),
  22. },
  23. radius: {
  24. type: Number,
  25. default: 0,
  26. },
  27. small: {
  28. type: Boolean,
  29. default: false,
  30. },
  31. });
  32. const getColumnCount = () => {
  33. switch (props.mode) {
  34. case 'row4':
  35. case 'row8':
  36. return 4;
  37. case 'row5':
  38. case 'row10':
  39. return 5;
  40. default:
  41. return 4;
  42. }
  43. };
  44. const getMaxItems = () => {
  45. switch (props.mode) {
  46. case 'row4':
  47. return 4;
  48. case 'row8':
  49. return 8;
  50. case 'row5':
  51. return 5;
  52. case 'row10':
  53. return 10;
  54. default:
  55. return 4;
  56. }
  57. };
  58. const _handleClick = (url) => {
  59. if (url) {
  60. uni.navigateTo({
  61. url,
  62. fail: (err) => {
  63. uni.switchTab({
  64. url,
  65. });
  66. },
  67. });
  68. }
  69. };
  70. const displayIcons = computed(() => {
  71. return props.icons.slice(0, getMaxItems());
  72. });
  73. const iconSize = computed(() => (props.small ? '31px' : '44px'));
  74. </script>
  75. <style lang="less" scoped>
  76. .navigator {
  77. width: 100%;
  78. height: 100%;
  79. display: flex;
  80. flex-direction: column;
  81. align-items: center;
  82. justify-content: center;
  83. }
  84. .icon-list {
  85. display: grid;
  86. grid-template-columns: repeat(var(--columns), 1fr);
  87. gap: 16rpx;
  88. width: 100%;
  89. padding: 16px 8px;
  90. box-sizing: border-box;
  91. background-color: #fff;
  92. border-radius: 8px;
  93. .icon-item {
  94. display: flex;
  95. flex-direction: column;
  96. gap: 4px;
  97. align-items: center;
  98. .icon-wrapper {
  99. width: v-bind('iconSize');
  100. height: v-bind('iconSize');
  101. overflow: hidden;
  102. image {
  103. width: 100%;
  104. height: 100%;
  105. object-fit: cover;
  106. }
  107. }
  108. .icon-text {
  109. width: 100%;
  110. overflow: hidden;
  111. color: #333;
  112. font-size: 12px;
  113. white-space: nowrap;
  114. text-align: center;
  115. text-overflow: ellipsis;
  116. }
  117. }
  118. }
  119. .row4 {
  120. grid-template-rows: 1fr;
  121. }
  122. .row8 {
  123. // grid-template-rows: repeat(2, 1fr);
  124. }
  125. .row5 {
  126. grid-template-rows: 1fr;
  127. }
  128. .row10 {
  129. grid-template-rows: repeat(2, 1fr);
  130. }
  131. </style>