登录流程:
- 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 页面
70 lines
1.8 KiB
JavaScript
70 lines
1.8 KiB
JavaScript
import { defineStore } from 'pinia'
|
|
import { ref, computed } from 'vue'
|
|
import { login as loginApi, logout as logoutApi, getMe } from '@/api/auth.js'
|
|
|
|
export const useUserStore = defineStore('user', () => {
|
|
const token = ref(localStorage.getItem('token') || '')
|
|
const userInfo = ref(null)
|
|
const permissions = ref([])
|
|
const menus = ref([])
|
|
|
|
// Getters
|
|
const isLoggedIn = computed(() => !!token.value)
|
|
const isSuper = computed(() => userInfo.value?.is_super === true || userInfo.value?.is_super === 1)
|
|
|
|
// Actions
|
|
async function login(credentials) {
|
|
const res = await loginApi(credentials)
|
|
const { token: newToken } = res.data
|
|
token.value = newToken
|
|
// Only store token here — userInfo, permissions, menus
|
|
// will be loaded by router guard via getInfo() which also
|
|
// triggers dynamic route generation.
|
|
localStorage.setItem('token', newToken)
|
|
return res
|
|
}
|
|
|
|
async function getInfo() {
|
|
const res = await getMe()
|
|
const data = res.data
|
|
// /auth/me returns flat structure: { id, username, ..., permissions, menus }
|
|
userInfo.value = data
|
|
permissions.value = data.permissions || []
|
|
menus.value = data.menus || []
|
|
return res
|
|
}
|
|
|
|
async function logout() {
|
|
try {
|
|
await logoutApi()
|
|
} catch {
|
|
// Ignore logout API errors — clear local state regardless
|
|
} finally {
|
|
token.value = ''
|
|
userInfo.value = null
|
|
permissions.value = []
|
|
menus.value = []
|
|
localStorage.removeItem('token')
|
|
localStorage.removeItem('userInfo')
|
|
}
|
|
}
|
|
|
|
function hasPermission(permCode) {
|
|
if (isSuper.value) return true
|
|
return permissions.value.includes(permCode)
|
|
}
|
|
|
|
return {
|
|
token,
|
|
userInfo,
|
|
permissions,
|
|
menus,
|
|
isLoggedIn,
|
|
isSuper,
|
|
login,
|
|
getInfo,
|
|
logout,
|
|
hasPermission
|
|
}
|
|
})
|