@ithinkdt/cloud
Version:
iThinkDT Cloud
267 lines (234 loc) • 8.41 kB
JavaScript
import { watch, ref, unref, shallowReactive, reactive, isRef } from 'vue'
import { $fetch, useCoreCtx } from '@ithinkdt/core'
import { CTX } from './ctx'
export function useModuleTree({
appCode,
immediate = true,
includeApp = true,
includeEmptyApp = true,
includeAction = true,
} = {}) {
const { saveIcon } = useCoreCtx()
const moduleMap = shallowReactive(new Map())
let allApp
const result = ref([])
const loading = ref(false)
const exec = (appCode) =>
Promise.all([$fetch.get(`${CTX.SYS}cer/module/list/all`), $fetch.get(`${CTX.SYS}pub/app/all`)]).then(
([modules, apps]) => {
allApp = apps
moduleMap.clear()
const map = new Map(modules.map((item) => [item.id, item]))
if (appCode?.trim()) {
apps = apps.filter((app) => app.appCode === appCode)
} else if (!includeEmptyApp) {
apps = apps.filter((app) => map.get(app.id)?.children?.length)
}
const data = reactive(
mapModules(
apps.map((app) => {
return {
id: 'app-' + app.id,
asMenu: true,
isApp: true,
appId: app.id,
appCode: app.appCode,
icon: app.logo,
moduleName: app.appName,
children: map.get(app.id)?.children ?? [],
}
}),
undefined,
moduleMap,
[],
includeAction,
),
)
return includeApp ? data : data[0]?.children ?? []
},
)
if (isRef(appCode)) {
watch(
appCode,
async (appCode) => {
result.value = appCode ? await exec(appCode) : []
},
{ immediate },
)
} else {
exec(appCode).then((res) => {
result.value = res
})
}
const getByKey = (key) => {
const index = moduleMap.get(key) ?? []
let arr = result.value
for (let i = includeApp ? 0 : 1; i < index.length - 1; i++) {
arr = arr[index[i]]?.children ?? []
}
return arr[index.at(-1)] ?? undefined
}
const getParentByKey = (key) => {
const pkey = getByKey(key)?.parentKey
return (pkey && getByKey(pkey)) || undefined
}
const refresh = () =>
exec(unref(appCode)).then((res) => {
result.value = res
})
function updateSort(array) {
if (!array?.length) return
array.sort((a, b) => a.sort - b.sort)
for (const [i, it] of array.entries()) {
const paths = moduleMap.get(it.id)
paths[paths.length - 1] = i
}
}
const transformSaveData = async (
m,
parent = m.key ? getParentByKey(m.key) : m.parentKey ? getByKey(m.parentKey) : undefined,
) => {
const _m = {
id: m.key,
parentId: m.parentKey,
appId: parent?.appId || allApp.find((app) => app.appCode === m.appCode)?.id,
moduleType: moduleTypeMap[m.type],
visiable: m.hidden !== true,
sortField: m.sort,
moduleUrl:
(m.type === 'federate'
? m.remote + MODULE_PATH_SPLIT
: m.type === 'lowcode'
? m.lcKey + (m.remote ? MODULE_APP_SPLIT + m.remote : '') + MODULE_PATH_SPLIT
: '') + m.path,
icon: m.icon,
moduleLevel: m.level,
moduleName: m.label,
moduleNameEn: m.labelEn,
resources: m.resources?.filter((it) => !!it).join(';'),
asMenu: m.type !== 'action',
}
_m.children = m.children?.length
? await Promise.all(
m.children?.map((c) => {
c.parentKey ||= ''
return transformSaveData(c, _m)
}),
)
: undefined
if (_m.icon && typeof _m.icon !== 'string') {
const { id } = await saveIcon({
..._m.icon,
type: 'module',
})
_m.icon = id
}
if (_m.parentId === undefined || _m.parentId?.startsWith('app-')) {
_m.parentId = '0'
}
return _m
}
const mapSavedData = (saved, m, _m) => {
const p = getByKey(m.parentKey)
let m2
if (_m.id) {
const last = getByKey(_m.id)
Object.assign(last, m)
} else {
m2 = mapModule(saved, -1, p, moduleMap, moduleMap.get(m.parentKey), includeAction)
const children = p.children || []
children.push(m2)
p.children = children
}
updateSort(p?.children)
return m2
}
return {
tree: result,
loading,
getByKey,
getParentByKey,
refresh,
save: async (ms) => {
const batch = Array.isArray(ms)
const _ms = await (batch ? Promise.all(ms.map((m) => transformSaveData(m))) : transformSaveData(ms))
return $fetch
.post(batch ? `${CTX.SYS}cer/module/batch/save` : `${CTX.SYS}cer/module/save`, _ms)
.then((saved) => {
if (batch) {
refresh()
return _ms.map((_m, i) => mapSavedData(saved[i], ms[i], _m))
} else {
return mapSavedData(saved, ms, _ms)
}
})
},
del: (m) => {
const id = m.key ?? m
return $fetch.post(`${CTX.SYS}cer/module/delete`, { id }, { reqType: 'urlencoded' }).then(() => {
const p = getParentByKey(id)
const index = p.children.findIndex((c) => c.id === id)
index > -1 && p.children.splice(index, 1)
updateSort(p.children)
})
},
}
}
const moduleTypeMap = {
group: '00',
module: '01',
lowcode: '02',
federate: '03',
external: '04',
other: '99',
}
const moduleTypeMap2 = Object.fromEntries(Object.entries(moduleTypeMap).map(([k, v]) => [v, k]))
export const MODULE_PATH_SPLIT = ' | '
export const MODULE_APP_SPLIT = ' @ '
export { moduleTypeMap2 as moduleTypeMap }
const mapModule = (m, i, parent, map, path, includeAction) => {
if (!includeAction && !m.isApp && !m.asMenu) {
return
}
const s = m.resources?.trim()
path = [...path, i]
map?.set(m.id, path)
let children =
m.asMenu && m.children?.length ? mapModules(m.children, m, map, path, includeAction) : m.isApp ? [] : undefined
if (!includeAction) {
children = children?.filter((it) => !!it)
if (!children?.length) children = undefined
}
let type = moduleTypeMap2[m.moduleType ?? moduleTypeMap.module]
if (type === 'group') type = 'module'
const [prefix, path2] = m.moduleUrl?.split(MODULE_PATH_SPLIT) ?? []
const _m = {
id: m.id,
key: m.id,
parentKey: parent?.id,
parent: parent,
label: m.moduleName,
labelEn: m.moduleNameEn,
resources: s ? s.split(';') : [],
type: m.isApp ? 'other' : m.asMenu ? type : 'action',
level: m.moduleLevel,
icon: m.icon,
path: type === 'federate' || type === 'lowcode' ? path2 : m.moduleUrl,
remote: type === 'federate' ? prefix : type === 'lowcode' ? prefix.split(MODULE_APP_SPLIT)[1] : undefined,
lcKey: type === 'lowcode' ? prefix.split(MODULE_APP_SPLIT)[0] : undefined,
hidden: m.visiable === false,
sort: m.sortField,
appId: m.appId,
appCode: m.appCode || parent?.appCode,
children,
}
return _m
}
const mapModules = (modules, parent, map, path = [], includeAction) => {
let i = 0
return modules.map((m) => {
const _m = mapModule(m, i, parent, map, path, includeAction)
if (_m) i++
return _m
})
}