mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
refactor: 系统模块角色分配相关优化
This commit is contained in:
@@ -148,7 +148,7 @@
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<el-row class="mt20" type="flex" justify="end">
|
||||
<el-row v-if="props.pageable" class="mt20" type="flex" justify="end">
|
||||
<el-pagination
|
||||
:small="props.size == 'small'"
|
||||
@current-change="handlePageNumChange"
|
||||
@@ -166,7 +166,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots } from 'vue';
|
||||
import { toRefs, watch, reactive, onMounted, Ref, ref, useSlots, toValue } from 'vue';
|
||||
import { TableColumn } from './index';
|
||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||
import { useThemeConfig } from '@/store/themeConfig';
|
||||
@@ -188,6 +188,7 @@ export interface PageTableProps {
|
||||
columns: TableColumn[]; // 列配置项 ==> 必传
|
||||
showSelection?: boolean;
|
||||
selectable?: (row: any) => boolean; // 是否可选
|
||||
pageable?: boolean;
|
||||
showSearch?: boolean; // 是否显示搜索表单
|
||||
data?: any[]; // 静态 table data 数据,若存在则不会使用 requestApi 返回的 data ==> 非必传
|
||||
lazy?: boolean; // 是否自动执行请求 api ==> 非必传(默认为false)
|
||||
@@ -197,12 +198,13 @@ export interface PageTableProps {
|
||||
queryForm?: any; // 查询表单参数 ==> 非必传(默认为{pageNum:1, pageSize: 10})
|
||||
border?: boolean; // 是否带有纵向边框 ==> 非必传(默认为false)
|
||||
toolButton?: ('setting' | 'search')[] | boolean; // 是否显示表格功能按钮 ==> 非必传(默认为true)
|
||||
searchCol?: any; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 }
|
||||
searchCol?: any; // 表格搜索项 每列占比配置 ==> 非必传 { xs: 1, sm: 2, md: 2, lg: 3, xl: 4 } | number 如 3
|
||||
}
|
||||
|
||||
// 接受父组件参数,配置默认值
|
||||
const props = withDefaults(defineProps<PageTableProps>(), {
|
||||
columns: () => [],
|
||||
pageable: true,
|
||||
showSelection: false,
|
||||
lazy: false,
|
||||
queryForm: {
|
||||
@@ -250,6 +252,7 @@ const changeSimpleFormItem = (searchItem: SearchItem) => {
|
||||
const queryForm_: Ref<any> = useVModel(props, 'queryForm', emit);
|
||||
|
||||
const { tableData, total, loading, search, reset, getTableData, handlePageNumChange, handlePageSizeChange } = usePageTable(
|
||||
props.pageable,
|
||||
props.pageApi,
|
||||
queryForm_,
|
||||
props.beforeQueryFn,
|
||||
@@ -324,9 +327,14 @@ const handleSelectionChange = (val: any) => {
|
||||
emit('update:selectionData', val);
|
||||
};
|
||||
|
||||
const getData = () => {
|
||||
return toValue(tableData);
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
tableRef: tableRef,
|
||||
search: getTableData,
|
||||
getData,
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -121,7 +121,7 @@ export class TableColumn {
|
||||
|
||||
/**
|
||||
* 使用标签类型展示该列(用于枚举值友好展示)
|
||||
* @param param 枚举对象
|
||||
* @param param 枚举对象, 如AccountStatusEnum
|
||||
* @returns this
|
||||
*/
|
||||
typeTag(param: any): TableColumn {
|
||||
|
||||
@@ -4,11 +4,13 @@ import { reactive, toRefs } from 'vue';
|
||||
|
||||
/**
|
||||
* @description table 页面操作方法封装
|
||||
* @param pageable 是否为分页获取
|
||||
* @param {Api} api 获取表格数据 api (必传)
|
||||
* @param {Object} param 获取数据请求参数 (非必传,默认为{pageNum: 1, pageSize: 10})
|
||||
* @param {Function} dataCallBack 对api请求返回的数据进行处理的回调方法 (非必传)
|
||||
* */
|
||||
export const usePageTable = (
|
||||
pageable: boolean = true,
|
||||
api?: Api,
|
||||
params: any = {
|
||||
// 当前页数
|
||||
@@ -46,8 +48,12 @@ export const usePageTable = (
|
||||
let res = await api.request(sp);
|
||||
dataCallBack && (res = dataCallBack(res));
|
||||
|
||||
state.tableData = res.list;
|
||||
state.total = res.total;
|
||||
if (pageable) {
|
||||
state.tableData = res.list;
|
||||
state.total = res.total;
|
||||
} else {
|
||||
state.tableData = res;
|
||||
}
|
||||
} catch (error: any) {
|
||||
ElMessage.error(error?.message);
|
||||
} finally {
|
||||
@@ -55,12 +61,19 @@ export const usePageTable = (
|
||||
}
|
||||
};
|
||||
|
||||
const setPageNum = (pageNum: number) => {
|
||||
if (!pageable) {
|
||||
return;
|
||||
}
|
||||
state.searchParams.pageNum = pageNum;
|
||||
};
|
||||
|
||||
/**
|
||||
* @description 表格数据查询(pageNum = 1)
|
||||
* @return void
|
||||
* */
|
||||
const search = () => {
|
||||
state.searchParams.pageNum = 1;
|
||||
setPageNum(1);
|
||||
getTableData();
|
||||
};
|
||||
|
||||
@@ -69,7 +82,7 @@ export const usePageTable = (
|
||||
* @return void
|
||||
* */
|
||||
const reset = () => {
|
||||
state.searchParams.pageNum = 1;
|
||||
setPageNum(1);
|
||||
for (let prop of Object.keys(state.searchParams)) {
|
||||
if (prop == 'pageNum' || prop == 'pageSize') {
|
||||
continue;
|
||||
@@ -85,7 +98,7 @@ export const usePageTable = (
|
||||
* @return void
|
||||
* */
|
||||
const handlePageSizeChange = (val: number) => {
|
||||
state.searchParams.pageNum = 1;
|
||||
setPageNum(1);
|
||||
state.searchParams.pageSize = val;
|
||||
getTableData();
|
||||
};
|
||||
|
||||
@@ -106,20 +106,7 @@
|
||||
|
||||
<el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
|
||||
<el-form :model="showMemDialog.memForm" label-width="auto">
|
||||
<el-form-item label="账号">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
remote
|
||||
:remote-method="getAccount"
|
||||
v-model="showMemDialog.memForm.accountIds"
|
||||
filterable
|
||||
multiple
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
>
|
||||
<el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="`${item.username} [${item.name}]`" :value="item.id">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<AccountSelectFormItem v-model="showMemDialog.memForm.accountIds" multiple focus />
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
@@ -135,12 +122,12 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
|
||||
import { tagApi } from './api';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
|
||||
|
||||
const teamForm: any = ref(null);
|
||||
const tagTreeRef: any = ref(null);
|
||||
@@ -267,14 +254,6 @@ const showMembers = async (team: any) => {
|
||||
state.showMemDialog.title = `[${team.name}] 成员信息`;
|
||||
};
|
||||
|
||||
const getAccount = (username: any) => {
|
||||
if (username) {
|
||||
accountApi.list.request({ username }).then((res) => {
|
||||
state.showMemDialog.accounts = res.list;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const deleteMember = async (data: any) => {
|
||||
await tagApi.delTeamMem.request(data);
|
||||
ElMessage.success('移除成功');
|
||||
|
||||
@@ -16,12 +16,6 @@
|
||||
>
|
||||
</template>
|
||||
|
||||
<template #showmore="{ data }">
|
||||
<el-link @click.prevent="showRoles(data)" type="success">角色</el-link>
|
||||
|
||||
<el-link class="ml5" @click.prevent="showResources(data)" type="info">菜单&权限</el-link>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button link v-if="actionBtns[perms.addAccount]" @click="editAccount(data)" type="primary">编辑</el-button>
|
||||
|
||||
@@ -54,33 +48,16 @@
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog :title="showResourceDialog.title" v-model="showResourceDialog.visible" width="400px">
|
||||
<el-tree
|
||||
style="height: 50vh; overflow: auto"
|
||||
:data="showResourceDialog.resources"
|
||||
node-key="id"
|
||||
:props="showResourceDialog.defaultProps"
|
||||
:expand-on-click-node="true"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<span v-if="data.type == ResourceTypeEnum.Menu.value">{{ node.label }}</span>
|
||||
<span v-if="data.type == ResourceTypeEnum.Permission.value" style="color: #67c23a">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-dialog>
|
||||
|
||||
<role-edit v-model:visible="roleDialog.visible" :account="roleDialog.account" @cancel="cancel()" />
|
||||
<role-allocation v-model:visible="roleDialog.visible" :account="roleDialog.account" @cancel="cancel()" />
|
||||
<account-edit v-model:visible="accountDialog.visible" v-model:account="accountDialog.data" @val-change="valChange()" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
|
||||
import RoleEdit from './RoleEdit.vue';
|
||||
import RoleAllocation from './RoleAllocation.vue';
|
||||
import AccountEdit from './AccountEdit.vue';
|
||||
import { AccountStatusEnum, ResourceTypeEnum } from '../enums';
|
||||
import { AccountStatusEnum } from '../enums';
|
||||
import { accountApi } from '../api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
@@ -102,7 +79,6 @@ const columns = [
|
||||
TableColumn.new('username', '用户名'),
|
||||
TableColumn.new('status', '状态').typeTag(AccountStatusEnum),
|
||||
TableColumn.new('lastLoginTime', '最后登录时间').isTime(),
|
||||
TableColumn.new('showmore', '查看更多').isSlot().setMinWidth(150),
|
||||
TableColumn.new('creator', '创建账号'),
|
||||
TableColumn.new('createTime', '创建时间').isTime(),
|
||||
TableColumn.new('modifier', '更新账号'),
|
||||
@@ -152,7 +128,7 @@ const state = reactive({
|
||||
},
|
||||
});
|
||||
|
||||
const { selectionData, query, showRoleDialog, showResourceDialog, roleDialog, accountDialog } = toRefs(state);
|
||||
const { selectionData, query, showRoleDialog, roleDialog, accountDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
if (Object.keys(actionBtns).length > 0) {
|
||||
@@ -164,25 +140,6 @@ const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const showResources = async (row: any) => {
|
||||
let showResourceDialog = state.showResourceDialog;
|
||||
showResourceDialog.title = '"' + row.username + '" 的菜单&权限';
|
||||
showResourceDialog.resources = [];
|
||||
showResourceDialog.resources = await accountApi.resources.request({
|
||||
id: row.id,
|
||||
});
|
||||
showResourceDialog.visible = true;
|
||||
};
|
||||
|
||||
const showRoles = async (row: any) => {
|
||||
let showRoleDialog = state.showRoleDialog;
|
||||
showRoleDialog.title = '"' + row.username + '" 的角色信息';
|
||||
showRoleDialog.accountRoles = await accountApi.roles.request({
|
||||
id: row.id,
|
||||
});
|
||||
showRoleDialog.visible = true;
|
||||
};
|
||||
|
||||
const changeStatus = async (row: any) => {
|
||||
let id = row.id;
|
||||
let status = row.status == -1 ? 1 : -1;
|
||||
@@ -203,11 +160,6 @@ const resetOtpSecret = async (row: any) => {
|
||||
row.otpSecret = '-';
|
||||
};
|
||||
|
||||
const showRoleEdit = (data: any) => {
|
||||
state.roleDialog.visible = true;
|
||||
state.roleDialog.account = data;
|
||||
};
|
||||
|
||||
const editAccount = (data: any) => {
|
||||
if (!data) {
|
||||
state.accountDialog.data = null;
|
||||
@@ -217,6 +169,11 @@ const editAccount = (data: any) => {
|
||||
state.accountDialog.visible = true;
|
||||
};
|
||||
|
||||
const showRoleEdit = (data: any) => {
|
||||
state.roleDialog.visible = true;
|
||||
state.roleDialog.account = data;
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
state.roleDialog.visible = false;
|
||||
state.roleDialog.account = null;
|
||||
|
||||
209
mayfly_go_web/src/views/system/account/RoleAllocation.vue
Executable file
209
mayfly_go_web/src/views/system/account/RoleAllocation.vue
Executable file
@@ -0,0 +1,209 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
@open="searchAccountRoles()"
|
||||
:title="account == null ? '' : '分配“' + account.username + '”的角色'"
|
||||
v-model="dialogVisible"
|
||||
:before-close="cancel"
|
||||
:destroy-on-close="true"
|
||||
width="55%"
|
||||
>
|
||||
<el-tabs style="min-height: 540px" v-model="state.tabName" @tab-change="onTabChange">
|
||||
<el-tab-pane label="已分配" :name="relatedTabName">
|
||||
<page-table
|
||||
ref="relatePageTableRef"
|
||||
:pageable="false"
|
||||
:page-api="accountApi.roles"
|
||||
v-model:query-form="releateQuery"
|
||||
:columns="relatedColumns"
|
||||
:tool-button="false"
|
||||
lazy
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button @click="showResources" icon="view" type="primary" link>用户菜单&权限</el-button>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button v-auth="'account:saveRoles'" type="danger" @click="relateRole(-1, data.roleId)" icon="delete" link plain>移除</el-button>
|
||||
</template>
|
||||
</page-table>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="未分配" :name="unRelatedTabName">
|
||||
<page-table
|
||||
ref="unRelatePageTableRef"
|
||||
:page-api="roleApi.list"
|
||||
:search-items="unRelatedSearchItems"
|
||||
v-model:query-form="unRelatedQuery"
|
||||
:columns="unRelatedColumns"
|
||||
:search-col="3"
|
||||
lazy
|
||||
>
|
||||
<template #action="{ data }">
|
||||
<el-button
|
||||
v-auth="'account:saveRoles'"
|
||||
@click="relateRole(1, data.id)"
|
||||
:disabled="data.code?.indexOf('COMMON') == 0 || data.status == RoleStatusEnum.Disable.value"
|
||||
type="success"
|
||||
icon="CirclePlus"
|
||||
link
|
||||
plain
|
||||
>分配</el-button
|
||||
>
|
||||
</template>
|
||||
</page-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-dialog :title="showResourceDialog.title" v-model="showResourceDialog.visible" width="400px">
|
||||
<el-tree
|
||||
style="height: 50vh; overflow: auto"
|
||||
:data="showResourceDialog.resources"
|
||||
node-key="id"
|
||||
:props="showResourceDialog.defaultProps"
|
||||
:expand-on-click-node="true"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span class="custom-tree-node">
|
||||
<span v-if="data.type == ResourceTypeEnum.Menu.value">{{ node.label }}</span>
|
||||
<span v-if="data.type == ResourceTypeEnum.Permission.value" style="color: #67c23a">{{ node.label }}</span>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, ref } from 'vue';
|
||||
import { roleApi, accountApi } from '../api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import { ResourceTypeEnum, RoleStatusEnum } from '../enums';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
account: Object,
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
|
||||
const relatedColumns = [
|
||||
TableColumn.new('roleName', '角色名'),
|
||||
TableColumn.new('code', '角色code'),
|
||||
TableColumn.new('status', '角色状态').typeTag(RoleStatusEnum),
|
||||
TableColumn.new('creator', '分配者'),
|
||||
TableColumn.new('createTime', '分配时间').isTime(),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(80).fixedRight().alignCenter(),
|
||||
];
|
||||
|
||||
const unRelatedSearchItems = [SearchItem.input('name', '角色名'), SearchItem.input('code', '角色code')];
|
||||
const unRelatedColumns = [
|
||||
TableColumn.new('name', '角色名'),
|
||||
TableColumn.new('code', '角色code'),
|
||||
TableColumn.new('status', '角色状态').typeTag(RoleStatusEnum),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(80).fixedRight().alignCenter(),
|
||||
];
|
||||
|
||||
const relatePageTableRef: any = ref(null);
|
||||
const unRelatePageTableRef: any = ref(null);
|
||||
|
||||
// 已分配与未分配tab名
|
||||
const relatedTabName = 'related';
|
||||
const unRelatedTabName = 'unRelated';
|
||||
|
||||
const state = reactive({
|
||||
tabName: relatedTabName,
|
||||
// 该账号拥有的角色id
|
||||
unRelatedQuery: {
|
||||
pageNum: 1,
|
||||
pageSize: 7,
|
||||
name: null,
|
||||
code: null,
|
||||
notIds: '',
|
||||
},
|
||||
releateQuery: {
|
||||
id: 0, //账号id
|
||||
},
|
||||
showResourceDialog: {
|
||||
title: '',
|
||||
visible: false,
|
||||
resources: [],
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
let relatedRoleIds: Number[] = []; // 用户已关联的角色ids
|
||||
|
||||
const { releateQuery, unRelatedQuery, showResourceDialog } = toRefs(state);
|
||||
|
||||
const dialogVisible = useVModel(props, 'visible', emit);
|
||||
|
||||
const searchAccountRoles = async () => {
|
||||
state.releateQuery.id = props.account?.id;
|
||||
await relatePageTableRef.value?.search();
|
||||
relatedRoleIds = relatePageTableRef.value.getData()?.map((x: any) => x.roleId);
|
||||
};
|
||||
|
||||
const searchUnRelateRoles = () => {
|
||||
state.unRelatedQuery.notIds = relatedRoleIds.join(',');
|
||||
unRelatePageTableRef.value?.search();
|
||||
};
|
||||
|
||||
const onTabChange = () => {
|
||||
if (state.tabName == unRelatedTabName) {
|
||||
searchUnRelateRoles();
|
||||
return;
|
||||
}
|
||||
|
||||
searchAccountRoles();
|
||||
};
|
||||
|
||||
const relateRole = async (relateType: number, roleId: number) => {
|
||||
await accountApi.saveRole.request({
|
||||
id: props.account!.id,
|
||||
roleId,
|
||||
relateType,
|
||||
});
|
||||
ElMessage.success('操作成功');
|
||||
if (state.tabName == relatedTabName) {
|
||||
searchAccountRoles();
|
||||
} else {
|
||||
relatedRoleIds.push(roleId);
|
||||
searchUnRelateRoles();
|
||||
}
|
||||
};
|
||||
|
||||
const showResources = async () => {
|
||||
let showResourceDialog = state.showResourceDialog;
|
||||
showResourceDialog.title = '"' + props.account?.username + '" 的菜单&权限';
|
||||
showResourceDialog.resources = [];
|
||||
showResourceDialog.resources = await accountApi.resources.request({
|
||||
id: props.account?.id,
|
||||
});
|
||||
showResourceDialog.visible = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消
|
||||
*/
|
||||
const cancel = () => {
|
||||
state.unRelatedQuery.pageNum = 1;
|
||||
state.unRelatedQuery.name = null;
|
||||
state.unRelatedQuery.code = null;
|
||||
state.tabName = relatedTabName;
|
||||
dialogVisible.value = false;
|
||||
emit('cancel');
|
||||
};
|
||||
</script>
|
||||
@@ -1,166 +0,0 @@
|
||||
<template>
|
||||
<div class="account-dialog">
|
||||
<el-dialog :title="account == null ? '' : '分配“' + account.username + '”的角色'" v-model="dialogVisible" :before-close="cancel" :show-close="false">
|
||||
<div class="card pd5">
|
||||
<div>
|
||||
<el-input placeholder="请输入角色名" style="width: 150px" v-model="query.name" @clear="clear()" clearable> </el-input>
|
||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="allRole" border ref="roleTable" @select="select" style="width: 100%">
|
||||
<el-table-column :selectable="selectable" type="selection" width="40"></el-table-column>
|
||||
<el-table-column prop="name" label="角色名称"></el-table-column>
|
||||
<el-table-column prop="code" label="角色code"></el-table-column>
|
||||
<el-table-column prop="remark" label="角色描述">
|
||||
<template #default="scope">
|
||||
{{ scope.row.remark ? scope.row.remark : '暂无描述' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row type="flex" justify="end">
|
||||
<el-pagination
|
||||
@current-change="handlePageChange"
|
||||
style="text-align: center; margin-top: 20px"
|
||||
background
|
||||
layout="prev, pager, next, total, jumper"
|
||||
:total="total"
|
||||
v-model:current-page="query.pageNum"
|
||||
:page-size="query.pageSize"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancel()">取 消</el-button>
|
||||
<el-button type="primary" :loading="btnLoading" @click="btnOk">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { toRefs, reactive, watch, ref } from 'vue';
|
||||
import { roleApi, accountApi } from '../api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
account: Object,
|
||||
});
|
||||
|
||||
//定义事件
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
|
||||
const roleTable: any = ref(null);
|
||||
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
btnLoading: false,
|
||||
// 所有角色
|
||||
allRole: [] as any,
|
||||
// 该账号拥有的角色id
|
||||
query: {
|
||||
name: null,
|
||||
pageNum: 1,
|
||||
pageSize: 5,
|
||||
},
|
||||
total: 0,
|
||||
});
|
||||
|
||||
const { dialogVisible, btnLoading, allRole, query, total } = toRefs(state);
|
||||
|
||||
// 用户拥有的角色信息
|
||||
let roles: any[] = [];
|
||||
|
||||
watch(props, (newValue: any) => {
|
||||
state.dialogVisible = newValue.visible;
|
||||
if (state.dialogVisible && newValue.account && newValue.account.id != 0) {
|
||||
accountApi.roleIds
|
||||
.request({
|
||||
id: props.account!.id,
|
||||
})
|
||||
.then((res) => {
|
||||
roles = res || [];
|
||||
search();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handlePageChange = () => {
|
||||
search();
|
||||
};
|
||||
|
||||
const selectable = (row: any) => {
|
||||
// 角色code不以COMMON开头才可勾选
|
||||
return row.code.indexOf('COMMON') != 0;
|
||||
};
|
||||
|
||||
const select = (val: any, row: any) => {
|
||||
// 如果账号的角色id存在则为取消该角色(删除角色id列表中的该记录id),否则为新增角色
|
||||
if (roles.includes(row.id)) {
|
||||
for (let i = 0; i < roles.length; i++) {
|
||||
let item = roles[i];
|
||||
if (item === row.id) {
|
||||
roles.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
roles.push(row.id);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 检查是否勾选权限,即是否拥有权限
|
||||
*/
|
||||
const checkSelected = () => {
|
||||
// 必须用异步,否则勾选不了
|
||||
setTimeout(() => {
|
||||
roleTable.value.clearSelection();
|
||||
state.allRole.forEach((r: any) => {
|
||||
if (roles.includes(r.id)) {
|
||||
roleTable.value.toggleRowSelection(r, true);
|
||||
}
|
||||
});
|
||||
}, 50);
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
let roleIds = roles.join(',');
|
||||
await accountApi.saveRoles.request({
|
||||
id: props.account!.id,
|
||||
roleIds: roleIds,
|
||||
});
|
||||
ElMessage.success('保存成功!');
|
||||
cancel();
|
||||
};
|
||||
|
||||
/**
|
||||
* 取消
|
||||
*/
|
||||
const cancel = () => {
|
||||
state.query.pageNum = 1;
|
||||
state.query.name = null;
|
||||
emit('update:visible', false);
|
||||
emit('cancel');
|
||||
};
|
||||
|
||||
/**
|
||||
* 清空查询框
|
||||
*/
|
||||
const clear = () => {
|
||||
state.query.pageNum = 1;
|
||||
state.query.name = null;
|
||||
search();
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
let res = await roleApi.list.request(state.query);
|
||||
state.allRole = res.list;
|
||||
state.total = res.total;
|
||||
checkSelected();
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<el-form-item label="账号">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
remote
|
||||
:remote-method="getAccount"
|
||||
v-model="accountId"
|
||||
filterable
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
v-bind="$attrs"
|
||||
:ref="(el: any) => focus && el?.focus()"
|
||||
>
|
||||
<el-option v-for="item in accounts" :key="item.id" :label="`${item.username} [${item.name}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { accountApi } from '../../api';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
},
|
||||
// 是否获取焦点
|
||||
focus: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:modelValue']);
|
||||
|
||||
const accountId = useVModel(props, 'modelValue', emit);
|
||||
|
||||
const accounts: any = ref([]);
|
||||
|
||||
const getAccount = (username: any) => {
|
||||
if (username) {
|
||||
accountApi.list.request({ username }).then((res) => {
|
||||
accounts.value = res.list;
|
||||
});
|
||||
} else {
|
||||
accounts.value = [];
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -19,6 +19,7 @@ export const roleApi = {
|
||||
roleResourceIds: Api.newGet('/sys/roles/{id}/resourceIds'),
|
||||
roleResources: Api.newGet('/sys/roles/{id}/resources'),
|
||||
saveResources: Api.newPost('/sys/roles/{id}/resources'),
|
||||
roleAccounts: Api.newGet('/sys/roles/{id}/accounts'),
|
||||
};
|
||||
|
||||
export const accountApi = {
|
||||
@@ -28,10 +29,9 @@ export const accountApi = {
|
||||
del: Api.newDelete('/sys/accounts/{id}'),
|
||||
changeStatus: Api.newPut('/sys/accounts/change-status/{id}/{status}'),
|
||||
resetOtpSecret: Api.newPut('/sys/accounts/{id}/reset-otp'),
|
||||
roleIds: Api.newGet('/sys/accounts/{id}/roleIds'),
|
||||
roles: Api.newGet('/sys/accounts/{id}/roles'),
|
||||
resources: Api.newGet('/sys/accounts/{id}/resources'),
|
||||
saveRoles: Api.newPost('/sys/accounts/roles'),
|
||||
saveRole: Api.newPost('/sys/accounts/roles'),
|
||||
};
|
||||
|
||||
export const configApi = {
|
||||
|
||||
137
mayfly_go_web/src/views/system/role/AccountAllocation.vue
Executable file
137
mayfly_go_web/src/views/system/role/AccountAllocation.vue
Executable file
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
@open="searchRoleAccount()"
|
||||
:title="role == null ? '' : `[${role.name}] 关联的账号`"
|
||||
v-model="dialogVisible"
|
||||
:destroy-on-close="true"
|
||||
width="55%"
|
||||
>
|
||||
<page-table ref="pageTableRef" :page-api="roleApi.roleAccounts" :search-items="searchItems" v-model:query-form="query" :columns="columns" lazy>
|
||||
<template #tableHeader>
|
||||
<el-button v-auth="perms.saveAccountRole" type="primary" icon="plus" @click="showAddAccount()">添加</el-button>
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<el-button link v-if="actionBtns[perms.saveAccountRole]" @click="relateAccount(-1, data.accountId)" icon="delete" type="danger"
|
||||
>移除</el-button
|
||||
>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<el-dialog width="400px" title="添加账号" :before-close="cancelAddAccount" v-model="addAccountDialog.visible" :destroy-on-close="true">
|
||||
<el-form label-width="auto">
|
||||
<AccountSelectFormItem v-model="addAccountDialog.accountId" :focus="true" />
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddAccount()">取 消</el-button>
|
||||
<el-button @click="relateAccount(1, addAccountDialog.accountId)" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
|
||||
import { AccountStatusEnum } from '../enums';
|
||||
import { accountApi, roleApi } from '../api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import PageTable from '@/components/pagetable/PageTable.vue';
|
||||
import { TableColumn } from '@/components/pagetable';
|
||||
import { hasPerms } from '@/components/auth/auth';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import { useVModel } from '@vueuse/core';
|
||||
import AccountSelectFormItem from '../account/components/AccountSelectFormItem.vue';
|
||||
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
role: Object,
|
||||
});
|
||||
|
||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
||||
|
||||
const perms = {
|
||||
addAccount: 'account:add',
|
||||
delAccount: 'account:del',
|
||||
saveAccountRole: 'account:saveRoles',
|
||||
changeAccountStatus: 'account:changeStatus',
|
||||
};
|
||||
|
||||
const searchItems = [SearchItem.input('name', '姓名'), SearchItem.input('username', '用户名')];
|
||||
const columns = [
|
||||
TableColumn.new('accountName', '姓名'),
|
||||
TableColumn.new('username', '用户名'),
|
||||
TableColumn.new('accountStatus', '用户状态').typeTag(AccountStatusEnum),
|
||||
TableColumn.new('creator', '分配者'),
|
||||
TableColumn.new('createTime', '分配时间').isTime(),
|
||||
];
|
||||
|
||||
// 该用户拥有的的操作列按钮权限
|
||||
const actionBtns = hasPerms([perms.addAccount, perms.saveAccountRole, perms.changeAccountStatus]);
|
||||
const actionColumn = TableColumn.new('action', '操作').isSlot().fixedRight().setMinWidth(80).noShowOverflowTooltip().alignCenter();
|
||||
|
||||
const pageTableRef: Ref<any> = ref(null);
|
||||
|
||||
const state = reactive({
|
||||
/**
|
||||
* 查询条件
|
||||
*/
|
||||
query: {
|
||||
username: '',
|
||||
name: '',
|
||||
id: 0,
|
||||
pageNum: 1,
|
||||
pageSize: 0,
|
||||
},
|
||||
addAccountDialog: {
|
||||
visible: false,
|
||||
accounts: [] as any,
|
||||
accountId: null as any,
|
||||
},
|
||||
});
|
||||
|
||||
const { query, addAccountDialog } = toRefs(state);
|
||||
|
||||
const dialogVisible = useVModel(props, 'visible', emit);
|
||||
|
||||
onMounted(() => {
|
||||
if (Object.keys(actionBtns).length > 0) {
|
||||
columns.push(actionColumn);
|
||||
}
|
||||
});
|
||||
|
||||
const searchRoleAccount = () => {
|
||||
state.query.id = props.role?.id;
|
||||
pageTableRef.value.search();
|
||||
};
|
||||
|
||||
const relateAccount = async (relateType: number, accountId: number) => {
|
||||
await accountApi.saveRole.request({
|
||||
id: accountId,
|
||||
roleId: props.role?.id,
|
||||
relateType,
|
||||
});
|
||||
ElMessage.success('操作成功');
|
||||
// 如果是新增账号,则关闭新增账号弹窗
|
||||
if (relateType == 1) {
|
||||
cancelAddAccount();
|
||||
}
|
||||
searchRoleAccount();
|
||||
};
|
||||
|
||||
const showAddAccount = () => {
|
||||
state.addAccountDialog.visible = true;
|
||||
};
|
||||
|
||||
const cancelAddAccount = () => {
|
||||
state.addAccountDialog.accountId = null;
|
||||
state.addAccountDialog.accounts = [];
|
||||
state.addAccountDialog.visible = false;
|
||||
};
|
||||
</script>
|
||||
<style lang="scss"></style>
|
||||
@@ -20,6 +20,14 @@
|
||||
<el-button v-if="actionBtns[perms.updateRole]" @click="editRole(data)" type="primary" link>编辑</el-button>
|
||||
<el-button @click="showResources(data)" type="info" link>权限详情</el-button>
|
||||
<el-button v-if="actionBtns[perms.saveRoleResource]" @click="editResource(data)" type="success" link>权限分配</el-button>
|
||||
<el-button
|
||||
v-if="actionBtns[perms.saveAccountRole]"
|
||||
:disabled="data.code?.indexOf('COMMON') == 0"
|
||||
@click="showAccountAllocation(data)"
|
||||
type="success"
|
||||
link
|
||||
>用户管理</el-button
|
||||
>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
@@ -31,6 +39,9 @@
|
||||
:defaultCheckedKeys="resourceDialog.defaultCheckedKeys"
|
||||
@cancel="cancelEditResources()"
|
||||
/>
|
||||
|
||||
<account-allocation v-model:visible="accountAllocationDialog.visible" :role="accountAllocationDialog.role" />
|
||||
|
||||
<show-resource v-model:visible="showResourceDialog.visible" :title="showResourceDialog.title" v-model:resources="showResourceDialog.resources" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -47,12 +58,14 @@ import { TableColumn } from '@/components/pagetable';
|
||||
import { hasPerms } from '@/components/auth/auth';
|
||||
import { RoleStatusEnum } from '../enums';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import AccountAllocation from './AccountAllocation.vue';
|
||||
|
||||
const perms = {
|
||||
addRole: 'role:add',
|
||||
delRole: 'role:del',
|
||||
updateRole: 'role:update',
|
||||
saveRoleResource: 'role:saveResources',
|
||||
saveAccountRole: 'account:saveRoles',
|
||||
};
|
||||
|
||||
const searchItems = [SearchItem.input('name', '角色名')];
|
||||
@@ -67,8 +80,8 @@ const columns = ref([
|
||||
TableColumn.new('updateTime', '更新时间').isTime(),
|
||||
]);
|
||||
|
||||
const actionBtns = hasPerms([perms.updateRole, perms.saveRoleResource]);
|
||||
const actionColumn = TableColumn.new('action', '操作').isSlot().setMinWidth(260).fixedRight().alignCenter();
|
||||
const actionBtns = hasPerms([perms.updateRole, perms.saveRoleResource, perms.saveAccountRole]);
|
||||
const actionColumn = TableColumn.new('action', '操作').isSlot().setMinWidth(290).fixedRight().alignCenter();
|
||||
|
||||
const pageTableRef: Ref<any> = ref(null);
|
||||
const state = reactive({
|
||||
@@ -94,9 +107,13 @@ const state = reactive({
|
||||
resources: [],
|
||||
title: '',
|
||||
},
|
||||
accountAllocationDialog: {
|
||||
visible: false,
|
||||
role: {},
|
||||
},
|
||||
});
|
||||
|
||||
const { query, selectionData, resourceDialog, roleEditDialog, showResourceDialog } = toRefs(state);
|
||||
const { query, selectionData, resourceDialog, roleEditDialog, showResourceDialog, accountAllocationDialog } = toRefs(state);
|
||||
|
||||
onMounted(() => {
|
||||
if (Object.keys(actionBtns).length > 0) {
|
||||
@@ -176,6 +193,11 @@ const editResource = async (row: any) => {
|
||||
state.resourceDialog.role = row;
|
||||
};
|
||||
|
||||
const showAccountAllocation = (data: any) => {
|
||||
state.accountAllocationDialog.role = data;
|
||||
state.accountAllocationDialog.visible = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取所有菜单树的叶子节点
|
||||
* @param {Object} trees 菜单树列表
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/internal/sys/api/form"
|
||||
"mayfly-go/internal/sys/api/vo"
|
||||
"mayfly-go/internal/sys/application"
|
||||
"mayfly-go/internal/sys/consts"
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/contextx"
|
||||
@@ -91,10 +92,7 @@ func (a *Account) ChangePassword(rc *req.Ctx) {
|
||||
func (a *Account) AccountInfo(rc *req.Ctx) {
|
||||
ap := new(vo.AccountPersonVO)
|
||||
// 角色信息
|
||||
roles := new([]vo.AccountRoleVO)
|
||||
a.RoleApp.GetAccountRoles(rc.GetLoginAccount().Id, roles)
|
||||
|
||||
ap.Roles = *roles
|
||||
ap.Roles = a.getAccountRoles(rc.GetLoginAccount().Id)
|
||||
rc.ResData = ap
|
||||
}
|
||||
|
||||
@@ -173,16 +171,47 @@ func (a *Account) DeleteAccount(rc *req.Ctx) {
|
||||
}
|
||||
}
|
||||
|
||||
// 获取账号角色id列表,用户回显角色分配
|
||||
func (a *Account) AccountRoleIds(rc *req.Ctx) {
|
||||
rc.ResData = a.RoleApp.GetAccountRoleIds(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
// 获取账号角色信息列表
|
||||
func (a *Account) AccountRoles(rc *req.Ctx) {
|
||||
rc.ResData = a.getAccountRoles(uint64(ginx.PathParamInt(rc.GinCtx, "id")))
|
||||
}
|
||||
|
||||
// 获取账号角色id列表,用户回显角色分配
|
||||
func (a *Account) AccountRoles(rc *req.Ctx) {
|
||||
vos := new([]vo.AccountRoleVO)
|
||||
a.RoleApp.GetAccountRoles(uint64(ginx.PathParamInt(rc.GinCtx, "id")), vos)
|
||||
rc.ResData = vos
|
||||
func (a *Account) getAccountRoles(accountId uint64) []*vo.AccountRoleVO {
|
||||
vos := make([]*vo.AccountRoleVO, 0)
|
||||
|
||||
accountRoles, err := a.RoleApp.GetAccountRoles(accountId)
|
||||
biz.ErrIsNil(err)
|
||||
|
||||
if len(accountRoles) == 0 {
|
||||
return vos
|
||||
}
|
||||
|
||||
// 获取角色信息进行组装
|
||||
roleIds := collx.ArrayMap[*entity.AccountRole, uint64](accountRoles, func(val *entity.AccountRole) uint64 {
|
||||
return val.RoleId
|
||||
})
|
||||
roles, err := a.RoleApp.ListByQuery(&entity.RoleQuery{Ids: roleIds})
|
||||
biz.ErrIsNil(err)
|
||||
roleId2Role := collx.ArrayToMap[*entity.Role, uint64](roles, func(val *entity.Role) uint64 {
|
||||
return val.Id
|
||||
})
|
||||
|
||||
for _, ac := range accountRoles {
|
||||
role := roleId2Role[ac.RoleId]
|
||||
if role == nil {
|
||||
continue
|
||||
}
|
||||
vos = append(vos, &vo.AccountRoleVO{
|
||||
RoleId: ac.RoleId,
|
||||
RoleName: role.Name,
|
||||
Code: role.Code,
|
||||
Status: role.Status,
|
||||
CreateTime: ac.CreateTime, // 分配时间
|
||||
Creator: ac.Creator, // 分配者
|
||||
})
|
||||
}
|
||||
|
||||
return vos
|
||||
}
|
||||
|
||||
func (a *Account) AccountResources(rc *req.Ctx) {
|
||||
@@ -192,19 +221,13 @@ func (a *Account) AccountResources(rc *req.Ctx) {
|
||||
rc.ResData = resources.ToTrees(0)
|
||||
}
|
||||
|
||||
// 保存账号角色信息
|
||||
func (a *Account) SaveRoles(rc *req.Ctx) {
|
||||
// 关联账号角色
|
||||
func (a *Account) RelateRole(rc *req.Ctx) {
|
||||
var form form.AccountRoleForm
|
||||
ginx.BindJsonAndValid(rc.GinCtx, &form)
|
||||
rc.ReqParam = form
|
||||
|
||||
// 将,拼接的字符串进行切割并转换
|
||||
newIds := collx.ArrayMap[string, uint64](strings.Split(form.RoleIds, ","), func(val string) uint64 {
|
||||
id, _ := strconv.Atoi(val)
|
||||
return uint64(id)
|
||||
})
|
||||
|
||||
a.RoleApp.SaveAccountRole(rc.MetaCtx, form.Id, newIds)
|
||||
biz.ErrIsNil(a.RoleApp.RelateAccountRole(rc.MetaCtx, form.Id, form.RoleId, consts.AccountRoleRelateType(form.RelateType)))
|
||||
}
|
||||
|
||||
// 重置otp秘钥
|
||||
|
||||
@@ -17,6 +17,7 @@ type RoleForm struct {
|
||||
|
||||
// 账号分配角色表单
|
||||
type AccountRoleForm struct {
|
||||
Id uint64 `json:"id" binding:"required"`
|
||||
RoleIds string `json:"roleIds"`
|
||||
Id uint64 `json:"id" binding:"required"`
|
||||
RoleId uint64 `json:"roleId" binding:"required"`
|
||||
RelateType int `json:"relateType" binding:"required"`
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/ginx"
|
||||
"mayfly-go/pkg/req"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strconv"
|
||||
"strings"
|
||||
@@ -20,8 +21,16 @@ type Role struct {
|
||||
|
||||
func (r *Role) Roles(rc *req.Ctx) {
|
||||
g := rc.GinCtx
|
||||
condition := &entity.Role{Name: g.Query("name")}
|
||||
res, err := r.RoleApp.GetPageList(condition, ginx.GetPageParam(g), new([]entity.Role))
|
||||
cond, pageParam := ginx.BindQueryAndPage(g, new(entity.RoleQuery))
|
||||
|
||||
notIdsStr := g.Query("notIds")
|
||||
if notIdsStr != "" {
|
||||
cond.NotIds = collx.ArrayMap[string, uint64](strings.Split(notIdsStr, ","), func(val string) uint64 {
|
||||
return uint64(anyx.ConvInt(val))
|
||||
})
|
||||
}
|
||||
|
||||
res, err := r.RoleApp.GetPageList(cond, pageParam, new([]entity.Role))
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -77,3 +86,14 @@ func (r *Role) SaveResource(rc *req.Ctx) {
|
||||
|
||||
r.RoleApp.SaveRoleResource(rc.MetaCtx, form.Id, newIds)
|
||||
}
|
||||
|
||||
// 查看角色关联的用户
|
||||
func (r *Role) RoleAccount(rc *req.Ctx) {
|
||||
g := rc.GinCtx
|
||||
cond, pageParam := ginx.BindQueryAndPage[*entity.RoleAccountQuery](g, new(entity.RoleAccountQuery))
|
||||
cond.RoleId = uint64(ginx.PathParamInt(g, "id"))
|
||||
var accounts []*vo.AccountRoleVO
|
||||
res, err := r.RoleApp.GetRoleAccountPage(cond, pageParam, &accounts)
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -16,13 +16,19 @@ type AccountManageVO struct {
|
||||
|
||||
// 账号角色信息
|
||||
type AccountRoleVO struct {
|
||||
Name *string `json:"name"`
|
||||
Status int `json:"status"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator string `json:"creator"`
|
||||
RoleId uint64 `json:"roleId"`
|
||||
RoleName string `json:"roleName"`
|
||||
Code string `json:"code"`
|
||||
Status int `json:"status"`
|
||||
AccountId uint64 `json:"accountId" gorm:"column:accountId"`
|
||||
AccountName string `json:"accountName" gorm:"column:accountName"`
|
||||
Username string `json:"username"`
|
||||
AccountStatus int `json:"accountStatus" gorm:"column:accountStatus"`
|
||||
CreateTime *time.Time `json:"createTime"`
|
||||
Creator string `json:"creator"`
|
||||
}
|
||||
|
||||
// 账号个人信息
|
||||
type AccountPersonVO struct {
|
||||
Roles []AccountRoleVO `json:"roles"` // 角色信息
|
||||
Roles []*AccountRoleVO `json:"roles"` // 角色信息
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ var (
|
||||
accountApp = newAccountApp(persistence.GetAccountRepo())
|
||||
configApp = newConfigApp(persistence.GetConfigRepo())
|
||||
resourceApp = newResourceApp(persistence.GetResourceRepo())
|
||||
roleApp = newRoleApp(persistence.GetRoleRepo())
|
||||
roleApp = newRoleApp(persistence.GetRoleRepo(), persistence.GetAccountRoleRepo())
|
||||
syslogApp = newSyslogApp(persistence.GetSyslogRepo())
|
||||
)
|
||||
|
||||
|
||||
@@ -2,20 +2,23 @@ package application
|
||||
|
||||
import (
|
||||
"context"
|
||||
"mayfly-go/internal/sys/consts"
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/internal/sys/domain/repository"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/model"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Role interface {
|
||||
GetPageList(condition *entity.Role, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
GetPageList(condition *entity.RoleQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
ListByQuery(condition *entity.RoleQuery) ([]*entity.Role, error)
|
||||
|
||||
SaveRole(ctx context.Context, role *entity.Role) error
|
||||
|
||||
@@ -28,36 +31,37 @@ type Role interface {
|
||||
// 保存角色资源关联记录
|
||||
SaveRoleResource(ctx context.Context, roleId uint64, resourceIds []uint64)
|
||||
|
||||
// 删除角色资源关联记录
|
||||
DeleteRoleResource(ctx context.Context, roleId uint64, resourceId uint64)
|
||||
// 关联账号角色
|
||||
RelateAccountRole(ctx context.Context, accountId, roleId uint64, relateType consts.AccountRoleRelateType) error
|
||||
|
||||
// 获取账号角色id列表
|
||||
GetAccountRoleIds(accountId uint64) []uint64
|
||||
// 获取账号关联角色
|
||||
GetAccountRoles(accountId uint64) ([]*entity.AccountRole, error)
|
||||
|
||||
// 保存账号角色关联信息
|
||||
SaveAccountRole(ctx context.Context, accountId uint64, roleIds []uint64)
|
||||
|
||||
DeleteAccountRole(ctx context.Context, accountId, roleId uint64)
|
||||
|
||||
GetAccountRoles(accountId uint64, toEntity any)
|
||||
// 获取角色关联的用户信息
|
||||
GetRoleAccountPage(condition *entity.RoleAccountQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
}
|
||||
|
||||
func newRoleApp(roleRepo repository.Role) Role {
|
||||
func newRoleApp(roleRepo repository.Role, accountRoleRepo repository.AccountRole) Role {
|
||||
return &roleAppImpl{
|
||||
roleRepo: roleRepo,
|
||||
roleRepo: roleRepo,
|
||||
accountRoleRepo: accountRoleRepo,
|
||||
}
|
||||
}
|
||||
|
||||
type roleAppImpl struct {
|
||||
roleRepo repository.Role
|
||||
roleRepo repository.Role
|
||||
accountRoleRepo repository.AccountRole
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) GetPageList(condition *entity.Role, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
func (m *roleAppImpl) GetPageList(condition *entity.RoleQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
return m.roleRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) ListByQuery(condition *entity.RoleQuery) ([]*entity.Role, error) {
|
||||
return m.roleRepo.ListByQuery(condition)
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) SaveRole(ctx context.Context, role *entity.Role) error {
|
||||
role.Code = strings.ToUpper(role.Code)
|
||||
if role.Id != 0 {
|
||||
// code不可更改,防止误传
|
||||
role.Code = ""
|
||||
@@ -65,11 +69,11 @@ func (m *roleAppImpl) SaveRole(ctx context.Context, role *entity.Role) error {
|
||||
}
|
||||
|
||||
role.Status = 1
|
||||
return gormx.Insert(role)
|
||||
return m.roleRepo.Insert(ctx, role)
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) DeleteRole(ctx context.Context, id uint64) error {
|
||||
// 删除角色与资源的关联关系
|
||||
// 删除角色与资源账号的关联关系
|
||||
return gormx.Tx(
|
||||
func(db *gorm.DB) error {
|
||||
return m.roleRepo.DeleteByIdWithDb(ctx, db, id)
|
||||
@@ -77,6 +81,9 @@ func (m *roleAppImpl) DeleteRole(ctx context.Context, id uint64) error {
|
||||
func(db *gorm.DB) error {
|
||||
return gormx.DeleteByWithDb(db, &entity.RoleResource{RoleId: id})
|
||||
},
|
||||
func(db *gorm.DB) error {
|
||||
return gormx.DeleteByWithDb(db, &entity.AccountRole{RoleId: id})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -110,44 +117,35 @@ func (m *roleAppImpl) SaveRoleResource(ctx context.Context, roleId uint64, resou
|
||||
m.roleRepo.SaveRoleResource(addVals)
|
||||
|
||||
for _, v := range delIds {
|
||||
m.DeleteRoleResource(ctx, roleId, v)
|
||||
m.roleRepo.DeleteRoleResource(roleId, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) DeleteRoleResource(ctx context.Context, roleId uint64, resourceId uint64) {
|
||||
m.roleRepo.DeleteRoleResource(roleId, resourceId)
|
||||
}
|
||||
func (m *roleAppImpl) RelateAccountRole(ctx context.Context, accountId, roleId uint64, relateType consts.AccountRoleRelateType) error {
|
||||
accountRole := &entity.AccountRole{AccountId: accountId, RoleId: roleId}
|
||||
if relateType == consts.AccountRoleUnbind {
|
||||
return m.accountRoleRepo.DeleteByCond(ctx, accountRole)
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) GetAccountRoleIds(accountId uint64) []uint64 {
|
||||
return m.roleRepo.GetAccountRoleIds(accountId)
|
||||
}
|
||||
|
||||
// 保存账号角色关联信息
|
||||
func (m *roleAppImpl) SaveAccountRole(ctx context.Context, accountId uint64, roleIds []uint64) {
|
||||
oIds := m.GetAccountRoleIds(accountId)
|
||||
|
||||
addIds, delIds, _ := collx.ArrayCompare(roleIds, oIds, func(i1, i2 uint64) bool {
|
||||
return i1 == i2
|
||||
})
|
||||
err := m.accountRoleRepo.GetBy(accountRole)
|
||||
if err == nil {
|
||||
return errorx.NewBiz("该用户已拥有该权限")
|
||||
}
|
||||
|
||||
la := contextx.GetLoginAccount(ctx)
|
||||
|
||||
createTime := time.Now()
|
||||
creator := la.Username
|
||||
creatorId := la.Id
|
||||
for _, v := range addIds {
|
||||
rr := &entity.AccountRole{AccountId: accountId, RoleId: v, CreateTime: &createTime, CreatorId: creatorId, Creator: creator}
|
||||
m.roleRepo.SaveAccountRole(rr)
|
||||
}
|
||||
for _, v := range delIds {
|
||||
m.DeleteAccountRole(ctx, accountId, v)
|
||||
}
|
||||
accountRole.Creator = la.Username
|
||||
accountRole.CreatorId = la.Id
|
||||
accountRole.CreateTime = &createTime
|
||||
return m.accountRoleRepo.Insert(ctx, accountRole)
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) DeleteAccountRole(ctx context.Context, accountId, roleId uint64) {
|
||||
m.roleRepo.DeleteAccountRole(accountId, roleId)
|
||||
func (m *roleAppImpl) GetAccountRoles(accountId uint64) ([]*entity.AccountRole, error) {
|
||||
var res []*entity.AccountRole
|
||||
err := m.accountRoleRepo.ListByCond(&entity.AccountRole{AccountId: accountId}, &res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
func (m *roleAppImpl) GetAccountRoles(accountId uint64, toEntity any) {
|
||||
m.roleRepo.GetAccountRoles(accountId, toEntity)
|
||||
func (m *roleAppImpl) GetRoleAccountPage(condition *entity.RoleAccountQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
return m.accountRoleRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
|
||||
}
|
||||
|
||||
8
server/internal/sys/consts/consts.go
Normal file
8
server/internal/sys/consts/consts.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package consts
|
||||
|
||||
type AccountRoleRelateType int
|
||||
|
||||
const (
|
||||
AccountRoleBind AccountRoleRelateType = 1
|
||||
AccountRoleUnbind AccountRoleRelateType = -1
|
||||
)
|
||||
@@ -5,3 +5,16 @@ type SysLogQuery struct {
|
||||
Type int8 `json:"type" form:"type"`
|
||||
Description string `json:"description" form:"description"`
|
||||
}
|
||||
|
||||
type RoleQuery struct {
|
||||
Ids []uint64 `json:"ids"`
|
||||
Name string `json:"name" form:"name"`
|
||||
Code string `json:"code" form:"code"`
|
||||
NotIds []uint64 `json:"notIds"`
|
||||
}
|
||||
|
||||
type RoleAccountQuery struct {
|
||||
RoleId uint64 `json:"roleId" `
|
||||
Name string `json:"name" form:"name"`
|
||||
Username string `json:"username" form:"username"`
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import (
|
||||
type Role interface {
|
||||
base.Repo[*entity.Role]
|
||||
|
||||
GetPageList(condition *entity.Role, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
GetPageList(condition *entity.RoleQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
|
||||
ListByQuery(condition *entity.RoleQuery) ([]*entity.Role, error)
|
||||
|
||||
// 获取角色拥有的资源id数组,从role_resource表获取
|
||||
GetRoleResourceIds(roleId uint64) []uint64
|
||||
@@ -19,14 +21,10 @@ type Role interface {
|
||||
SaveRoleResource(rr []*entity.RoleResource)
|
||||
|
||||
DeleteRoleResource(roleId uint64, resourceId uint64)
|
||||
|
||||
// 获取账号拥有的角色id数组,从account_role表获取
|
||||
GetAccountRoleIds(accountId uint64) []uint64
|
||||
|
||||
SaveAccountRole(ar *entity.AccountRole)
|
||||
|
||||
DeleteAccountRole(accountId, roleId uint64)
|
||||
|
||||
// 获取账号角色信息列表
|
||||
GetAccountRoles(accountId uint64, toEntity any)
|
||||
}
|
||||
|
||||
type AccountRole interface {
|
||||
base.Repo[*entity.AccountRole]
|
||||
|
||||
GetPageList(condition *entity.RoleAccountQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package persistence
|
||||
|
||||
import (
|
||||
"mayfly-go/internal/sys/domain/entity"
|
||||
"mayfly-go/internal/sys/domain/repository"
|
||||
"mayfly-go/pkg/base"
|
||||
"mayfly-go/pkg/gormx"
|
||||
"mayfly-go/pkg/model"
|
||||
)
|
||||
|
||||
type accountRoleRepoImpl struct {
|
||||
base.RepoImpl[*entity.AccountRole]
|
||||
}
|
||||
|
||||
func newAccountRoleRepo() repository.AccountRole {
|
||||
return &accountRoleRepoImpl{base.RepoImpl[*entity.AccountRole]{M: new(entity.AccountRole)}}
|
||||
}
|
||||
|
||||
func (m *accountRoleRepoImpl) GetPageList(condition *entity.RoleAccountQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQueryWithTableName("t_sys_account_role t").
|
||||
Select("t.creator, t.create_time, a.username, a.name accountName, a.status accountStatus, a.id accountId").
|
||||
Joins("JOIN t_sys_account a ON t.account_id = a.id AND a.status = 1").
|
||||
Eq0("a.is_deleted", model.ModelUndeleted).
|
||||
Eq0("t.is_deleted", model.ModelUndeleted).
|
||||
RLike("a.username", condition.Username).
|
||||
RLike("a.name", condition.Name).
|
||||
Eq("t.role_id", condition.RoleId).
|
||||
OrderByDesc("t.id")
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
@@ -3,11 +3,12 @@ package persistence
|
||||
import "mayfly-go/internal/sys/domain/repository"
|
||||
|
||||
var (
|
||||
accountRepo = newAccountRepo()
|
||||
configRepo = newConfigRepo()
|
||||
resourceRepo = newResourceRepo()
|
||||
roleRepo = newRoleRepo()
|
||||
syslogRepo = newSyslogRepo()
|
||||
accountRepo = newAccountRepo()
|
||||
configRepo = newConfigRepo()
|
||||
resourceRepo = newResourceRepo()
|
||||
roleRepo = newRoleRepo()
|
||||
accountRoleRepo = newAccountRoleRepo()
|
||||
syslogRepo = newSyslogRepo()
|
||||
)
|
||||
|
||||
func GetAccountRepo() repository.Account {
|
||||
@@ -26,6 +27,10 @@ func GetRoleRepo() repository.Role {
|
||||
return roleRepo
|
||||
}
|
||||
|
||||
func GetAccountRoleRepo() repository.AccountRole {
|
||||
return accountRoleRepo
|
||||
}
|
||||
|
||||
func GetSyslogRepo() repository.Syslog {
|
||||
return syslogRepo
|
||||
}
|
||||
|
||||
@@ -16,11 +16,28 @@ func newRoleRepo() repository.Role {
|
||||
return &roleRepoImpl{base.RepoImpl[*entity.Role]{M: new(entity.Role)}}
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) GetPageList(condition *entity.Role, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(condition).WithCondModel(condition).WithOrderBy(orderBy...)
|
||||
func (m *roleRepoImpl) GetPageList(condition *entity.RoleQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||
qd := gormx.NewQuery(new(entity.Role)).
|
||||
Like("name", condition.Name).
|
||||
Like("code", condition.Code).
|
||||
In("id", condition.Ids).
|
||||
NotIn("id", condition.NotIds).
|
||||
WithOrderBy(orderBy...)
|
||||
return gormx.PageQuery(qd, pageParam, toEntity)
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) ListByQuery(condition *entity.RoleQuery) ([]*entity.Role, error) {
|
||||
var res []*entity.Role
|
||||
qd := gormx.NewQuery(new(entity.Role)).
|
||||
Like("name", condition.Name).
|
||||
Like("code", condition.Code).
|
||||
In("id", condition.Ids).
|
||||
NotIn("id", condition.NotIds).
|
||||
OrderByDesc("id")
|
||||
err := gormx.ListByQueryCond(qd, &res)
|
||||
return res, err
|
||||
}
|
||||
|
||||
// 获取角色拥有的资源id数组,从role_resource表获取
|
||||
func (m *roleRepoImpl) GetRoleResourceIds(roleId uint64) []uint64 {
|
||||
var rrs []entity.RoleResource
|
||||
@@ -51,32 +68,3 @@ func (m *roleRepoImpl) SaveRoleResource(rr []*entity.RoleResource) {
|
||||
func (m *roleRepoImpl) DeleteRoleResource(roleId uint64, resourceId uint64) {
|
||||
gormx.DeleteBy(&entity.RoleResource{RoleId: roleId, ResourceId: resourceId})
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) GetAccountRoleIds(accountId uint64) []uint64 {
|
||||
var rrs []entity.AccountRole
|
||||
|
||||
condtion := &entity.AccountRole{AccountId: accountId}
|
||||
gormx.ListBy(condtion, &rrs, "RoleId")
|
||||
|
||||
var rids []uint64
|
||||
for _, v := range rrs {
|
||||
rids = append(rids, v.RoleId)
|
||||
}
|
||||
return rids
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) SaveAccountRole(ar *entity.AccountRole) {
|
||||
gormx.Insert(ar)
|
||||
}
|
||||
|
||||
func (m *roleRepoImpl) DeleteAccountRole(accountId, roleId uint64) {
|
||||
gormx.DeleteBy(&entity.AccountRole{RoleId: roleId, AccountId: accountId})
|
||||
}
|
||||
|
||||
// 获取账号角色信息列表
|
||||
func (m *roleRepoImpl) GetAccountRoles(accountId uint64, toEntity any) {
|
||||
sql := "SELECT r.status, r.name, ar.create_time AS CreateTime, ar.creator AS creator " +
|
||||
"FROM t_sys_role r JOIN t_sys_account_role ar ON r.id = ar.role_id AND ar.account_id = ? AND r.is_deleted = 0 AND ar.is_deleted = 0 " +
|
||||
"ORDER BY ar.create_time DESC"
|
||||
gormx.GetListBySql2Model(sql, toEntity, accountId)
|
||||
}
|
||||
|
||||
@@ -47,11 +47,8 @@ func InitAccountRouter(router *gin.RouterGroup) {
|
||||
|
||||
req.NewDelete(":id", a.DeleteAccount).Log(req.NewLogSave("删除账号")).RequiredPermissionCode("account:del"),
|
||||
|
||||
// 获取所有用户角色id列表
|
||||
req.NewGet(":id/roleIds", a.AccountRoleIds),
|
||||
|
||||
// 保存用户角色
|
||||
req.NewPost("/roles", a.SaveRoles).Log(req.NewLogSave("保存用户角色")).RequiredPermissionCode("account:saveRoles"),
|
||||
// 关联用户角色
|
||||
req.NewPost("/roles", a.RelateRole).Log(req.NewLogSave("关联用户角色")).RequiredPermissionCode("account:saveRoles"),
|
||||
|
||||
// 获取用户角色
|
||||
req.NewGet(":id/roles", a.AccountRoles),
|
||||
|
||||
@@ -27,6 +27,8 @@ func InitRoleRouter(router *gin.RouterGroup) {
|
||||
req.NewGet(":id/resources", r.RoleResource),
|
||||
|
||||
req.NewPost(":id/resources", r.SaveResource).Log(req.NewLogSave("保存角色资源")).RequiredPermissionCode("role:saveResources"),
|
||||
|
||||
req.NewGet(":id/accounts", r.RoleAccount),
|
||||
}
|
||||
|
||||
req.BatchSetGroup(rg, reqs[:])
|
||||
|
||||
@@ -11,6 +11,7 @@ const (
|
||||
And = "AND"
|
||||
Or = "OR"
|
||||
In = "IN"
|
||||
NotIn = "NOT IN"
|
||||
Not = "NOT"
|
||||
Like = "LIKE"
|
||||
Eq = "="
|
||||
|
||||
@@ -72,6 +72,13 @@ func PageQuery[T any](q *QueryCond, pageParam *model.PageParam, toModels T) (*mo
|
||||
return &model.PageResult[T]{Total: count, List: toModels}, nil
|
||||
}
|
||||
|
||||
// 根据查询条件查询列表信息
|
||||
func ListByQueryCond(q *QueryCond, list any) error {
|
||||
q.Undeleted()
|
||||
gdb := q.GenGdb()
|
||||
return gdb.Find(list).Error
|
||||
}
|
||||
|
||||
// 获取满足model中不为空的字段值条件的所有数据.
|
||||
//
|
||||
// @param list为数组类型 如 var users *[]User,可指定为非model结构体,即只包含需要返回的字段结构体
|
||||
|
||||
@@ -127,6 +127,10 @@ func (q *QueryCond) In(column string, val any) *QueryCond {
|
||||
return q.Cond(consts.In, column, val, true)
|
||||
}
|
||||
|
||||
func (q *QueryCond) NotIn(column string, val any) *QueryCond {
|
||||
return q.Cond(consts.NotIn, column, val, true)
|
||||
}
|
||||
|
||||
// // Ne 不等于 !=
|
||||
func (q *QueryCond) Ne(column string, val any) *QueryCond {
|
||||
q.Cond(consts.Ne, column, val, true)
|
||||
|
||||
Reference in New Issue
Block a user