index.vue 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  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">张小明</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.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 } from 'vue';
  31. import { getHomeApp, jumpToPage } from '@/api/app';
  32. interface AppPage {
  33. pageId: number;
  34. appName: string;
  35. }
  36. interface AppInfo {
  37. appId: number;
  38. appName: string;
  39. appPages: AppPage[];
  40. }
  41. const appList = ref<AppInfo[]>([]);
  42. // Icon Mapping
  43. const iconMap: Record<string, string> = {
  44. '立项': 'project_initiation.png',
  45. '项目变更': 'project_change.png',
  46. '进出场单': 'entry_exit_form.png',
  47. '文档': 'document.png',
  48. '申购': 'purchase_request.png',
  49. 'BOM变更': 'bom_change.png',
  50. '兑换': 'redeem.png',
  51. '兑换付款': 'redeem_payment.png',
  52. '配置品审批': 'configuration_approval.png',
  53. '投标入场': 'entry_approval.png',
  54. '配置付款': 'configuration_payment.png',
  55. '项目汇总': 'project_summary.png',
  56. '工单生成': 'work_order_service.png',
  57. };
  58. // Helper to get icon path
  59. const getIconPath = (name: string) => {
  60. const filename = iconMap[name] || 'document.png'; // Default icon
  61. return new URL(`/src/assets/images/home/${filename}`, import.meta.url).href;
  62. };
  63. const handleClick = async (item: AppPage) => {
  64. console.log('Clicked:', item.appName, item.pageId);
  65. try {
  66. const res = await jumpToPage(item.pageId);
  67. if (res.result) {
  68. window.open(res.result, '_blank');
  69. }
  70. } catch (error) {
  71. console.error('Jump to page failed:', error);
  72. }
  73. };
  74. const initData = async () => {
  75. try {
  76. const res = await getHomeApp();
  77. if (res.result) {
  78. appList.value = res.result;
  79. }
  80. } catch (error) {
  81. console.error('Failed to get home app list:', error);
  82. }
  83. };
  84. onMounted(() => {
  85. initData();
  86. });
  87. </script>
  88. <style scoped lang="scss">
  89. .home-page {
  90. min-height: 100vh;
  91. background: linear-gradient(180deg, #F5F7FA 0%, #FFFFFF 100%);
  92. overflow-y: scroll;
  93. }
  94. .header {
  95. position: fixed;
  96. top: 0;
  97. left: 0;
  98. right: 0;
  99. z-index: 100;
  100. display: flex;
  101. justify-content: space-between;
  102. align-items: center;
  103. padding: 20px 40px;
  104. background: linear-gradient(90deg, #1E88E5 0%, #42A5F5 100%);
  105. box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
  106. .logo {
  107. img {
  108. height: 32px;
  109. }
  110. }
  111. .user-info {
  112. display: flex;
  113. align-items: center;
  114. gap: 12px;
  115. color: white;
  116. cursor: pointer;
  117. .user-avatar {
  118. width: 32px;
  119. height: 32px;
  120. border-radius: 50%;
  121. border: 2px solid rgba(255, 255, 255, 0.3);
  122. }
  123. .username {
  124. font-size: 14px;
  125. font-weight: 500;
  126. }
  127. }
  128. }
  129. .main-content {
  130. max-width: 1400px;
  131. margin: 0 auto;
  132. padding: 92px 20px 40px 20px; // 72px header height + 20px spacing
  133. }
  134. .section {
  135. margin-bottom: 48px;
  136. .section-title {
  137. font-size: 18px;
  138. font-weight: 600;
  139. color: #333;
  140. margin-bottom: 24px;
  141. padding-left: 12px;
  142. border-left: 4px solid #1E88E5;
  143. }
  144. .icon-grid {
  145. display: grid;
  146. grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
  147. gap: 32px 24px;
  148. background: white;
  149. padding: 32px;
  150. border-radius: 12px;
  151. box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
  152. @media (max-width: 768px) {
  153. grid-template-columns: repeat(4, 1fr);
  154. gap: 24px 16px;
  155. padding: 24px;
  156. }
  157. @media (max-width: 480px) {
  158. grid-template-columns: repeat(3, 1fr);
  159. gap: 20px 12px;
  160. padding: 20px;
  161. }
  162. }
  163. .icon-item {
  164. display: flex;
  165. flex-direction: column;
  166. align-items: center;
  167. gap: 12px;
  168. cursor: pointer;
  169. transition: transform 0.2s ease;
  170. &:hover {
  171. transform: translateY(-4px);
  172. }
  173. .icon-wrapper {
  174. width: 64px;
  175. height: 64px;
  176. border-radius: 16px;
  177. display: flex;
  178. align-items: center;
  179. justify-content: center;
  180. transition: all 0.3s ease;
  181. img {
  182. width: 64px;
  183. height: 64px;
  184. object-fit: contain;
  185. }
  186. }
  187. .icon-label {
  188. font-size: 14px;
  189. color: #666;
  190. text-align: center;
  191. font-weight: 500;
  192. white-space: nowrap;
  193. }
  194. }
  195. }
  196. </style>