feat: 角色管理

fix/0524_ch
向文可 2 years ago
parent fb14c93ae6
commit e40eefde1e

@ -0,0 +1,42 @@
import request from '@/utils/request.js';
export const search = (params) => {
return request({
url: '/uc/role',
method: 'get',
params,
});
};
export const detail = (id) => {
return request({
url: '/uc/role/' + id,
method: 'get',
});
};
export const create = (data) => {
return request({
url: '/uc/role',
method: 'post',
data,
});
};
export const update = (data) => {
return request({
url: '/uc/role/' + data.id,
method: 'put',
data,
});
};
export const updateMenu = (data) => {
return request({
url: '/uc/role/menu',
method: 'put',
data,
});
};
export const remove = (idList) => {
return request({
url: '/uc/role',
method: 'delete',
params: { idList },
});
};

@ -27,6 +27,15 @@ export default [
icon: 'menu-2',
},
},
{
path: 'role',
name: 'RoleManagement',
component: () => import('@/views/permission/role/index.vue'),
meta: {
title: '角色管理',
icon: 'account-box-fill',
},
},
{
path: 'employee',
name: 'EmployeeManagement',

@ -1,5 +1,5 @@
import * as deptAPI from '@/api/permission/dept.js';
import * as api from '@/api/system/employee.js';
import * as api from '@/api/permission/employee.js';
import { ElMessage, ElMessageBox } from '@/plugins/element-plus';
const state = () => ({
code: 'EmployeeManagement',

@ -0,0 +1,115 @@
import * as featureAPI from '@/api/permission/feature.js';
import * as menuAPI from '@/api/permission/menu.js';
import * as api from '@/api/permission/role.js';
import * as systemAPI from '@/api/permission/system.js';
import { ElMessage, ElMessageBox } from '@/plugins/element-plus';
const state = () => ({
code: 'RoleManagement',
condition: {},
list: [],
total: 0,
opts: {
init: false,
system: [],
status: [
{
label: '启用',
value: true,
},
{
label: '禁用',
value: false,
},
],
},
});
const getters = {};
const mutations = {
setCode: (state, data) => (state.code = data),
setCondition: (state, data) => (state.condition = data),
setList: (state, data) => (state.list = data),
setTotal: (state, data) => (state.total = data),
setOpts: (state, data) => (state.opts = data),
};
const actions = {
search: async ({ state, commit, rootGetters }) => {
let res = await api.search({ ...rootGetters['local/page'](state.code), ...state.condition });
commit('setList', res?.records || []);
commit('setTotal', res?.total || 0);
if (!res) {
ElMessage.error('查询失败');
}
return res;
},
load: async ({ state, commit }) => {
commit('setOpts', {
...state.opts,
init: true,
system: await systemAPI.search(),
});
},
loadMenu: async (context, systemId) => {
return await menuAPI.search({ systemId });
},
loadPermission: async (context, menuId) => {
return await featureAPI.search({ menuId });
},
updateMenu: async (context, data) => {
let res = await api.updateMenu(data);
if (!res) {
ElMessage.error('保存菜单权限失败');
}
return res;
},
detail: async (context, id) => {
let res = await api.detail(id);
if (!res) {
ElMessage.error('加载详情失败');
}
return res;
},
save: async ({ dispatch }, data) => {
let save = data.id ? api.update : api.create;
let res = await save(data);
if (res) {
ElMessage.success('保存成功');
dispatch('search');
} else {
ElMessage.error('保存失败');
}
return res;
},
remove: async ({ dispatch }, idList) => {
if (!idList.length) {
ElMessage.warning('请选择要删除的数据');
} else {
try {
await ElMessageBox.confirm('数据删除后无法恢复,确定要删除吗?', '危险操作');
let res = await api.remove(idList.join(','));
if (res) {
ElMessage.success('删除成功');
dispatch('search');
} else {
ElMessage.error('删除失败');
}
} catch (e) {
console.info('取消删除', e);
}
}
},
enable: async ({ dispatch }, data) => {
let res = await api.update(data);
if (res) {
ElMessage.success((data.isEnable ? '启用' : '禁用') + '成功');
dispatch('search');
} else {
ElMessage.error((data.isEnable ? '启用' : '禁用') + '失败');
}
},
};
export default {
state,
getters,
mutations,
actions,
};

@ -44,7 +44,7 @@
<script setup lang="jsx">
import ElButton from '@/components/extra/ElButton.vue';
import { ElSwitch } from 'element-plus/es';
import ElSwitch from '@/components/extra/ElSwitch.vue';
const router = useRouter();
const store = useStore();
const loading = ref(false);

@ -0,0 +1,383 @@
<template>
<div class="list-container">
<table-list
v-loading="loading"
:code="code"
:config="config"
:data="list"
:operation="['create', 'search']"
:reset="handleReset"
title="角色"
:total="total"
@create="handleCreate()"
@remove="handleRemove"
@search="handleSearch"
>
<template #search>
<el-form inline>
<el-form-item label="所属系统" prop="systemId">
<el-select
v-model="state.condition.systemId"
:config="{ label: 'systemName', value: 'id' }"
:opts="opts.system"
/>
</el-form-item>
<el-form-item label="状态" prop="isEnable">
<el-select v-model="state.condition.isEnable" :opts="opts.status" />
</el-form-item>
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="state.condition.roleName" />
</el-form-item>
</el-form>
</template>
</table-list>
<el-dialog v-model="formState.formVisible" :title="(formState.form.id ? '编辑' : '新增') + '角色'">
<el-tabs v-model="formState.currentTab">
<el-tab-pane label="基本信息" name="1">
<el-form
ref="refsForm"
v-loading="formState.submitting"
label-width="100px"
:model="formState.form"
:rules="formState.rules1"
>
<el-form-item label="所属系统" prop="systemId">
<el-select
v-model="formState.form.systemId"
:config="{ label: 'systemName', value: 'id' }"
:opts="opts.system"
/>
</el-form-item>
<el-form-item label="角色名称" prop="roleName">
<el-input v-model="formState.form.roleName" />
</el-form-item>
<el-form-item label="角色描述" prop="roleDesc">
<el-input v-model="formState.form.roleDesc" />
</el-form-item>
<el-form-item label="是否启用" prop="isEnable">
<el-switch v-model="formState.form.isEnable" />
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="角色权限" name="2">
<el-form
ref="refsForm2"
v-loading="formState.submitting"
class="form2"
inline
label-position="top"
:model="formState.form"
:rules="formState.rules2"
>
<el-form-item label="系统模块菜单" prop="menuIds">
<el-tree
:data="menuList"
:default-checked-keys="formState.form.menuIds"
default-expand-all
:expand-on-click-node="false"
highlight-current
node-key="id"
:props="{
label: 'title',
value: 'id',
children: 'menuChild',
}"
show-checkbox
@check-change="handleCheckMenu"
@node-click="handleLoadPermission"
/>
</el-form-item>
<el-form-item label="功能权限" prop="permissionIds">
<el-checkbox v-model="checkAll"></el-checkbox>
<el-tree
ref="refsTree"
:data="permissionList"
:default-checked-keys="formState.form.permissionIds"
default-expand-all
:expand-on-click-node="false"
highlight-current
node-key="id"
:props="{
label: 'name',
value: 'id',
}"
show-checkbox
@check-change="handleCheckPermission"
/>
</el-form-item>
</el-form>
</el-tab-pane>
<el-tab-pane label="绑定组织" name="3">
<el-form
ref="refsForm3"
v-loading="formState.submitting"
inline
label-position="top"
:model="formState.form"
:rules="formState.rules3"
>
<el-form-item label="选择绑定组织" prop="menuIds" />
</el-form>
</el-tab-pane>
</el-tabs>
<template #footer>
<el-button @click="handleCancel"></el-button>
<el-button :loading="formState.submitting" type="primary" @click="handleSave"></el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="jsx">
import ElButton from '@/components/extra/ElButton.vue';
import ElSwitch from '@/components/extra/ElSwitch.vue';
const { proxy } = getCurrentInstance();
const store = useStore();
const loading = ref(false);
const code = computed(() => store.state.role.code);
const list = computed(() => store.state.role.list);
const total = computed(() => store.state.role.total);
const opts = computed(() => store.state.role.opts);
if (!unref(opts).init) {
store.dispatch('role/load');
}
const state = reactive({
condition: {
systemId: null,
isEnable: null,
roleName: null,
},
});
watch(
() => state.condition,
(value) => {
store.commit('role/setCondition', _.cloneDeep(value));
},
{ immediate: true, deep: true }
);
const handleReset = () => {
state.condition = {
systemId: null,
isEnable: null,
roleName: null,
};
};
const handleSearch = async () => {
loading.value = true;
await store.dispatch('role/search');
loading.value = false;
};
onActivated(handleSearch);
const handleRemove = async (rows) => {
store.dispatch(
'role/remove',
rows.map((item) => item.id)
);
};
const handleEnable = async (row) => {
loading.value = true;
await store.dispatch('role/enable', { id: row.id, isEnable: !row.isEnable });
loading.value = false;
};
/* 表单 */
const refsForm = ref(null);
const refsForm2 = ref(null);
const refsForm3 = ref(null);
const menuList = ref([]);
const permissionList = ref([]);
const formState = reactive({
currentTab: '1',
formVisible: false,
submitting: false,
form: {
id: null,
systemId: null,
roleName: null,
roleDesc: null,
isEnable: true,
menuIds: [],
permissionIds: [],
},
rules1: {
systemId: [{ required: true, message: '所属系统不能为空' }],
roleName: [{ required: true, message: '角色名称不能为空' }],
roleDesc: [{ required: true, message: '角色描述不能为空' }],
isEnable: [{ required: true, message: '是否启用不能为空' }],
},
rules2: {
menuIds: [{ required: true, message: '功能权限不能为空' }],
},
});
watch(
() => formState.currentTab,
async (value) => {
let res = await store.dispatch('role/detail', formState.form.id);
res.menuIds = [];
res.permissionIds = [];
const deep = (arr) => {
(arr || []).forEach((item) => {
res.menuIds.push(item.id);
res.permissionIds.push(
...(item.permissionChild || []).filter((item) => item.isHave).map((item) => item.id)
);
deep(item.menuChild);
});
};
deep(res.roleMenuTree);
delete res.roleMenuTree;
Object.assign(formState.form, res);
if (value === '2') {
menuList.value = await store.dispatch('role/loadMenu', formState.form.systemId);
}
}
);
const refsTree = ref(null);
const checkAll = ref(false);
watch(checkAll, (value) => {
unref(refsTree).setCheckedKeys(value ? unref(permissionList).map((item) => item.id) : []);
});
const handleLoadPermission = async (data) => {
permissionList.value = await store.dispatch('role/loadPermission', data.id);
};
const handleCheckMenu = (data, checked, childChecked) => {
if (checked || childChecked) {
formState.form.menuIds.push(data.id);
} else {
formState.form.menuIds.splice(formState.form.menuIds.indexOf(data.id), 1);
}
};
const handleCheckPermission = (data, checked, childChecked) => {
if (checked || childChecked) {
formState.form.permissionIds.push(data.id);
} else {
formState.form.permissionIds.splice(formState.form.permissionIds.indexOf(data.id), 1);
}
};
const handleCreate = (row) => {
formState.formVisible = true;
Object.assign(
formState.form,
row || {
id: null,
systemId: null,
roleName: null,
roleDesc: null,
}
);
};
const handleCancel = () => {
formState.formVisible = false;
};
const handleSave = async () => {
formState.submitting = true;
try {
await proxy.$validate(
formState.currentTab === '1' ? refsForm : formState.currentTab === '2' ? refsForm2 : refsForm3
);
let data = _.cloneDeep(formState.form);
let res = await store.dispatch(
formState.currentTab === '1'
? 'role/save'
: formState.currentTab === '2'
? 'role/updateMenu'
: 'role/updateDept',
data
);
if (res && formState.currentTab === '1') {
formState.form.id = res.id;
}
if (res && formState.currentTab === '3') {
formState.formVisible = false;
} else {
formState.currentTab = +formState.currentTab + 1 + '';
}
} catch (e) {
console.info('取消保存', e);
}
formState.submitting = false;
};
const config = reactive({
//
columns: [
{
label: 'ID',
prop: 'id',
width: 120,
},
{
label: '角色名称',
prop: 'roleName',
minWidth: 160,
},
{
label: '角色描述',
prop: 'phone',
minWidth: 160,
},
{
label: '所属系统',
prop: 'systemName',
minWidth: 160,
},
{
label: '绑定组织',
prop: 'roles',
minWidth: 160,
slots: {
default: ({ row }) => row.departmentList?.map((item) => <ElTag>{item.name}</ElTag>) || '无',
},
},
{
label: '状态',
prop: 'isEnable',
width: 100,
slots: {
default: ({ row }) => <ElSwitch modelValue={row.isEnable} onInput={() => handleEnable(row)} />,
},
},
{
label: '创建人',
prop: 'createUserName',
width: 160,
},
{
label: '创建时间',
prop: 'createTime',
width: 180,
},
{
label: '操作',
fixed: 'right',
slots: {
default: ({ row }) => (
<div>
<ElButton type="text" onClick={() => handleCreate(row)}>
编辑
</ElButton>
<ElButton type="text" onClick={() => handleRemove([row])}>
删除
</ElButton>
</div>
),
},
width: 120,
},
],
});
</script>
<style lang="less" scoped>
.form2 {
display: flex;
.el-form-item {
width: 50%;
flex: 1;
:deep(.el-form-item__content) {
.el-tree {
width: 100%;
}
}
}
}
</style>

@ -33,7 +33,7 @@
<script setup lang="jsx">
import ElImage from '@/components/extra/ElImage.vue';
import { ElSwitch } from 'element-plus/es';
import ElSwitch from '@/components/extra/ElSwitch.vue';
const { proxy } = getCurrentInstance();
const store = useStore();
const loading = ref(false);

Loading…
Cancel
Save