实现在角色权限中加入数据权限鉴权配置办法
数据权限

因为项目开发中涉及到比较细化的数据权限划分,需要对部门数据权限就行赋予数据权限(数据要根据各个部门进行权限查看),在框架的基础上进行了下修改,写的比较差,但功能实现了,具体如下:


实现菜单权限和数据权限的单独设置
实现办法,首先创建一个ba_role_data_permissions表,存储角色对应的部门信息

DROP TABLE IF EXISTS `ba_role_data_permissions`;
CREATE TABLE `ba_role_data_permissions`  (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  `role_id` int(11) UNSIGNED NOT NULL COMMENT '关联的角色ID,外键指向角色表',
  `data_permission` enum('all','department','department_and_below','personal','custom') CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '数据权限类型',
  `custom_department_id` int(11) UNSIGNED NULL DEFAULT NULL COMMENT '可选的自定义部门ID,只有在权限类型为custom时使用',
  `created_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
  `updated_at` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '记录更新时间',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `role_id`(`role_id`) USING BTREE,
  CONSTRAINT `ba_role_data_permissions_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `ba_admin_group` (`id`) ON DELETE CASCADE ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 49 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '角色与数据权限的关系表' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

ba_hr_departments表结构

DROP TABLE IF EXISTS `ba_hr_departments`;
CREATE TABLE `ba_hr_departments`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '部门名称',
  `description` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '部门描述',
  `pid` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '上级部门ID',
  `sort` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '排序',
  `status` enum('1','0') CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '单选框:1=启用,0=禁用',
  `create_time` bigint(16) UNSIGNED NULL DEFAULT NULL COMMENT '创建时间',
  `update_time` bigint(16) UNSIGNED NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 15 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '部门管理' ROW_FORMAT = DYNAMIC;

SET FOREIGN_KEY_CHECKS = 1;

然后在\app\admin\controller\auth\Group.php加入代码

public function setRoleDataPermission()
    {
        $roleId = $this->request->post('role_id');
        $dataPermissionType = $this->request->post('data_permission');
        $customDepartmentId = $this->request->post('custom_department_id');

        // 验证角色ID的有效性
        if (!$this->isValidRoleId($roleId)) {
            $this->error('无效的角色ID');
        }

        // 设置数据权限
        $dataPermission = [
            'role_id' => $roleId,
            'data_permission' => $dataPermissionType,
            'custom_department_id' => $customDepartmentId,
        ];

        // 检查是否存在记录
        $exists = Db::name('role_data_permissions')->where('role_id', $roleId)->find();

        if ($exists) {
            // 如果存在,更新数据
            $result = Db::name('role_data_permissions')->where('role_id', $roleId)->update($dataPermission);
        } else {
            // 如果不存在,插入新数据
            $result = Db::name('role_data_permissions')->insert($dataPermission);
        }

        // 根据返回值判断操作是否成功
        if ($result !== false) {
            $this->success('数据权限设置成功');
        } else {
            $this->error('数据权限设置失败');
        }
    }
private function isValidRoleId($roleId)
    {
        //查找角色ID是否存在
        $role = AdminGroup::where('id', $roleId)->find();
        return $role !== null; //如果角色存在,返回true,否则返回false
    }

接下来修改app\common\controller\Backend.php里面的getDataLimitAdminIds方法,修改后的代码

protected function getDataLimitAdminIds(): array
    {
        if (!$this->dataLimit || $this->auth->isSuperAdmin()) {
            return [];
        }

        $adminIds = [];

        // 获取角色的数据权限类型和自定义部门 ID
        $dataPermissionType = $this->getDataPermissionType($this->auth->getRoleId());
        $customDepartmentId = $this->getCustomDepartmentId($this->auth->getRoleId());

        switch ($dataPermissionType) {
            case 'all':
                // 如果指定了 custom_department_id,获取该部门的所有管理员
                $adminIds = $customDepartmentId
                    ? $this->getDepartmentAdminIds($customDepartmentId)
                    : $this->getAllAdminIds();
                break;

            case 'department':
                // 仅限于本部门
                $adminIds = $this->getDepartmentAdminIds($this->auth->getDeptId());
                break;

            case 'department_and_below':
                // 本部门及以下
                $adminIds = $this->getDepartmentAndBelowAdminIds($this->auth->getDeptId());
                break;

            case 'personal':
                // 仅限于本人
                $adminIds[] = $this->auth->id;
                break;

            case 'custom':
                // 自定义部门数据权限
                if ($customDepartmentId) {
                    $adminIds = $this->getCustomDepartmentAdminIds($customDepartmentId);
                }
                break;
        }

        // 确保当前用户的 ID 被包含在结果中
        // if (!in_array($this->auth->id, $adminIds)) {
        //     $adminIds[] = $this->auth->id;
        // }

        return array_unique($adminIds);
    }

    private function getDataPermissionType($roleId)
    {
        return Db::table('ba_role_data_permissions')->where('role_id', $roleId)->value('data_permission');
    }

    private function getCustomDepartmentId($roleId)
    {
        return Db::table('ba_role_data_permissions')->where('role_id', $roleId)->value('custom_department_id');
    }

    private function getAllAdminIds(): array
    {
        return Db::table('ba_admin')->column('id');
    }

    private function getDepartmentAdminIds($departmentId): array
    {
        $adminIds = Db::name('admin')
            ->where('dpt_id', $departmentId)
            ->column('id');

        // if (!in_array($this->auth->id, $adminIds)) {
        //     $adminIds[] = $this->auth->id;
        // }

        return array_unique($adminIds);
    }

    private function getDepartmentAndBelowAdminIds($departmentId): array
    {
        $departmentIds = $this->getAllChildDepartmentIds($departmentId);
        return Db::name('admin')
            ->whereIn('dpt_id', $departmentIds)
            ->column('id');
    }
    private function getCustomDepartmentAdminIds($customDepartmentId): array
    {
        // 获取当前部门及其所有子部门的管理员 ID
        $adminIds = Db::name('admin')
            ->whereIn('dpt_id', $this->getAllChildDepartmentIds($customDepartmentId))
            ->column('id');

        // 添加当前用户的 ID(如果当前用户不在结果中)
        // if (!in_array($this->auth->id, $adminIds)) {
        //     $adminIds[] = $this->auth->id;
        // }

        return array_unique($adminIds);
    }

    private function getAllChildDepartmentIds($departmentId): array
    {
        // 获取当前部门的子部门ID
        $children = Db::name('hr_departments')
            ->where('pid', $departmentId)
            ->column('id');

        // 创建一个数组,包含当前部门ID和所有子部门ID
        $allIds = [$departmentId]; // 添加当前部门ID

        foreach ($children as $childId) {
            $allIds = array_merge($allIds, $this->getAllChildDepartmentIds($childId));
        }

        return array_unique($allIds);
    }

app\admin\library\Auth.php里面新增

//获取当前用户的角色id
    public function getRoleId()
    {
        $roleid=Db::table('ba_admin_group_access')->where('uid',$this->id)->value('group_id');
        return $roleid;
    }
    //获取当前用户的部门id
    public function getDeptId()
    {
        $deptid=Db::table('ba_admin')->where('id',$this->id)->value('dpt_id');
        return $deptid;
    }

然后对需要开启数据权限的控制器加入对应代码就可以进行数据权限校验了

 /**
     * 开启数据限制
     */
    protected bool|string|int $dataLimit = false;

    /**
     * 数据限制字段,数据表内必须存在此字段
     */
    protected string $dataLimitField = 'admin_id';

    /**
     * 数据限制开启时自动填充字段值为当前管理员id
     */
    protected bool $dataLimitFieldAutoFill = true;

前端配置代码

<template>
    <el-dialog class="ba-operate-dialog" v-model="baTable.table.extend!.showInfo" width="50%">
        <template #header>
            <div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">数据权限</div>
        </template>
        <el-form v-if="baTable.table.extend!.infoData" :model="form" ref="formRef">
  <el-form-item label="角色ID" prop="role_id">
    <el-input v-model="form.role_id" disabled />
  </el-form-item>
  <el-form-item label="角色名称" prop="role_name">
    <el-input v-model="baTable.table.extend!.infoData.name" disabled/>
  </el-form-item>
  <el-form-item label="数据权限" prop="data_permission">
    <el-select v-model="form.data_permission">
      <el-option label="全部数据权限" value="all" />
      <el-option label="本部门数据权限" value="department" />
      <el-option label="本部门及以下数据权限" value="department_and_below" />
      <el-option label="本人数据权限" value="personal" />
      <el-option label="自定义数据权限" value="custom" />
    </el-select>
  </el-form-item>
  <el-form-item v-if="form.data_permission === 'custom'" label="自定义部门ID" prop="custom_department_id">
          <el-input v-model="form.custom_department_id" />
        </el-form-item>
</el-form>

        <span class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="savePermission">确 定</el-button>
      </span>

        
    </el-dialog>
</template>

cleanXss
import { inject,ref,watch } from 'vue'
import { timeFormat } from '/@/utils/common'
import type baTableClass from '/@/utils/baTable'
import FormItem from '/@/components/formItem/index.vue'
import{setRoleDataPermission}from '/@/api/backend/auth/group'
const baTable = inject('baTable') as baTableClass
import { ElMessage } from 'element-plus';
const formRef = ref(); // 声明表单引用
const dialogVisible = ref(false);
const form = ref({
  role_id: '',
  data_permission: '',
  custom_department_id: ''
});

const savePermission = () => {
    console.log(form.value);
    setRoleDataPermission(form.value).then(res => {
        console.log(res);

      if (res.code === 1) {
        ElMessage.success('权限已保存');
        baTable.table.extend!.showInfo = false;
        // 刷新权限列表逻辑
      } else {
        ElMessage.error('保存权限失败');
      }
    }).catch(err => {
      ElMessage.error('保存权限失败');
    })

};
// 监听父组件传来的 infoData 变化
watch(() => baTable.table.extend!.infoData, (newValue) => {
    if (newValue) {
        form.value.role_id = newValue.id; // 设置角色ID
        // 可以根据需要设置其他字段
    }
});


cleanXss

<style scoped lang="scss">
.info-box {
    margin-top: 60px;
    div {
        width: 100%;
        text-align: center;
    }
    .mt-40 {
        margin-top: 40px;
    }
}
</style>

其他注意事项:
需在admin表内新增一个dpt_id字段存储用户的部门id

8个回答默认排序 投票数排序
珙桐-全栈老技术外包
珙桐-全栈老技术外包
啦啦啦。开发找我哈
1月前

列表页增加按钮的代码好像没写?

adminchat
adminchat回复珙桐-全栈老技术外包
这家伙很懒,什么也没写~
1月前
<template>
    <div class="default-main ba-table-box">
        <el-alert class="ba-table-alert" v-if="!adminInfo.super" :title="t('auth.group.Manage subordinate role groups here')" type="info" show-icon />
        <el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />

        <!-- 表格顶部菜单 -->
        <TableHeader
            :buttons="['refresh', 'add', 'edit', 'delete', 'unfold', 'quickSearch', 'columnDisplay']"
            :quick-search-placeholder="t('Quick search placeholder', { fields: t('auth.group.GroupName') })"
        />

        <!-- 表格 -->
        <!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
        <Table ref="tableRef" :pagination="false" />

        <!-- 表单 -->
        <PopupForm ref="formRef" />

        <!-- 示例核心代码(1/3) -->
        <!-- 详情组件在此处使用,但显示与否的判断是写在组件内的 -->
        <Info />


    </div>
</template>

cleanXss
import { onMounted, provide, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import PopupForm from './popupForm.vue'
import { getAdminRules } from '/@/api/backend/auth/group'
import { baTableApi } from '/@/api/common'
import { defaultOptButtons } from '/@/components/table'
import TableHeader from '/@/components/table/header/index.vue'
import Table from '/@/components/table/index.vue'
import { useAdminInfo } from '/@/stores/adminInfo'
import baTableClass from '/@/utils/baTable'
import { getArrayKey } from '/@/utils/common'
import { uuid } from '/@/utils/random'
import Info from './info.vue'

defineOptions({
    name: 'auth/group',
})

const formRef = ref()
const tableRef = ref()
const { t } = useI18n()
const adminInfo = useAdminInfo()
const optButtons: OptButton[] = defaultOptButtons(['edit', 'delete'])

/**
 * 示例核心代码(2/3)
 * 表格操作按钮组 optButtons 只是个普通的数组,此处向其 push 一个 OptButton
 */
 optButtons.push({
    render: 'tipButton',
    // name 是任意的
    name: 'info',
    // title 是语言翻译 key
    title: '数据权限',
    text: '',
    type: 'success',
    icon: 'fa fa-database icon',
    click(row, field) {
        console.info('%c-------详情按钮被点击了--------', 'color:blue')
        console.log('接受到行数据和列数据', row, field)
        console.log('%c赋值:baTable.table.extend!.showInfo = true', 'color:red')

        // 在 extend 上自定义一个变量标记详情弹窗显示状态,详情组件内以此判断显示即可!
        baTable.table.extend!.showInfo = true

        // 您也可以使用 baTable.form.operate,默认情况它有三个值`Add、Edit、空字符串`,前两个值将显示添加和编辑弹窗

        // 您也可以再来个 loading 态,然后请求详情数据等
        baTable.table.extend!.infoLoading = true
        setTimeout&#40;(&#41; => {
            baTable.table.extend!.infoData = row
            baTable.table.extend!.infoLoading = false
        }, 1000)
    },
})


