Linhanmic 1 месяц назад
Родитель
Сommit
c0da53217f

+ 22 - 9
src/components/AdminMenu.vue

@@ -1,11 +1,11 @@
 <template>
-    <div class="relative" @mouseleave="isOpen = false">
-      <div @mouseenter="isOpen = true" class="flex items-center cursor-pointer">
+    <div class="relative">
+      <div @click="toggleMenu" class="flex items-center cursor-pointer">
         <img src="@/assets/admin-avatar.png" alt="Admin" class="w-8 h-8 rounded-full mr-2" />
         <span class="text-gray-800">管理员</span>
       </div>
       <div v-if="isOpen" class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10">
-        <router-link to="/admin/change-password" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">修改密码</router-link>
+        <router-link to="/change-password" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">修改密码</router-link>
         <a @click="logout" class="block px-4 py-2 text-gray-800 hover:bg-gray-100 cursor-pointer">登出</a>
       </div>
     </div>
@@ -13,15 +13,28 @@
   
   <script setup>
   import { ref } from 'vue';
-  import { useStore } from 'vuex';
+  import { useAuthStore } from '@/store/modules/auth';
   import { useRouter } from 'vue-router';
   
-  const store = useStore();
+  const authStore = useAuthStore();
   const router = useRouter();
   const isOpen = ref(false);
