2025-06-10 11:25:17 +00:00
|
|
|
|
<template>
|
|
|
|
|
|
<el-scrollbar v-loading="loading">
|
2025-06-26 08:36:08 +00:00
|
|
|
|
<app-table :data="tableData" border :span-method="objectSpanMethod">
|
2025-07-08 12:00:30 +00:00
|
|
|
|
<el-table-column prop="module" :width="130" :label="$t('views.role.permission.moduleName')"/>
|
2025-06-26 08:36:08 +00:00
|
|
|
|
<el-table-column
|
|
|
|
|
|
prop="name"
|
|
|
|
|
|
:width="150"
|
|
|
|
|
|
:label="$t('views.role.permission.operationTarget')"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<el-table-column prop="permission" :label="$t('views.model.modelForm.permissionType.label')">
|
|
|
|
|
|
<template #default="{ row }">
|
2025-07-09 06:26:23 +00:00
|
|
|
|
<el-checkbox
|
|
|
|
|
|
v-for="item in row.permission"
|
|
|
|
|
|
:key="item.id"
|
|
|
|
|
|
v-model="item.enable"
|
|
|
|
|
|
:disabled="disabled"
|
|
|
|
|
|
@change="(val: boolean) => handleCellChange(val, item, row)"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="ellipsis" style="width: 96px">{{ item.name }}</div>
|
|
|
|
|
|
</el-checkbox>
|
2025-06-26 08:36:08 +00:00
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column :width="40">
|
|
|
|
|
|
<template #header>
|
|
|
|
|
|
<el-checkbox
|
|
|
|
|
|
:model-value="allChecked"
|
|
|
|
|
|
:indeterminate="allIndeterminate"
|
|
|
|
|
|
:disabled="disabled"
|
|
|
|
|
|
@change="handleCheckAll"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
<el-checkbox
|
|
|
|
|
|
v-model="row.enable"
|
|
|
|
|
|
:indeterminate="row.indeterminate"
|
|
|
|
|
|
:disabled="disabled"
|
|
|
|
|
|
@change="(value: boolean) => handleRowChange(value, row)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</app-table>
|
2025-06-10 11:25:17 +00:00
|
|
|
|
</el-scrollbar>
|
|
|
|
|
|
<div v-if="!disabled" class="footer border-t">
|
2025-06-26 06:32:03 +00:00
|
|
|
|
<el-button type="primary" style="width: 80px" :loading="loading" @click="handleSave">
|
2025-06-10 11:25:17 +00:00
|
|
|
|
{{ $t('common.save') }}
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2025-07-08 12:00:30 +00:00
|
|
|
|
import {ref, watch, computed} from 'vue'
|
2025-06-26 06:32:03 +00:00
|
|
|
|
import type {
|
|
|
|
|
|
RoleItem,
|
|
|
|
|
|
RolePermissionItem,
|
|
|
|
|
|
RoleTableDataItem,
|
|
|
|
|
|
ChildrenPermissionItem,
|
|
|
|
|
|
} from '@/api/type/role'
|
2025-07-08 12:00:30 +00:00
|
|
|
|
import {loadPermissionApi} from '@/utils/dynamics-api/permission-api'
|
2025-06-26 09:00:11 +00:00
|
|
|
|
import RoleApi from '@/api/system/role'
|
2025-07-08 12:00:30 +00:00
|
|
|
|
import {MsgSuccess} from '@/utils/message'
|
|
|
|
|
|
import {t} from '@/locales'
|
|
|
|
|
|
import {hasPermission} from "@/utils/permission";
|
|
|
|
|
|
import {EditionConst, RoleConst} from "@/utils/permission/data.ts";
|
2025-06-10 11:25:17 +00:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
currentRole?: RoleItem
|
|
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
const tableData = ref<RoleTableDataItem[]>([])
|
2025-07-08 12:00:30 +00:00
|
|
|
|
const needDisable = computed(() => {
|
|
|
|
|
|
const isEeOrPe = hasPermission([EditionConst.IS_EE, EditionConst.IS_PE], 'OR')
|
|
|
|
|
|
const isAdminOrExtendAdmin = hasPermission([RoleConst.ADMIN, RoleConst.EXTENDS_ADMIN], 'OR')
|
|
|
|
|
|
const isWorkspaceManage =
|
|
|
|
|
|
hasPermission(
|
2025-07-09 06:26:23 +00:00
|
|
|
|
[
|
|
|
|
|
|
RoleConst.WORKSPACE_MANAGE.getWorkspaceRole,
|
|
|
|
|
|
RoleConst.EXTENDS_WORKSPACE_MANAGE.getWorkspaceRole,
|
|
|
|
|
|
],
|
2025-07-08 12:00:30 +00:00
|
|
|
|
'OR'
|
2025-07-09 06:26:23 +00:00
|
|
|
|
)
|
2025-07-08 12:00:30 +00:00
|
|
|
|
|
|
|
|
|
|
if (!isEeOrPe) {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (isAdminOrExtendAdmin) {
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return isWorkspaceManage
|
|
|
|
|
|
})
|
|
|
|
|
|
const disabled = computed(() => props.currentRole?.internal || needDisable.value)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
|
|
|
|
|
|
function transformData(data: RolePermissionItem[]) {
|
|
|
|
|
|
const transformedData: RoleTableDataItem[] = []
|
2025-06-26 06:32:03 +00:00
|
|
|
|
data.forEach((module) => {
|
|
|
|
|
|
module.children.forEach((feature) => {
|
|
|
|
|
|
const perChecked = feature.permission.filter((p) => p.enable).map((p) => p.id)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
|
|
|
|
|
|
transformedData.push({
|
|
|
|
|
|
module: module.name,
|
|
|
|
|
|
name: feature.name,
|
|
|
|
|
|
permission: feature.permission,
|
|
|
|
|
|
enable: feature.enable,
|
|
|
|
|
|
perChecked,
|
2025-06-26 06:32:03 +00:00
|
|
|
|
indeterminate: perChecked.length > 0 && perChecked.length < feature.permission.length,
|
2025-06-10 11:25:17 +00:00
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
2025-06-26 06:32:03 +00:00
|
|
|
|
return transformedData
|
|
|
|
|
|
}
|
2025-06-10 11:25:17 +00:00
|
|
|
|
|
|
|
|
|
|
async function getRolePermission() {
|
|
|
|
|
|
if (!props.currentRole?.id) return
|
|
|
|
|
|
try {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
tableData.value = []
|
2025-06-26 09:00:11 +00:00
|
|
|
|
const res = await RoleApi.getRolePermissionList(props.currentRole.id, loading)
|
2025-06-26 06:32:03 +00:00
|
|
|
|
tableData.value = transformData(res.data)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error(error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-09 06:26:23 +00:00
|
|
|
|
function handleCellChange(
|
|
|
|
|
|
value: boolean,
|
|
|
|
|
|
item: ChildrenPermissionItem,
|
|
|
|
|
|
row: RoleTableDataItem,
|
|
|
|
|
|
) {
|
|
|
|
|
|
item.enable = value
|
|
|
|
|
|
const readItem = row.permission.find((p) => /:READ$/.test(p.id))
|
|
|
|
|
|
// 如果勾选的不是 READ,则强制把 READ 也勾上
|
|
|
|
|
|
if (value && item.id !== readItem?.id && readItem && !readItem.enable) {
|
|
|
|
|
|
readItem.enable = true
|
|
|
|
|
|
} else if (!value && item.id === readItem?.id) {
|
|
|
|
|
|
// 取消 READ 整行其他权限全部取消
|
|
|
|
|
|
row.permission.forEach((p) => (p.enable = false))
|
|
|
|
|
|
}
|
2025-06-10 11:25:17 +00:00
|
|
|
|
|
2025-07-09 06:26:23 +00:00
|
|
|
|
const checkedIds = row.permission.filter((p) => p.enable).map((p) => p.id)
|
|
|
|
|
|
row.perChecked = checkedIds
|
|
|
|
|
|
row.enable = checkedIds.length === row.permission.length
|
|
|
|
|
|
row.indeterminate =
|
|
|
|
|
|
checkedIds.length > 0 && checkedIds.length < row.permission.length
|
2025-06-10 11:25:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function handleRowChange(checked: boolean, row: RoleTableDataItem) {
|
|
|
|
|
|
if (checked) {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
row.permission.forEach((p) => (p.enable = true))
|
2025-06-10 11:25:17 +00:00
|
|
|
|
} else {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
row.permission.forEach((p) => (p.enable = false))
|
2025-06-10 11:25:17 +00:00
|
|
|
|
}
|
2025-07-09 06:26:23 +00:00
|
|
|
|
row.perChecked = checked ? row.permission.map((p) => p.id) : []
|
2025-06-10 11:25:17 +00:00
|
|
|
|
row.indeterminate = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const allChecked = computed(() => {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
return tableData.value.length > 0 && tableData.value.every((item) => item.enable)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
const allIndeterminate = computed(() => {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
return !allChecked.value && tableData.value.some((item) => item.enable)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function handleCheckAll(checked: boolean) {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
tableData.value.forEach((item) => {
|
2025-06-10 11:25:17 +00:00
|
|
|
|
item.enable = checked
|
2025-06-26 06:32:03 +00:00
|
|
|
|
item.perChecked = checked ? item.permission.map((p) => p.id) : []
|
2025-06-10 11:25:17 +00:00
|
|
|
|
item.indeterminate = false
|
2025-06-26 06:32:03 +00:00
|
|
|
|
item.permission.forEach((p) => (p.enable = checked))
|
2025-06-10 11:25:17 +00:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 12:00:30 +00:00
|
|
|
|
const objectSpanMethod = ({row, column, rowIndex, columnIndex}: any) => {
|
2025-06-10 11:25:17 +00:00
|
|
|
|
if (columnIndex === 0) {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
const sameModuleRows = tableData.value.filter((item) => item.module === row.module)
|
|
|
|
|
|
const firstRowIndex = tableData.value.findIndex((item) => item.module === row.module)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
if (rowIndex === firstRowIndex) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
rowspan: sameModuleRows.length,
|
2025-06-26 06:32:03 +00:00
|
|
|
|
colspan: 1,
|
2025-06-10 11:25:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return {
|
|
|
|
|
|
rowspan: 0,
|
2025-06-26 06:32:03 +00:00
|
|
|
|
colspan: 0,
|
2025-06-10 11:25:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-08 12:00:30 +00:00
|
|
|
|
watch(() => props.currentRole?.id, getRolePermission, {immediate: true})
|
2025-06-10 11:25:17 +00:00
|
|
|
|
|
|
|
|
|
|
async function handleSave() {
|
|
|
|
|
|
try {
|
2025-07-09 06:26:23 +00:00
|
|
|
|
const permissions = tableData.value.flatMap((row) =>
|
|
|
|
|
|
row.permission.map((p) => ({ id: p.id, enable: p.enable })),
|
|
|
|
|
|
)
|
2025-06-26 08:36:08 +00:00
|
|
|
|
await loadPermissionApi('role').saveRolePermission(props.currentRole?.id as string, permissions, loading)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
MsgSuccess(t('common.saveSuccess'))
|
|
|
|
|
|
} catch (error) {
|
2025-06-26 06:32:03 +00:00
|
|
|
|
console.log(error)
|
2025-06-10 11:25:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
:deep(.el-checkbox-group) {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.footer {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
padding: 16px 24px;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
2025-06-26 06:32:03 +00:00
|
|
|
|
</style>
|