| | |
| | | <template> |
| | | <ElDrawer |
| | | :model-value="visible" |
| | | title="角色权限" |
| | | :title="t('pages.system.role.permission.title')" |
| | | size="860px" |
| | | destroy-on-close |
| | | @update:model-value="handleVisibleChange" |
| | | @closed="handleClosed" |
| | | > |
| | | <div class="mb-4 text-sm text-[var(--art-text-secondary)]"> |
| | | 当前角色:{{ roleLabel }} |
| | | {{ t('pages.system.role.permission.currentRole') }}{{ roleLabel }} |
| | | </div> |
| | | |
| | | <ElTabs v-model="activeScopeType" class="role-scope-tabs"> |
| | |
| | | <div v-else class="space-y-3"> |
| | | <div class="flex items-center justify-between gap-3"> |
| | | <ElSpace wrap> |
| | | <ElButton @click="handleSelectAll(config.scopeType)">全选</ElButton> |
| | | <ElButton @click="handleClear(config.scopeType)">清空</ElButton> |
| | | <ElButton @click="handleSelectAll(config.scopeType)">{{ t('pages.system.role.permission.selectAll') }}</ElButton> |
| | | <ElButton @click="handleClear(config.scopeType)">{{ t('pages.system.role.permission.clear') }}</ElButton> |
| | | </ElSpace> |
| | | <ElButton type="primary" @click="handleSave(config.scopeType)">保存当前权限</ElButton> |
| | | <ElButton type="primary" @click="handleSave(config.scopeType)">{{ t('pages.system.role.permission.saveCurrent') }}</ElButton> |
| | | </div> |
| | | |
| | | <div class="flex items-center gap-3"> |
| | | <ElInput |
| | | v-model.trim="scopeState[config.scopeType].condition" |
| | | clearable |
| | | placeholder="搜索权限树" |
| | | :placeholder="t('pages.system.role.permission.searchPlaceholder')" |
| | | @clear="handleSearch(config.scopeType)" |
| | | @keyup.enter="handleSearch(config.scopeType)" |
| | | /> |
| | | <ElButton @click="handleSearch(config.scopeType)">搜索</ElButton> |
| | | <ElButton @click="handleSearch(config.scopeType)">{{ t('common.actions.search') }}</ElButton> |
| | | </div> |
| | | |
| | | <ElScrollbar height="56vh"> |
| | |
| | | <div class="flex items-center gap-2"> |
| | | <span>{{ resolveScopeNodeLabel(data) }}</span> |
| | | <ElTag v-if="data.isAuthButton" type="info" effect="plain" size="small"> |
| | | 按钮 |
| | | {{ t('pages.system.role.permission.authButton') }} |
| | | </ElTag> |
| | | </div> |
| | | </template> |
| | |
| | | } from '../rolePage.helpers' |
| | | import { fetchGetRoleScopeList, fetchGetRoleScopeTree, fetchUpdateRoleScope } from '@/api/system-manage' |
| | | import { resolveBackendMenuTitle } from '@/utils/backend-menu-title' |
| | | import { formatMenuTitle } from '@/utils/router' |
| | | import { guardRequestWithMessage } from '@/utils/sys/requestGuard' |
| | | import { ElMessage } from 'element-plus' |
| | | import { useI18n } from 'vue-i18n' |
| | | |
| | | const props = defineProps({ |
| | | visible: { required: false, default: false }, |
| | |
| | | }) |
| | | |
| | | const emit = defineEmits(['update:visible', 'success']) |
| | | const { t } = useI18n() |
| | | |
| | | const scopeConfigs = ['menu', 'pda', 'matnr', 'warehouse'].map((scopeType) => getRoleScopeConfig(scopeType)) |
| | | const activeScopeType = ref(props.scopeType || 'menu') |
| | |
| | | label: 'label', |
| | | children: 'children' |
| | | } |
| | | const scopeState = reactive( |
| | | Object.fromEntries( |
| | | scopeConfigs.map((config) => [ |
| | | config.scopeType, |
| | | { |
| | | loading: false, |
| | | loaded: false, |
| | | treeData: [], |
| | | checkedKeys: [], |
| | | halfCheckedKeys: [], |
| | | condition: '' |
| | | } |
| | | ]) |
| | | ) |
| | | ) |
| | | const scopeState = reactive(Object.fromEntries(scopeConfigs.map((config) => [config.scopeType, createScopeTabState()]))) |
| | | |
| | | const visible = computed({ |
| | | get: () => props.visible, |
| | | set: (value) => emit('update:visible', value) |
| | | }) |
| | | |
| | | const roleLabel = computed(() => props.roleData?.name || props.roleData?.code || '未选择角色') |
| | | const roleLabel = computed(() => props.roleData?.name || props.roleData?.code || t('pages.system.role.permission.unselected')) |
| | | |
| | | function createScopeTabState() { |
| | | return { |
| | | loading: false, |
| | | loaded: false, |
| | | treeData: [], |
| | | checkedKeys: [], |
| | | halfCheckedKeys: [], |
| | | condition: '' |
| | | } |
| | | } |
| | | |
| | | function resetScopeTabState(state) { |
| | | Object.assign(state, createScopeTabState()) |
| | | } |
| | | |
| | | function hasNodeKey(value) { |
| | | return value !== void 0 && value !== null && value !== '' |
| | | } |
| | | |
| | | const loadScopeData = async (scopeType, { reloadSelection = true } = {}) => { |
| | | const config = getRoleScopeConfig(scopeType) |
| | | const state = scopeState[scopeType] |
| | | state.loading = true |
| | | try { |
| | | const requests = [fetchGetRoleScopeTree(config.scopeType, { condition: state.condition || '' })] |
| | | if (reloadSelection) { |
| | | requests.unshift(fetchGetRoleScopeList(config.scopeType, props.roleData.id)) |
| | | } |
| | | const selectionRequest = reloadSelection |
| | | ? fetchGetRoleScopeList(config.scopeType, props.roleData.id) |
| | | : Promise.resolve(state.checkedKeys) |
| | | const treeRequest = fetchGetRoleScopeTree(config.scopeType, { condition: state.condition || '' }) |
| | | |
| | | const [checkedIds, treeData] = reloadSelection ? await Promise.all(requests) : [state.checkedKeys, await requests[0]] |
| | | const guardedResult = await guardRequestWithMessage( |
| | | Promise.all([selectionRequest, treeRequest]), |
| | | null, |
| | | { |
| | | timeoutMessage: t('pages.system.role.permission.scopeLoadTimeout', { title: config.title }) |
| | | } |
| | | ) |
| | | if (!guardedResult) { |
| | | state.treeData = [] |
| | | state.checkedKeys = [] |
| | | state.halfCheckedKeys = [] |
| | | state.loaded = true |
| | | return |
| | | } |
| | | const [checkedIds, treeData] = guardedResult |
| | | state.treeData = normalizeRoleScopeTreeData(config.scopeType, treeData) |
| | | state.checkedKeys = normalizeScopeKeys(checkedIds) |
| | | state.halfCheckedKeys = [] |
| | | state.loaded = true |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || `加载${config.title}失败`) |
| | | ElMessage.error(error?.message || t('pages.system.role.permission.scopeLoadFailed', { title: config.title })) |
| | | } finally { |
| | | state.loading = false |
| | | nextTick(() => { |
| | |
| | | scopeState[scopeType].halfCheckedKeys |
| | | ) |
| | | ) |
| | | ElMessage.success('权限保存成功') |
| | | ElMessage.success(t('pages.system.role.permission.saveSuccess')) |
| | | emit('success') |
| | | visible.value = false |
| | | } catch (error) { |
| | | ElMessage.error(error?.message || '权限保存失败') |
| | | ElMessage.error(error?.message || t('pages.system.role.permission.saveFailed')) |
| | | } |
| | | } |
| | | |
| | |
| | | if (!rawLabel) { |
| | | return '' |
| | | } |
| | | return resolveBackendMenuTitle(rawLabel) |
| | | if (activeScopeType.value === 'menu') { |
| | | const resolvedTitle = resolveBackendMenuTitle(rawLabel, data?.component || '') |
| | | return resolvedTitle ? formatMenuTitle(resolvedTitle) : '' |
| | | } |
| | | return rawLabel |
| | | } |
| | | |
| | | const handleClosed = () => { |
| | | activeScopeType.value = props.scopeType || 'menu' |
| | | Object.keys(scopeState).forEach((key) => { |
| | | scopeState[key].loading = false |
| | | scopeState[key].loaded = false |
| | | scopeState[key].treeData = [] |
| | | scopeState[key].checkedKeys = [] |
| | | scopeState[key].halfCheckedKeys = [] |
| | | scopeState[key].condition = '' |
| | | resetScopeTabState(scopeState[key]) |
| | | }) |
| | | } |
| | | |
| | |
| | | const keys = [] |
| | | const traverse = (nodeList) => { |
| | | nodeList.forEach((node) => { |
| | | if (node.id !== void 0 && node.id !== null && node.id !== '') { |
| | | if (hasNodeKey(node.id)) { |
| | | keys.push(String(node.id)) |
| | | } |
| | | if (node.children?.length) { |