Bladeren bron

refactor: реорганизация API модулей

- Создание отдельных composables для каждого API модуля

- Обновление компонентов для использования новых API модулей

- Улучшение типизации и обработки ошибок
horanchikk 3 maanden geleden
bovenliggende
commit
a9474cb5bc

+ 15 - 4
components/Base/SideBar/index.vue

@@ -117,6 +117,8 @@
 import { useSwipe } from '@vueuse/core'
 import { useSideBar } from '~/store/useSideBar'
 import { useUser } from '~/store/useUser'
+import { useUserApi } from '~/composables/useUserApi'
+import { useLogger } from '~/composables/useLogger'
 
 const { $api } = useNuxtApp()
 const store = useSideBar()
@@ -128,6 +130,8 @@ const { direction } = useSwipe(sideBarEl, {
   },
 })
 const user = useUser()
+const { user: userApi } = useUserApi()
+const log = useLogger('SideBar')
 
 const links = ref<{
   name: string
@@ -177,8 +181,15 @@ const links = ref<{
 ])
 const info = ref()
 
-onMounted(
-  async () =>
-    (info.value = await $api.user.getInfo(user.$state.data.access_token)),
-)
+onMounted(async () => {
+  try {
+    if (!userApi || !user.data.access_token) {
+      log.error('Не удалось получить данные пользователя')
+      return
+    }
+    info.value = await userApi.getInfo(user.data.access_token)
+  } catch (e) {
+    log.error('Ошибка при получении информации о пользователе:', e)
+  }
+})
 </script>

+ 14 - 3
components/Wall/Post.vue

@@ -1,10 +1,21 @@
 <template>
   <div class="flex flex-col gap-2 text-foreground bg-background-100 rounded-lg overflow-hidden">
-    <img
+    <NuxtImg
       :src="$props.image"
       :alt="'Возникла проблема с загрузкой фото, попробуйте позже.'"
-      class="h-[250px] object-cover object-center"
-    >
+      class="h-[200px] object-cover object-center"
+      :height="200"
+      :custom="true"
+      :quality="10"
+      v-slot="{ src, isLoaded, imgAttrs }"
+    > 
+      <img
+        v-if="isLoaded"
+        v-bind="imgAttrs"
+        :src="src"
+      >
+      <div v-else class="w-full h-[200px] loading" />
+    </NuxtImg>
     <div class="px-2 leading-none text-xl font-semibold">
       {{ $props.title }}
     </div>

+ 18 - 0
composables/useApi.ts

@@ -0,0 +1,18 @@
+import { useNuxtApp } from '#app'
+import type { IApiInstance } from '~/plugins/api.client'
+
+export function useApi() {
+  const { $api } = useNuxtApp()
+
+  function checkModule<T extends keyof IApiInstance>(moduleName: T): IApiInstance[T] {
+    if (!$api?.[moduleName]) {
+      throw new Error(`Модуль ${moduleName} не инициализирован`)
+    }
+    return $api[moduleName]
+  }
+
+  return {
+    api: $api,
+    checkModule,
+  }
+} 

+ 18 - 0
composables/useBlogApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useBlogApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('BlogApi')
+
+  try {
+    const blogModule = checkModule('blog')
+    return {
+      blog: blogModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации BlogApi:', e)
+    return {
+      blog: null,
+    }
+  }
+} 

+ 18 - 0
composables/useBranchApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useBranchApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('BranchApi')
+
+  try {
+    const branchModule = checkModule('branch')
+    return {
+      branch: branchModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации BranchApi:', e)
+    return {
+      branch: null,
+    }
+  }
+} 

+ 18 - 0
composables/useContactsApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useContactsApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('ContactsApi')
+
+  try {
+    const contactsModule = checkModule('contacts')
+    return {
+      contacts: contactsModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации ContactsApi:', e)
+    return {
+      contacts: null,
+    }
+  }
+} 

+ 18 - 0
composables/useGalleryApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useGalleryApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('GalleryApi')
+
+  try {
+    const galleryModule = checkModule('gallery')
+    return {
+      gallery: galleryModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации GalleryApi:', e)
+    return {
+      gallery: null,
+    }
+  }
+} 

+ 18 - 0
composables/useNewsApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useNewsApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('NewsApi')
+
+  try {
+    const newsModule = checkModule('news')
+    return {
+      news: newsModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации NewsApi:', e)
+    return {
+      news: null,
+    }
+  }
+} 

+ 18 - 0
composables/useTimetableApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useTimetableApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('TimetableApi')
+
+  try {
+    const timetableModule = checkModule('timetable')
+    return {
+      timetable: timetableModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации TimetableApi:', e)
+    return {
+      timetable: null,
+    }
+  }
+} 