const baTable: baTableClass = new baTableClass(
    new baTableApi('/admin/auth.Group/'),
    {
        expandAll: true,
        dblClickNotEditColumn: [undefined],
        column: [
            { type: 'selection', align: 'center' },
            { label: t('auth.group.Group name'), prop: 'name', align: 'left', width: '200' },
            { label: t('auth.group.jurisdiction'), prop: 'rules', align: 'center' },
            {
                label: t('State'),
                prop: 'status',
                align: 'center',
                render: 'tag',
                custom: { '0': 'danger', '1': 'success' },
                replaceValue: { '0': t('Disable'), '1': t('Enable') },
            },
            { label: t('Update time'), prop: 'update_time', align: 'center', width: '160', render: 'datetime' },
            { label: t('Create time'), prop: 'create_time', align: 'center', width: '160', render: 'datetime' },
            { label: t('Operate'), align: 'center', width: 140, render: 'buttons', buttons: optButtons, operator: false },
        ],
    },
    {
        defaultItems: {
            status: '1',
        },
    },
    {
        // 提交前
        cleanXss: ({ formEl, operate, items }) => {
            let submitCallback = () => {
                baTable.form.submitLoading = true
                baTable.api
                    .postData(operate, {
                        ...items,
                        rules: formRef.value.getCheckeds(),
                    })
                    .then((res) => {
                        baTable.onTableHeaderAction('refresh', {})
                        baTable.form.submitLoading = false
                        baTable.form.operateIds?.shift()
                        if (baTable.form.operateIds!.length > 0) {
                            baTable.toggleForm('Edit', baTable.form.operateIds)
                        } else {
                            baTable.toggleForm()
                        }
                        baTable.runAfter('cleanXss', { res })
                    })
                    .catch(() => {
                        baTable.form.submitLoading = false
                    })
            }

            if (formEl) {
                baTable.form.ref = formEl
                formEl.validate((valid) => {
                    if (valid) {
                        submitCallback()
                    }
                })
            } else {
                submitCallback()
            }
            return false
        },
        // 双击编辑前
        onTableDblclick: ({ row }) => {
            return baTable.table.extend!.adminGroup.indexOf(row.id) === -1
        },
    },
    {
        getIndex: ({ res }) => {
            baTable.table.extend!.adminGroup = res.data.group
            let buttonsKey = getArrayKey(baTable.table.column, 'render', 'buttons')
            baTable.table.column[buttonsKey].buttons!.forEach((value: OptButton) => {
                value.display = (row) => {
                    return res.data.group.indexOf(row.id) === -1
                }
            })
        },
        // 切换表单后
        toggleForm({ operate }) {
            if (operate == 'Add') {
                menuRuleTreeUpdate()
            }
        },
        // 编辑请求完成后
        requestEdit() {
            menuRuleTreeUpdate()
        },
    }
)

