Files
yuezi-saas/frontend/src/utils/request.js
T
li 4a68855a88 fix: 全链路运行时修复 — 登录/路由/菜单/门店CRUD
登录流程:
- login() 不再设置 userInfo,由 router guard 触发 getInfo() + 路由生成
- validate() 从 callback 改为 Promise 式,修复异步链断裂
- getInfo() 适配 /auth/me 扁平响应结构

动态路由:
- import.meta.glob 替代 Vite 不支持的多级动态 import
- 目录菜单检测: !component || 'Layout' 而非仅 'Layout'
- resolveComponent 支持多种 key 格式 + 后缀匹配兜底

侧边栏:
- menu.title → menu.name (匹配后端字段)
- 空 path 目录节点加 fallback index

门店管理:
- StoreController: region_id 改 nullable, 新增 contact/capacity/description 验证
- has('status') → filled('status') 修复空字符串导致列表为空
- capacity null → 0 兜底,避免 NOT NULL 约束报错
- 前端: capacity 初始值 '' → 1, handleSubmit 过滤冗余字段
- region 列渲染 region.name 而非 [object Object]
- 新增 stores 表迁移: contact/capacity/description 列

种子数据:
- DatabaseSeeder 改用 InitSeeder
- createMenus() 补全 52 个叶子页面菜单 (13 模块)
- 目录菜单添加 path 属性

其他:
- vite proxy 端口 8000 → 8001
- request.js 添加 Accept: application/json
- UserStatus 枚举值对齐 (Active=1, Disabled=0)
- 新增 positions/index.vue 页面
2026-03-13 23:53:30 +08:00

85 lines
2.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import axios from 'axios'
import { ElMessage } from 'element-plus'
const request = axios.create({
baseURL: '/api/v1',
timeout: 15000,
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
// Request interceptor — inject Authorization token
request.interceptors.request.use(
(config) => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = `Bearer ${token}`
}
return config
},
(error) => {
return Promise.reject(error)
}
)
// Response interceptor — handle business codes and auth errors
request.interceptors.response.use(
(response) => {
const res = response.data
// Success: code === 0
if (res.code === 0) {
return res
}
// Auth error: 40100–40199 → clear token and redirect to login
if (res.code >= 40100 && res.code <= 40199) {
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
ElMessage.error('登录已过期,请重新登录')
// Avoid circular import by using window.location
window.location.href = '/login'
return Promise.reject(new Error(res.message || '未授权'))
}
// Other business error
ElMessage.error(res.message || '请求失败')
return Promise.reject(new Error(res.message || '请求失败'))
},
(error) => {
// Network / HTTP errors
if (error.response) {
const status = error.response.status
if (status === 401) {
localStorage.removeItem('token')
localStorage.removeItem('userInfo')
ElMessage.error('登录已过期,请重新登录')
window.location.href = '/login'
return Promise.reject(error)
}
if (status === 403) {
ElMessage.error('无权限访问')
return Promise.reject(error)
}
if (status === 404) {
ElMessage.error('请求的资源不存在')
return Promise.reject(error)
}
if (status >= 500) {
ElMessage.error('服务器内部错误,请稍后重试')
return Promise.reject(error)
}
}
if (error.code === 'ECONNABORTED' || error.message.includes('timeout')) {
ElMessage.error('请求超时,请检查网络连接')
return Promise.reject(error)
}
ElMessage.error(error.message || '网络错误')
return Promise.reject(error)
}
)
export default request