@ithinkdt/core
Version:
iThinkDT Core
225 lines (216 loc) • 8.73 kB
JavaScript
import { markRaw, reactive } from 'vue'
import { defineStore } from 'pinia'
import { promiseTimeout } from '@vueuse/core'
import { walkTree } from '@ithinkdt/common'
import { useFetch } from '../http'
export let useAuth
export function initAuthInfo({ login, logout, loadAuthInfo, changePwd, router, locale }) {
const fn = defineStore(`core:auth`, {
state: () => ({
error: false,
token: undefined,
app: undefined,
user: undefined,
modules: [],
fromModule: undefined,
}),
getters: {
logged(state) {
return !!state.token
},
moduleMap(state) {
const map = {}
walkTree(state.modules ?? [], (m, _i, parent) => {
m.parent = parent
if (m.type !== 'action') {
map[m.key] = m
if (m.path) {
map[m.path] = m
}
}
})
return map
},
menus: (() => {
function _walk(m) {
if (m?.hidden === true) return
if (m?.type === 'group' && m?.children?.length) {
const children = m.children.map((c) => _walk(c)).filter((m) => !!m)
return children.length > 0
? {
...m,
children: children,
}
: undefined
}
return m?.type === 'action' ? undefined : { ...m, children: undefined }
}
return (state) => {
const menus = state.modules?.map(_walk).filter((m) => !!m)
console.debug('[auth] menus', menus)
return menus
}
})(),
menuMap() {
const map = {}
walkTree(this.menus, (m) => {
map[m.key] = m
if (m.path) {
map[m.path] = m
}
})
return map
},
currentRouteInModule() {
const route = router.currentRoute.value
return !route?.meta?.activedModule?.trim() && !route?.meta?.activedModuleName?.trim()
},
currentModule() {
const route = router.currentRoute.value
if (!route) return
if (this.currentRouteInModule) {
return this.moduleMap[route.path] ? route.path : route.matched.at(-1)?.path ?? ''
}
return (
route.meta.activedModule?.trim() ||
router.resolve({ name: route.meta.activedModuleName?.trim() })?.path
)
},
activedModulePath() {
const route = router.currentRoute.value
if (!route) return []
let menu = this.moduleMap[this.currentModule] || this.moduleMap[this.fromModule]
const paths = []
if (
!this.currentRouteInModule ||
!(this.moduleMap[route.path] || this.moduleMap[route.matched.at(-1)?.path ?? ''])
) {
paths.push(
reactive({
key: route.path,
parentKey: menu?.key,
parent: menu,
label: route.path,
type: 'module',
path: route.path,
}),
)
}
while (menu) {
paths.unshift(menu)
menu = menu.parent
}
console.debug('[auth] activedModulePath', paths)
return paths
},
activedMenuPath() {
const paths = this.activedModulePath.map((m) => this.menuMap[m.key]).filter((m) => !!m)
console.debug('[auth] activedMenuPath', paths)
return paths
},
permissions(state) {
const permissions = {}
walkTree(state.modules, (m) => {
m.path && (permissions[m.path] = true)
if (m.actions) for (const r of m.actions) permissions[r] = true
})
console.debug('[auth] permissions', permissions)
return permissions
},
},
actions: (() => {
let _reject
let $load
const loginFn = useFetch(login)
const logoutFn = logout && useFetch(logout)
return {
async login(user) {
console.debug(`[login]: user [ ${user.username} ].`)
try {
const res = await loginFn(user)
console.debug('[login]: login success.', res)
return res
} catch (error) {
console.debug('[login]: login error.', error)
throw error
}
},
async load(force = false) {
if (!this.token) {
console.debug('[auth] load not set token!')
$load = undefined
throw new Error('未设置用户 token')
}
if ($load) {
if (!force) {
console.debug('[auth] load having $load, reuse.')
return $load
}
console.debug('[auth] load having $load, but force it.')
_reject(new Error('重新 load'))
}
const req = ($load = new Promise((resolve, reject) => {
_reject = reject
loadAuthInfo(this.token, locale.value).then(
(authInfo) => {
if (this.token === undefined || req !== $load) {
console.debug('[auth] load fetched auth info, but token or request expired.')
return
}
for (const key of Object.keys(authInfo)) {
this[key] = markRaw(authInfo[key])
}
console.debug('[auth] load fetched auth info.', authInfo)
resolve()
},
(error) => {
if (req === $load) {
if (error?.message === 'forbidden' || error?.message === 'timeout') {
this.error = error
resolve()
return
}
console.debug('[auth] load fetch auth error.', error)
this.error = error
$load = undefined
}
reject(error)
},
)
}))
return $load
},
async logout() {
console.debug('[auth] logout.')
const { token, user, app } = this.$state
await logoutFn?.({ username: user?.username, appCode: app?.appCode, token: token })
this.$reset()
this.$clear()
await promiseTimeout(300)
},
changePwd,
}
})(),
persist: {
storage: 'local',
version: 1,
excludes: ['app', 'modules'],
},
})
useAuth = fn
return fn
}
export function getFirstPageModule(modules) {
for (const m of modules ?? []) {
if (['module', 'lowcode', 'federate', 'external'].includes(m.type)) {
return m
}
if (m.type === 'group') {
const child = getFirstPageModule(m.children ?? [])
if (child) {
return child
}
}
}
return
}