const menuRuleTreeUpdate = () => {
    getAdminRules().then((res) => {
        baTable.form.extend!.menuRules = res.data.list

        if (baTable.form.items!.rules && baTable.form.items!.rules.length) {
            if (baTable.form.items!.rules.includes('*')) {
                let arr: number[] = []
                for (const key in baTable.form.extend!.menuRules) {
                    arr.push(baTable.form.extend!.menuRules[key].id)
                }
                baTable.form.extend!.defaultCheckedKeys = arr
            } else {
                baTable.form.extend!.defaultCheckedKeys = baTable.form.items!.rules
            }
        } else {
            baTable.form.extend!.defaultCheckedKeys = []
        }
        baTable.form.extend!.treeKey = uuid()
    })
}

provide('baTable', baTable)

onMounted(() => {
    baTable.table.ref = tableRef.value
    baTable.mount()
    baTable.getIndex()
})
cleanXss

<style scoped lang="scss"></style>
w434shengming
w434shengming回复adminchat
这家伙很懒,什么也没写~
1月前

你这里面的cleanXss是什么意思?

珙桐-全栈老技术外包
珙桐-全栈老技术外包回复adminchat
啦啦啦。开发找我哈
1月前

才看到回复。楼主辛苦了。。。暂时用不到,先做个记号!

