mirror of
https://gitee.com/wonderful-code/buildadmin
synced 2024-11-21 22:55:36 +00:00
feat:角色组管理
This commit is contained in:
parent
436156e0d9
commit
508fde0aa1
@ -2,8 +2,14 @@
|
||||
|
||||
namespace app\admin\controller\auth;
|
||||
|
||||
use app\admin\model\MenuRule;
|
||||
use ba\Tree;
|
||||
use app\common\controller\Backend;
|
||||
use app\admin\model\AdminGroup;
|
||||
use Exception;
|
||||
use think\db\exception\PDOException;
|
||||
use think\exception\ValidateException;
|
||||
use think\facade\Db;
|
||||
|
||||
class Group extends Backend
|
||||
{
|
||||
@ -16,10 +22,206 @@ class Group extends Backend
|
||||
|
||||
protected $quickSearchField = 'name';
|
||||
|
||||
/**
|
||||
* @var Tree
|
||||
*/
|
||||
protected $tree = null;
|
||||
|
||||
protected $keyword = false;
|
||||
|
||||
public function initialize()
|
||||
{
|
||||
parent::initialize();
|
||||
$this->model = new AdminGroup();
|
||||
$this->tree = Tree::instance();
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
if ($this->request->param('select')) {
|
||||
$this->select();
|
||||
}
|
||||
|
||||
$this->keyword = $this->request->request("quick_search");
|
||||
$this->success('', [
|
||||
'list' => $this->getGroups(),
|
||||
'remark' => get_route_remark(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function add()
|
||||
{
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
if (!$data) {
|
||||
$this->error(__('Parameter %s can not be empty', ['']));
|
||||
}
|
||||
|
||||
$data = $this->excludeFields($data);
|
||||
if (is_array($data['rules']) && $data['rules']) {
|
||||
$menuRuleModel = new MenuRule();
|
||||
$rules = $menuRuleModel->select();
|
||||
$superAdmin = true;
|
||||
foreach ($rules as $rule) {
|
||||
if (!in_array($rule['id'], $data['rules'])) {
|
||||
$superAdmin = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($superAdmin) {
|
||||
$data['rules'] = '*';
|
||||
} else {
|
||||
$data['rules'] = implode(',', $data['rules']);
|
||||
}
|
||||
} else {
|
||||
unset($data['rules']);
|
||||
}
|
||||
|
||||
$result = false;
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 模型验证
|
||||
if ($this->modelValidate) {
|
||||
$validate = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
||||
if (class_exists($validate)) {
|
||||
$validate = new $validate;
|
||||
$validate->scene('add')->check($data);
|
||||
}
|
||||
}
|
||||
$result = $this->model->save($data);
|
||||
Db::commit();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
} catch (PDOException $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
if ($result !== false) {
|
||||
$this->success(__('Added successfully'));
|
||||
} else {
|
||||
$this->error(__('No rows were added'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->error(__('Parameter error'));
|
||||
}
|
||||
|
||||
public function edit($id = null)
|
||||
{
|
||||
$row = $this->model->find($id);
|
||||
if (!$row) {
|
||||
$this->error(__('Record not found'));
|
||||
}
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
$data = $this->request->post();
|
||||
if (!$data) {
|
||||
$this->error(__('Parameter %s can not be empty', ['']));
|
||||
}
|
||||
|
||||
$data = $this->excludeFields($data);
|
||||
if (is_array($data['rules']) && $data['rules']) {
|
||||
$menuRuleModel = new MenuRule();
|
||||
$rules = $menuRuleModel->select();
|
||||
$superAdmin = true;
|
||||
foreach ($rules as $rule) {
|
||||
if (!in_array($rule['id'], $data['rules'])) {
|
||||
$superAdmin = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($superAdmin) {
|
||||
$data['rules'] = '*';
|
||||
} else {
|
||||
$data['rules'] = implode(',', $data['rules']);
|
||||
}
|
||||
} else {
|
||||
unset($data['rules']);
|
||||
}
|
||||
|
||||
$result = false;
|
||||
Db::startTrans();
|
||||
try {
|
||||
// 模型验证
|
||||
if ($this->modelValidate) {
|
||||
$validate = str_replace("\\model\\", "\\validate\\", get_class($this->model));
|
||||
if (class_exists($validate)) {
|
||||
$validate = new $validate;
|
||||
$validate->scene('edit')->check($data);
|
||||
}
|
||||
}
|
||||
$result = $row->save($data);
|
||||
Db::commit();
|
||||
} catch (ValidateException $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
} catch (PDOException $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
} catch (Exception $e) {
|
||||
Db::rollback();
|
||||
$this->error($e->getMessage());
|
||||
}
|
||||
if ($result !== false) {
|
||||
$this->success(__('Update successful'));
|
||||
} else {
|
||||
$this->error(__('No rows updated'));
|
||||
}
|
||||
}
|
||||
|
||||
$row->pid = $row->pid ? $this->model->where('id', $row->pid)->value('name') : '';
|
||||
$row->rules = $row->rules ? explode(',', $row->rules) : [];
|
||||
$this->success('', [
|
||||
'row' => $row
|
||||
]);
|
||||
}
|
||||
|
||||
public function select()
|
||||
{
|
||||
$isTree = $this->request->param('isTree');
|
||||
$this->keyword = $this->request->request("quick_search");
|
||||
$data = $this->getGroups();
|
||||
|
||||
if ($isTree && !$this->keyword) {
|
||||
$data = $this->tree->assembleTree($this->tree->getTreeArray($data, 'name'));
|
||||
}
|
||||
$this->success('', [
|
||||
'options' => $data
|
||||
]);
|
||||
}
|
||||
|
||||
public function getGroups()
|
||||
{
|
||||
$where = [];
|
||||
if ($this->keyword) {
|
||||
$keyword = explode(' ', $this->keyword);
|
||||
foreach ($keyword as $item) {
|
||||
$where[] = [$this->quickSearchField, 'like', '%' . $item . '%'];
|
||||
}
|
||||
}
|
||||
|
||||
$data = $this->model->where('status', '1')->where($where)->select();
|
||||
// 获取第一个权限的名称
|
||||
foreach ($data as $datum) {
|
||||
if ($datum->rules) {
|
||||
if ($datum->rules == '*') {
|
||||
$datum->rules = '超级管理员';
|
||||
} else {
|
||||
$rules = explode(',', $datum->rules);
|
||||
if ($rules) {
|
||||
$rulesFirstTitle = MenuRule::where('id', $rules[0])->value('title');
|
||||
$datum->rules = count($rules) == 1 ? $rulesFirstTitle : $rulesFirstTitle . '等 ' . count($rules) . ' 项权限';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$datum->rules = '无权限';
|
||||
}
|
||||
}
|
||||
return $this->tree->assembleChild($data);
|
||||
}
|
||||
|
||||
}
|
@ -18,6 +18,8 @@ class Tree
|
||||
*/
|
||||
public static $icon = array('│', '├', '└');
|
||||
|
||||
protected $childrens = [];
|
||||
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
@ -83,4 +85,37 @@ class Tree
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据指定字段组装childrens数组
|
||||
* @param array $data 数据源
|
||||
* @param string $pid 存储上级id的字段
|
||||
* @return array
|
||||
*/
|
||||
public function assembleChild($data, $pid = 'pid')
|
||||
{
|
||||
if (!$data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 以pid组成数组
|
||||
foreach ($data as $item) {
|
||||
$this->childrens[$item[$pid]][] = $item;
|
||||
}
|
||||
if (isset($this->childrens[0])) {
|
||||
return $this->getChildren($this->childrens[0]);
|
||||
} else {
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
||||
protected function getChildren($data): array
|
||||
{
|
||||
foreach ($data as $key => $item) {
|
||||
if (array_key_exists($item['id'], $this->childrens)) {
|
||||
$data[$key]['children'] = $this->getChildren($this->childrens[$item['id']]);
|
||||
}
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
}
|
9
web/src/api/backend/auth/group.ts
Normal file
9
web/src/api/backend/auth/group.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import createAxios from '/@/utils/axios'
|
||||
import { authMenu } from '/@/api/controllerUrls'
|
||||
|
||||
export function getMenuRules() {
|
||||
return createAxios({
|
||||
url: authMenu + 'index',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
158
web/src/views/backend/auth/group/form.vue
Normal file
158
web/src/views/backend/auth/group/form.vue
Normal file
@ -0,0 +1,158 @@
|
||||
<template>
|
||||
<!-- 对话框表单 -->
|
||||
<el-dialog
|
||||
custom-class="ba-operate-dialog"
|
||||
top="10vh"
|
||||
:close-on-click-modal="false"
|
||||
:model-value="baTable.form.operate ? true : false"
|
||||
@close="baTable.toggleForm"
|
||||
>
|
||||
<template #title>
|
||||
<div class="title" v-drag="['.ba-operate-dialog', '.el-dialog__header']" v-zoom="'.ba-operate-dialog'">
|
||||
{{ baTable.form.operate ? t(baTable.form.operate) : '' }}
|
||||
</div>
|
||||
</template>
|
||||
<div
|
||||
class="ba-operate-form"
|
||||
:class="'ba-' + baTable.form.operate + '-form'"
|
||||
:style="'width: calc(100% - ' + baTable.form.labelWidth! / 2 + 'px)'"
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
@keyup.enter="baTable.onSubmit(formRef)"
|
||||
:model="baTable.form.items"
|
||||
label-position="right"
|
||||
:label-width="baTable.form.labelWidth + 'px'"
|
||||
:rules="rules"
|
||||
>
|
||||
<el-form-item label="上级分组">
|
||||
<remoteSelect
|
||||
:params="{ isTree: true }"
|
||||
field="name"
|
||||
:remote-url="baTable.api.actionUrl.get('index')"
|
||||
v-model="baTable.form.items!.pid"
|
||||
placeholder="点击选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="分组名称">
|
||||
<el-input v-model="baTable.form.items!.name" type="string" placeholder="请输入分组名称"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="auth" label="权限">
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:key="state.treeKey"
|
||||
:default-checked-keys="state.defaultCheckedKeys"
|
||||
:default-expand-all="true"
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
:props="{ children: 'children', label: 'title' }"
|
||||
:data="state.menuRules"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="状态">
|
||||
<el-radio v-model="baTable.form.items!.status" label="0" :border="true">禁用</el-radio>
|
||||
<el-radio v-model="baTable.form.items!.status" label="1" :border="true">启用</el-radio>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div :style="'width: calc(100% - ' + baTable.form.labelWidth! / 1.8 + 'px)'">
|
||||
<el-button @click="baTable.toggleForm('')">取消</el-button>
|
||||
<el-button v-blur :loading="baTable.form.submitLoading" @click="baTable.onSubmit(formRef)" type="primary">
|
||||
{{ baTable.form.operateIds && baTable.form.operateIds.length > 1 ? '保存并编辑下一项' : '保存' }}
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive, ref, watch } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import type baTableClass from '/@/utils/baTable'
|
||||
import remoteSelect from '/@/components/remoteSelect/index.vue'
|
||||
import { getMenuRules } from '/@/api/backend/auth/group'
|
||||
import { FormItemRule } from 'element-plus/es/components/form/src/form.type'
|
||||
import type { ElForm, ElTree } from 'element-plus'
|
||||
import { uuid } from '/@/utils/random'
|
||||
|
||||
interface MenuRules {
|
||||
id: number
|
||||
title: string
|
||||
children: MenuRules[]
|
||||
}
|
||||
|
||||
const treeRef = ref<InstanceType<typeof ElTree>>()
|
||||
const formRef = ref<InstanceType<typeof ElForm>>()
|
||||
|
||||
const props = defineProps<{
|
||||
baTable: baTableClass
|
||||
}>()
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const state: {
|
||||
treeKey: string
|
||||
defaultCheckedKeys: number[]
|
||||
menuRules: MenuRules[]
|
||||
} = reactive({
|
||||
treeKey: uuid(),
|
||||
defaultCheckedKeys: [],
|
||||
menuRules: [],
|
||||
})
|
||||
|
||||
const rules: Partial<Record<string, FormItemRule[]>> = reactive({
|
||||
name: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入分组名称',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
auth: [
|
||||
{
|
||||
validator: (rule: any, val: string, callback: Function) => {
|
||||
let ids = getCheckeds()
|
||||
if (ids.length <= 0) {
|
||||
return callback(new Error('请选择权限'))
|
||||
}
|
||||
return callback()
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
getMenuRules().then((res) => {
|
||||
state.menuRules = res.data.list
|
||||
})
|
||||
|
||||
const getCheckeds = () => {
|
||||
return treeRef.value!.getCheckedKeys()
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getCheckeds,
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.baTable.form.items!.rules,
|
||||
() => {
|
||||
if (props.baTable.form.items!.rules && props.baTable.form.items!.rules.length) {
|
||||
if (props.baTable.form.items!.rules.includes('*')) {
|
||||
let arr: number[] = []
|
||||
for (const key in state.menuRules) {
|
||||
arr.push(state.menuRules[key].id)
|
||||
}
|
||||
state.defaultCheckedKeys = arr
|
||||
} else {
|
||||
state.defaultCheckedKeys = props.baTable.form.items!.rules
|
||||
}
|
||||
} else {
|
||||
state.defaultCheckedKeys = []
|
||||
}
|
||||
state.treeKey = uuid()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
@ -1,12 +1,94 @@
|
||||
<template></template>
|
||||
<template>
|
||||
<div class="default-main ba-table-box">
|
||||
<el-alert class="ba-table-alert" v-if="baTable.table.remark" :title="baTable.table.remark" type="info" show-icon />
|
||||
<!-- 表格顶部菜单 -->
|
||||
<TableHeader
|
||||
:field="baTable.table.column"
|
||||
:buttons="['refresh', 'add', 'edit', 'delete', 'unfold']"
|
||||
:enable-batch-opt="baTable.table.selection!.length > 0 ? true : false"
|
||||
:unfold="baTable.table.expandAll"
|
||||
:quick-search-placeholder="'通过组名模糊搜索'"
|
||||
@action="baTable.onTableHeaderAction"
|
||||
/>
|
||||
<!-- 表格 -->
|
||||
<!-- 要使用`el-table`组件原有的属性,直接加在Table标签上即可 -->
|
||||
<Table
|
||||
ref="tableRef"
|
||||
:default-expand-all="baTable.table.expandAll"
|
||||
:data="baTable.table.data"
|
||||
:field="baTable.table.column"
|
||||
:row-key="baTable.table.pk"
|
||||
:loading="baTable.table.loading"
|
||||
:pagination="false"
|
||||
@action="baTable.onTableAction"
|
||||
@row-dblclick="baTable.onTableDblclick"
|
||||
/>
|
||||
<Form ref="formRef" :ba-table="baTable" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue'
|
||||
import baTableClass from '/@/utils/baTable'
|
||||
import { authGroup } from '/@/api/controllerUrls'
|
||||
import { baTableApi } from '/@/api/common'
|
||||
import { authGroup } from '/@/api/controllerUrls'
|
||||
import Table from '/@/components/table/index.vue'
|
||||
import TableHeader from '/@/components/table/header/index.vue'
|
||||
import Form from './form.vue'
|
||||
import { defaultOptButtons } from '/@/components/table'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
|
||||
const baTable = new baTableClass(new baTableApi(authGroup), {
|
||||
column: [],
|
||||
const formRef = ref()
|
||||
const tableRef = ref()
|
||||
const { t } = useI18n()
|
||||
|
||||
const baTable = new baTableClass(
|
||||
new baTableApi(authGroup),
|
||||
{
|
||||
expandAll: true,
|
||||
dblClickNotEditColumn: [undefined, 'status'],
|
||||
column: [
|
||||
{ type: 'selection', align: 'center' },
|
||||
{ label: '组别名称', prop: 'name', align: 'left' },
|
||||
{ label: '权限', prop: 'rules', align: 'center' },
|
||||
{ label: '状态', prop: 'status', align: 'center', width: '80', render: 'switch' },
|
||||
{ label: '更新时间', prop: 'updatetime', align: 'center', width: '160', render: 'datetime' },
|
||||
{ label: '创建时间', prop: 'createtime', align: 'center', width: '160', render: 'datetime' },
|
||||
{
|
||||
label: '操作',
|
||||
align: 'center',
|
||||
width: '130',
|
||||
render: 'buttons',
|
||||
buttons: defaultOptButtons(['edit', 'delete']),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
defaultItems: {
|
||||
status: '1',
|
||||
},
|
||||
},
|
||||
{
|
||||
// 提交前
|
||||
onSubmit: () => {
|
||||
baTable.form.items!.rules = formRef.value.getCheckeds()
|
||||
if (baTable.form.items?.pid == baTable.form.items?.pidebak) {
|
||||
delete baTable.form.items?.pid
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
// 获得编辑数据后
|
||||
requestEdit: () => {
|
||||
baTable.form.items!['pidebak'] = baTable.form.items!.pid
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
baTable.table.ref = tableRef.value
|
||||
baTable.mount()
|
||||
baTable.getIndex()
|
||||
})
|
||||
</script>
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user