Pārlūkot izejas kodu

factory-app 修复bug

Gogs 1 mēnesi atpakaļ
vecāks
revīzija
f7041023c0

+ 6 - 0
apps/factory-app/postcss.config.js

@@ -0,0 +1,6 @@
+export default {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+};

+ 4 - 1
apps/factory-app/src/App.vue

@@ -70,7 +70,10 @@ const showLayout = computed(() => {
 async function handleLogout() {
   await authStore.logout();
   message.success('已退出登录');
-  router.push('/login');
+  router.push({
+    name: 'Login',
+    query: { factory: authStore.currentFactoryId || '' }
+  });
 }
 </script>
 

+ 28 - 8
apps/factory-app/src/router/index.ts

@@ -77,33 +77,53 @@ const router = createRouter({
   routes
 });
 
-// 路由守卫:认证检查 + factory_id验证
+// 构建redirect路径(剔除factory参数,避免与外层factory重复;登录后守卫会自动重新注入)
+function buildRedirect(to: { path: string; query: Record<string, any> }): string {
+  const restQuery = { ...to.query };
+  delete restQuery.factory;
+  const keys = Object.keys(restQuery);
+  if (keys.length === 0) return to.path;
+  const qs = keys.map(k => `${k}=${encodeURIComponent(String(restQuery[k]))}`).join('&');
+  return `${to.path}?${qs}`;
+}
+
+// 路由守卫:认证检查 + factory_id验证 + 自动注入factory参数
 router.beforeEach((to, _from, next) => {
   const authStore = useAuthStore();
+  const factoryId = authStore.currentFactoryId;
 
   // 需要认证的路由
   if (to.meta.requiresAuth) {
     if (!authStore.isLoggedIn) {
       // 未登录时跳转登录页,携带redirect和factory参数
+      // factory优先取store(已登录过的工厂),回退取URL query(支持深链直达 /?factory=xxx)
+      const factoryForLogin = factoryId || (to.query.factory as string) || '';
       next({
         name: 'Login',
         query: {
-          redirect: to.fullPath,
-          factory: authStore.currentFactoryId || ''
+          redirect: buildRedirect(to),
+          ...(factoryForLogin ? { factory: factoryForLogin } : {})
         }
       });
-    } else if (!authStore.currentFactoryId) {
-      // 有Token但无factory_id,跳转登录重新选择工厂
+    } else if (!factoryId) {
+      // 有Token但无factory_id,跳转登录重新选择工厂(同样回退URL query)
+      const factoryForLogin = (to.query.factory as string) || '';
       next({
         name: 'Login',
-        query: { redirect: to.fullPath }
+        query: {
+          redirect: buildRedirect(to),
+          ...(factoryForLogin ? { factory: factoryForLogin } : {})
+        }
       });
+    } else if (to.query.factory !== factoryId) {
+      // 已登录:确保URL携带正确的factory参数(自动注入,用户无感,支持深链/新标签页)
+      next({ ...to, query: { ...to.query, factory: factoryId } });
     } else {
       next();
     }
   } else if (to.name === 'Login' && authStore.isLoggedIn) {
-    // 已登录时访问登录页,跳转首页
-    next({ name: 'Dashboard' });
+    // 已登录时访问登录页,跳转首页,携带factory
+    next({ name: 'Dashboard', query: { factory: factoryId || '' } });
   } else {
     next();
   }

+ 1 - 3
apps/factory-app/src/stores/auth.ts

@@ -66,11 +66,9 @@ export const useAuthStore = defineStore('auth', () => {
     } catch (e) { /* 忽略 */ }
     token.value = null;
     user.value = null;
-    currentFactoryId.value = null;
-
+    // 保留 currentFactoryId,便于重新登录同一工厂
     removeSessionStorage(STORAGE_KEYS.FACTORY_TOKEN);
     removeSessionStorage(STORAGE_KEYS.FACTORY_USER);
-    removeSessionStorage(STORAGE_KEYS.CURRENT_FACTORY_ID);
   }
 
   /**

+ 7 - 3
apps/factory-app/src/views/LoginView.vue

@@ -76,7 +76,7 @@ import { message } from 'ant-design-vue';
 import { UserOutlined, LockOutlined, LoginOutlined } from '@ant-design/icons-vue';
 import { useAuthStore } from '@/stores/auth';
 import { FactoryApi, createApiClient } from '@smartcut/api-client';
-import { STORAGE_KEYS } from '@smartcut/shared-utils';
+import { STORAGE_KEYS, getSessionStorage } from '@smartcut/shared-utils';
 import { getApiBaseUrl } from '@/apiConfig';
 import type { Factory } from '@smartcut/types';
 
@@ -89,8 +89,12 @@ const loadingFactories = ref(false);
 const factories = ref<Factory[]>([]);
 const selectedFactory = ref('');
 
-// 从Query参数获取factory_id
-const factoryId = ref(route.query.factory as string);
+// 从Query参数获取factory_id,回退到SessionStorage
+const factoryId = ref(
+  (route.query.factory as string) ||
+  getSessionStorage<string>(STORAGE_KEYS.CURRENT_FACTORY_ID) ||
+  ''
+);
 
 const formState = reactive({
   username: '',

+ 2 - 2
apps/factory-app/src/views/UsersView.vue

@@ -380,8 +380,8 @@ async function deleteUser(id: number) {
     await userApi.deleteUser(id);
     message.success('用户已删除');
     loadUsers();
-  } catch (error) {
-    message.error('删除用户失败');
+  } catch (error: any) {
+    message.error(error.response?.data?.msg || error.message || '删除用户失败');
   }
 }
 

+ 15 - 4
packages/api-client/src/client.ts

@@ -54,27 +54,38 @@ export function createApiClient(config: ApiClientConfig): AxiosInstance {
     (error) => Promise.reject(error)
   );
 
-  // 响应拦截器:Token续期 + 401处理(保持不变)
+  // 响应拦截器:Token续期 + 业务错误码处理 + 401处理
   instance.interceptors.response.use(
     (response) => {
       const newToken = response.headers['x-new-token'];
       if (newToken) {
         setSessionStorage(config.tokenKey, newToken);
       }
+      // 后端对业务错误返回 HTTP 200 + code!=0,此处统一转为 rejected,
+      // 让各视图的 catch 块能正确显示错误信息
+      const body = response.data;
+      if (body && typeof body === 'object' && typeof body.code === 'number' && body.code !== 0) {
+        const err = new Error(body.msg || '请求失败');
+        (err as any).code = body.code;
+        (err as any).response = response;
+        return Promise.reject(err);
+      }
       return response;
     },
     (error) => {
       if (error.response?.status === 401) {
-        // 401时清除Token和factory_id
+        // 401时仅清除Token,保留factory_id便于重新登录同一工厂
         removeSessionStorage(config.tokenKey);
-        removeSessionStorage(STORAGE_KEYS.CURRENT_FACTORY_ID);
+        const factoryId = getSessionStorage<string>(STORAGE_KEYS.CURRENT_FACTORY_ID);
 
         // P2: 保存当前路径,登录后返回原页面;redirect 仅允许相对路径,防开放重定向
         const currentPath = window.location.pathname + window.location.search;
         const isSafeRedirect = currentPath.startsWith('/') && !currentPath.startsWith('//') && !currentPath.startsWith('/login');
-        window.location.href = isSafeRedirect
+        const base = isSafeRedirect
           ? '/login?redirect=' + encodeURIComponent(currentPath)
           : '/login';
+        const factoryParam = factoryId ? '&factory=' + encodeURIComponent(factoryId) : '';
+        window.location.href = base + factoryParam;
       }
       return Promise.reject(error);
     }