珙桐-全栈老技术外包
珙桐-全栈老技术外包回复w434shengming
啦啦啦。开发找我哈
1月前

代他回答你:一个安全函数。。。文档有写。。。

jayip
jayip
-
1月前

学习一下

doukai
doukai
这家伙很懒,什么也没写~
1月前

感谢分享

ipiaobo
ipiaobo
这家伙很懒,什么也没写~
1月前

感谢分享

w434shengming
w434shengming
这家伙很懒,什么也没写~
1月前

感谢分享

w434shengming
w434shengming
这家伙很懒,什么也没写~
1月前

router.ts:22 SyntaxError: The requested module '/src/api/backend/auth/group.ts?t=1730278509639' does not provide an export named 'setRoleDataPermission' (at info.vue:42:10)
triggerError @ vue-router.mjs:3578
(匿名) @ vue-router.mjs:3291
Promise.catch(异步)
pushWithRedirect @ vue-router.mjs:3285
push @ vue-router.mjs:3217
routePush @ router.ts:22
(匿名) @ index.vue:78
Promise.then(异步)
init @ index.vue:59
(匿名) @ index.vue:46
(匿名) @ runtime-core.esm-bundler.js:1552
callWithErrorHandling @ runtime-core.esm-bundler.js:202
callWithAsyncErrorHandling @ runtime-core.esm-bundler.js:209
hook.__weh.hook.__weh @ runtime-core.esm-bundler.js:1532
flushPostFlushCbs @ runtime-core.esm-bundler.js:386
flushJobs @ runtime-core.esm-bundler.js:426
Promise.then(异步)
queueFlush @ runtime-core.esm-bundler.js:326
queueJob @ runtime-core.esm-bundler.js:320
scheduler @ runtime-core.esm-bundler.js:5988
resetScheduling @ reactivity.esm-bundler.js:262
triggerEffects @ reactivity.esm-bundler.js:306
triggerRefValue @ reactivity.esm-bundler.js:1079
set value @ reactivity.esm-bundler.js:1126
finalizeNavigation @ vue-router.mjs:3460
(匿名) @ vue-router.mjs:3325
Promise.then(异步)
pushWithRedirect @ vue-router.mjs:3292
pushWithRedirect @ vue-router.mjs:3260
push @ vue-router.mjs:3217
install @ vue-router.mjs:3659
use @ runtime-core.esm-bundler.js:2576
start @ main.ts:23
await in start(异步)
(匿名) @ main.ts:36
显示另外 24 个框架
router.ts:39 SyntaxError: The requested module '/src/api/backend/auth/group.ts?t=1730278509639' does not provide an export named 'setRoleDataPermission' (at info.vue:42:10)

提示这个找不到

mrtian
mrtian
这家伙很懒,什么也没写~
1周前

很不错,感谢贡献!

mrtian
mrtian
这家伙很懒,什么也没写~
1周前

应该弄成一个模块,直接安装

请先登录
2
1
4
12