index.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. <template>
  2. <div class="home-page">
  3. <!-- Header -->
  4. <div class="header">
  5. <div class="logo">
  6. <img src="@/assets/images/home/logo@2x.png" alt="STORLEAD" />
  7. </div>
  8. <div class="user-info">
  9. <img src="@/assets/images/home/Group@2x.png" alt="User" class="user-avatar" />
  10. <span class="username">{{ userName }}</span>
  11. </div>
  12. </div>
  13. <!-- Main Content -->
  14. <div class="main-content">
  15. <div class="section" v-for="app in appList" :key="app.appId">
  16. <h2 class="section-title">{{ app.appName }}</h2>
  17. <div class="icon-grid">
  18. <div class="icon-item" v-for="page in app.appPages" :key="page.pageId" @click="handleClick(page)">
  19. <div class="icon-wrapper">
  20. <img :src="getIconPath(page.pageIcon || page.appName)" :alt="page.appName" />
  21. </div>
  22. <span class="icon-label">{{ page.appName }}</span>
  23. </div>
  24. </div>
  25. </div>
  26. </div>
  27. </div>
  28. </template>
  29. <script setup name="Index" lang="ts">
  30. import { ref, onMounted, computed } from 'vue';
  31. import { storeToRefs } from 'pinia';
  32. import { getHomeApp, jumpToPage } from '@/api/app';
  33. import useUserStore from '@/store/modules/user';
  34. interface AppPage {
  35. pageId: number;
  36. appName: string;
  37. pageIcon?: string;
  38. }
  39. interface AppInfo {
  40. appId: number;
  41. appName: string;
  42. appPages: AppPage[];
  43. }
  44. const appList = ref<AppInfo[]>([]);
  45. const userStore = useUserStore();
  46. const { userInfo, nickname } = storeToRefs(userStore);
  47. // 获取用户姓名,优先显示真实姓名,如果没有则显示用户名
  48. const userName = computed(() => {
  49. return userInfo.value?.realName || nickname.value || '用户';
  50. });
  51. // Helper to get icon path
  52. const getIconPath = (name: string) => {
  53. if (!name) {
  54. return '';
  55. }
  56. console.log('name', name);
  57. return new URL(`/src/assets/homeIcon/${name}@2x.png`, import.meta.url).href;
  58. };
  59. const handleClick = async (item: AppPage) => {
  60. console.log('Clicked:', item.appName, item.pageId);
  61. try {
  62. const res = await jumpToPage(item.pageId);
  63. if (res.result) {
  64. window.open(res.result, '_blank');
  65. }
  66. } catch (error) {
  67. console.error('Jump to page failed:', error);
  68. }
  69. };
  70. const initData = async () => {
  71. try {
  72. const res = await getHomeApp();
  73. if (res.result) {
  74. appList.value = res.result;
  75. }
  76. } catch (error) {
  77. console.error('Failed to get home app list:', error);
  78. }
  79. };
  80. onMounted(() => {
  81. initData();
  82. });
  83. </script>
  84. <style scoped lang="scss">
  85. .home-page {
  86. min-height: 100vh;
  87. background: linear-gradient(180deg, #F5F7FA 0%, #FFFFFF 100%);
  88. overflow-y: scroll;
  89. }
  90. .header {
  91. position: fixed;
  92. top: 0;
  93. left: 0;
  94. right: 0;
  95. z-index: 100;
  96. display: flex;
  97. justify-content: space-between;
  98. align-items: center;
  99. padding: 20px 40px;
  100. background: linear-gradient(90deg, #1E88E5 0%, #42A5F5 100%);
  101. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  102. .logo {
  103. img {
  104. height: 32px;
  105. }
  106. }
  107. .user-info {
  108. display: flex;
  109. align-items: center;
  110. gap: 12px;
  111. color: white;
  112. cursor: pointer;
  113. .user-avatar {
  114. width: 32px;
  115. height: 32px;
  116. border-radius: 50%;
  117. border: 2px solid rgba(255, 255, 255, 0.3);
  118. }
  119. .username {
  120. font-size: 14px;
  121. font-weight: 500;
  122. }
  123. }
  124. }
  125. .main-content {
  126. max-width: 1400px;
  127. margin: 0 auto;
  128. padding: 92px 20px 40px 20px; // 72px header height + 20px spacing
  129. }
  130. .section {
  131. margin-bottom: 48px;
  132. .section-title {
  133. font-size: 18px;
  134. font-weight: 600;
  135. color: #333;
  136. margin-bottom: 24px;
  137. padding-left: 12px;
  138. border-left: 4px solid #1E88E5;
  139. }
  140. .icon-grid {
  141. display: grid;
  142. grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  143. gap: 32px 24px;
  144. background: white;
  145. padding: 32px;
  146. border-radius: 12px;
  147. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
  148. @media (max-width: 768px) {
  149. grid-template-columns: repeat(4, 1fr);
  150. gap: 24px 16px;
  151. padding: 24px;
  152. }
  153. @media (max-width: 480px) {
  154. grid-template-columns: repeat(3, 1fr);
  155. gap: 20px 12px;
  156. padding: 20px;
  157. }
  158. }
  159. .icon-item {
  160. display: flex;
  161. flex-direction: column;
  162. align-items: center;
  163. gap: 12px;
  164. cursor: pointer;
  165. transition: transform 0.2s ease;
  166. &:hover {
  167. transform: translateY(-4px);
  168. }
  169. .icon-wrapper {
  170. width: 64px;
  171. height: 64px;
  172. border-radius: 16px;
  173. display: flex;
  174. align-items: center;
  175. justify-content: center;
  176. transition: all 0.3s ease;
  177. img {
  178. width: 64px;
  179. height: 64px;
  180. object-fit: contain;
  181. }
  182. }
  183. .icon-label {
  184. font-size: 14px;
  185. color: #666;
  186. text-align: center;
  187. font-weight: 500;
  188. white-space: nowrap;
  189. }
  190. }
  191. }
  192. </style>