+ 18 - 0
composables/useUserApi.ts

@@ -0,0 +1,18 @@
+import { useApi } from './useApi'
+
+export function useUserApi() {
+  const { checkModule } = useApi()
+  const log = useLogger('UserApi')
+
+  try {
+    const userModule = checkModule('user')
+    return {
+      user: userModule,
+    }
+  } catch (e) {
+    log.error('Ошибка инициализации UserApi:', e)
+    return {
+      user: null,
+    }
+  }
+} 

+ 59 - 23
pages/gallery/index.vue

@@ -1,28 +1,47 @@
 <template>
   <div
     v-if="albums"
-    class="w-screen h-full flex flex-col gap-5 mt-3"
-  >
-    <NuxtLink
-      v-for="album in albums"
-      :key="album.id"
-      :to="`/gallery/${album.id}`"
-      class="bg-background-100 bg-opacity-80 rounded-md mx-1 select-none hover:opacity-60 duration-150 transition-all"
+    class="w-screen flex flex-col gap-5 pt-3"
+  >  
+    <DynamicScroller
+      :items="albums"
+      :min-item-size="230"
+      class="h-full"
+      page-mode
     >
-      <img
-        class="w-full rounded-t-md"
-        :src="album.preview"
-        alt="Фото"
-      >
-      <p
-        class="text-center text-sm font-semibold my-3"
-        v-text="album.title"
-      />
-      <p
-        class="text-xs text-right mb-2 pr-2"
-        v-text="album.date"
-      />
-    </NuxtLink>
+      <template v-slot="{ item, index, active }">
+        <DynamicScrollerItem
+          :item="item"
+          :active="active"
+          :data-index="index"
+          :min-item-size="500"
+          :size-dependencies="[
+            item.title
+          ]"
+        >
+          <div class="bg-background-100 rounded-md mx-2">
+            <NuxtLink
+              :to="`/gallery/${item.id}`"
+            >
+              <NuxtImg
+                class="w-full rounded-t-md min-h-[300px] max-h-[300px] object-cover"
+                :src="item.preview"
+                alt="Фото"
+              />
+              <p
+                class="text-center text-sm font-semibold my-3 px-3"
+                v-text="item.title"
+              />
+              <p
+                class="text-xs text-right mb-2 pr-2 pb-1"
+                v-text="item.date"
+              />
+            </NuxtLink>
+          </div>
+          <div class="h-[16px]" />
+        </DynamicScrollerItem>
+      </template>
+    </DynamicScroller> 
   </div>
   <div
     v-else
@@ -33,12 +52,29 @@
 </template>
 
 <script setup lang="ts">
+import { DynamicScroller, DynamicScrollerItem } from 'vue-virtual-scroller'
+import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
+import { useGalleryApi } from '~/composables/useGalleryApi'
+import { useLogger } from '~/composables/useLogger'
+
 definePageMeta({
   name: 'Галерея',
   middleware: ['user-only'],
 })
 
 const albums = ref<object>()
-const { $api } = useNuxtApp()
-onMounted(async () => albums.value = await $api.gallery.getAlbums())
+const { gallery } = useGalleryApi()
+const log = useLogger('Gallery')
+
+onMounted(async () => {
+  try {
+    if (!gallery) {
+      log.error('Модуль галереи не инициализирован')
+      return
+    }
+    albums.value = await gallery.getAlbums()
+  } catch (e) {
+    log.error('Ошибка при получении альбомов:', e)
+  }
+})
 </script>

+ 2 - 11
pages/news/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="w-full flex flex-col p-2 gap-5">
+  <div class="w-screen flex flex-col p-2 gap-5">
     <div
       v-if="!newsList"
       class="w-full h-full"
@@ -12,15 +12,6 @@
       />
     </div>
     <template v-else>
-      <!-- <WallPost
-        v-for="news in newsList"
-        :key="news.id"
-        :image="news.preview.length > 0 ? news.preview : `/nophoto.png`"
-        :title="news.title"
-        :description="news.description"
-        :date="news.date"
-        @click="navigateTo(`/news/${news.id}`)"
-      /> -->
       <DynamicScroller
         :items="newsList"
         :min-item-size="230"
@@ -38,7 +29,7 @@
           >
             <WallPost
               :key="item.id"
-              :image="item.preview.length > 0 ? item.preview : `/nophoto.png`"
+              :image="item.preview.length > 0 || item.preview.includes('base64') ? item.preview : `/nophoto.png`"
               :title="item.title"
               :description="item.description"
               :date="item.date"