-  
-  const logout = () => {
-    store.dispatch('auth/logout');
-    router.push('/');
+
+  const toggleMenu = () => {
+    isOpen.value = !isOpen.value;
   };
+
+  const closeMenu = () => {
+    isOpen.value = false;
+  };
+
+  const logout = async () => {
+  try {
+    await authStore.logout(); // 使用 Pinia 的 action
+    router.push('/');
+    closeMenu();
+  } catch (error) {
+    console.error('Logout failed:', error);
+  }
+};
   </script>

+ 6 - 3
src/components/Pagination.vue

@@ -41,8 +41,11 @@ const props = defineProps({
 
 const emit = defineEmits(['page-change']);
 
-const totalPages = computed(() => Math.ceil(props.totalItems / props.itemsPerPage));
-// console.log("This!", totalPages);
+const totalPages = computed(() => {
+  const totalItems = props.totalItems || 1; // 如果 totalItems 为空或为 0,默认为 1
+  const itemsPerPage = props.itemsPerPage || 1; // 如果 itemsPerPage 为空或为 0,默认为 1
+  return Math.ceil(totalItems / itemsPerPage);
+});
 
 const totalPagesDisplay = computed(() => totalPages.value == NaN ? '1' : totalPages.value);
 
@@ -51,4 +54,4 @@ const onPageChange = (page) => {
     emit('page-change', page);
   }
 };
-</script>
+</script>

+ 60 - 40
src/components/UserMenu.vue

@@ -1,43 +1,63 @@
 <template>
-    <div class="relative" @mouseleave="isOpen = false">
-      <div @mouseenter="isOpen = true" class="flex items-center cursor-pointer">
-        <img :src="user.profileImage" :alt="user.name" class="w-8 h-8 rounded-full mr-2" />
-        <span class="text-gray-800">{{ user.name }}</span>
-      </div>
+  <div class="relative">
+    <div @click="toggleMenu" class="flex items-center cursor-pointer">
+      <img :src="user.profileImage" :alt="user.name" class="w-8 h-8 rounded-full mr-2" />
+      <span class="text-gray-800">{{ user.name }}</span>
+    </div>
+    <transition name="fade">
       <div v-if="isOpen" class="absolute right-0 mt-2 w-48 bg-white rounded-md shadow-lg z-10">
-        <router-link to="/profile" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">个人资料</router-link>
-        <router-link to="/change-password" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">修改密码</router-link>
-        <a @click="logout" class="block px-4 py-2 text-gray-800 hover:bg-gray-100 cursor-pointer">登出</a>
+        <router-link @click="closeMenu" to="/profile" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">个人资料</router-link>
+        <router-link @click="closeMenu" to="/change-password" class="block px-4 py-2 text-gray-800 hover:bg-gray-100">修改密码</router-link>
+        <a @click="logoutAndCloseMenu" class="block px-4 py-2 text-gray-800 hover:bg-gray-100 cursor-pointer">登出</a>
       </div>
-    </div>
-  </template>
-  
-  <script setup>
-  import { ref } from 'vue';
-  import { useStore } from 'vuex';
-  import { useRouter } from 'vue-router';
-  
-  const store = useStore();
-  const router = useRouter();
-  const isOpen = ref(false);
-  
-  const user = store.getters['auth/user'];
-  
-  const logout = () => {
-    store.dispatch('auth/logout');
-    router.push('/');
-  };
-  </script>
-
-  <style scoped>
-  .relative {
-    position: relative;
-  }
-  .absolute {
-    position: absolute; /* 下拉菜单绝对定位 */
-    top: 100%; /* 确保下拉菜单在 header 下面 */
-    right: 0; /* 右对齐 */
-    z-index: 10; /* 确保下拉菜单在其他元素之上 */
-  }
-  
-  </style>
+    </transition>
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import { useStore } from 'vuex';
+import { useRouter } from 'vue-router';
+
+const store = useStore();
+const router = useRouter();
+const isOpen = ref(false);
+
+const user = store.getters['auth/user'];
+
+const toggleMenu = () => {
+  isOpen.value = !isOpen.value;
+};
+
+const closeMenu = () => {
+  isOpen.value = false;
+};
+
+const logoutAndCloseMenu = () => {
+  store.dispatch('auth/logout');
+  router.push('/');
+  closeMenu();
+};
+</script>
+
+<style scoped>
+.relative {
+  position: relative;
+}
+.absolute {
+  position: absolute;
+  top: 100%;
+  right: 0;
+  z-index: 10;
+}
+
+.fade-enter-active,
+.fade-leave-active {
+  transition: opacity 0.3s ease;
+}
+
+.fade-enter-from,
+.fade-leave-to {
+  opacity: 0;
+}
+</style>

+ 4 - 2
src/router/index.js

@@ -12,6 +12,7 @@ import PaperUpload from '../views/PaperUpload.vue'
 import SharedLibrary from '../views/SharedLibrary.vue'
 import AdminAccount from '../views/AdminAccount.vue'
 import AdminFileManagement from '../views/AdminFileManagement.vue'
+import { useAuthStore } from '@/store/modules/auth';
 
 const routes = [
   {
@@ -96,8 +97,9 @@ const router = createRouter({
 
 // Navigation guard
 router.beforeEach((to, from, next) => {
-  const isAuthenticated = false;//authStore.isAuthenticated; // This should be replaced with actual auth check
-  const isAdmin = false;//authStore.isAdmin; // This should be replaced with actual admin check
+  const authStore = useAuthStore();
+  const isAuthenticated = authStore.isAuthenticated; // This should be replaced with actual auth check
+  const isAdmin = authStore.isAdmin; // This should be replaced with actual admin check
 
   if (to.matched.some(record => record.meta.requiresAuth)) {
     if (!isAuthenticated) {

+ 2 - 1
src/utils/request.js

@@ -1,4 +1,5 @@
 import axios from 'axios'
+import { useAuthStore } from '@/store/modules/auth';
 
 const request = axios.create({
   // baseURL: import.meta.env.VITE_APP_API_BASE_URL, // 使用环境变量设置基础URL
@@ -8,7 +9,7 @@ const request = axios.create({
 
 request.interceptors.request.use(
   (config) => {
-    const token = localStorage.getItem('token')
+    const token = localStorage.getItem("token");
     if (token) {
       config.headers['token'] = token // 修改为 'token',而不是 'Authorization'
     }

+ 1 - 1
src/views/ChangePassword.vue

@@ -66,7 +66,7 @@ export default {
           newPassword: newPassword.value
         });
         alert('密码修改成功');
-        router.push('/profile');
+        router.push('/login');
       } catch (error) {
         alert('修改密码失败:' + error.message);
       }

+ 4 - 2
src/views/Login.vue

@@ -50,8 +50,10 @@ export default {
     const handleLogin = async () => {
       try {
         const response = await userLoginPost({ username: username.value, password: password.value });
-        localStorage.setItem('token', response.token);
-        authStore.setUser(response.data);
+        if(response != null && response.data != null){
+          localStorage.setItem("token", response.data.token);
+          authStore.setUser(response.data);
+        }
         if (authStore.role === 'admin') {
           router.push('/admin');
         } else {

+ 10 - 5
src/views/SharedLibrary.vue

@@ -44,7 +44,7 @@
 </template>
 
 <script>
-import { ref, computed} from 'vue';
+import { ref, computed, watchEffect} from 'vue';
 import { useAuthStore } from '@/store/modules/auth';
 import FileItem from '@/components/FileItem.vue';
 import Pagination from '@/components/Pagination.vue';
@@ -75,10 +75,11 @@ export default {
     );
     
     const privateFiles = computed(() => 
-      isLoggedIn.value ? resources.value.filter(file => file.visibility === 'private') : []
+      resources.value
+      //isLoggedIn.value ? resources.value.filter(file => file.visibility === 'private') : []
     );
 
-    const totalFiles = computed(() => resources.value.length);
+    const totalFiles = computed(() => resources.value.total);
 
     const handleSearch = () => {
       fetchResources();
@@ -102,7 +103,8 @@ export default {
     const fetchResources = async () => {
       try {
         const response = await getResourcesPage(currentPage.value, pageSize.value, searchQuery.value);
-        resources.value = response.data;
+        resources.value = response.data.records;
+        totalFiles.value = response.data.total;
       } catch (error) {
         console.error('Failed to fetch resources:', error);
       }
@@ -117,7 +119,9 @@ export default {
       }
     };
 
-  
+    watchEffect(() => {
+      fetchResources();
+    });
 
     return {
       searchQuery,
@@ -136,6 +140,7 @@ export default {
     };
   }
 };
+
 </script>