update: 菜单管理
This commit is contained in:
parent
e3183975c3
commit
730a77d2c4
|
@ -170,22 +170,18 @@
|
||||||
placeholder="请选择关联菜单"
|
placeholder="请选择关联菜单"
|
||||||
multiple
|
multiple
|
||||||
show-search
|
show-search
|
||||||
tree-default-expand-all
|
|
||||||
:tree-data="form.treeData"
|
:tree-data="form.treeData"
|
||||||
|
:field-names="{
|
||||||
|
children: 'children',
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template #title="{ value: val, title }">
|
|
||||||
<b
|
|
||||||
v-if="val === 'parent 1-1'"
|
|
||||||
style="color: #08c"
|
|
||||||
>{{ val }}</b
|
|
||||||
>
|
|
||||||
<template v-else>{{ title }}</template>
|
|
||||||
</template>
|
|
||||||
</a-tree-select>
|
</a-tree-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="权限">
|
<a-form-item label="权限">
|
||||||
<PermissChoose v-model:value="form.data.permissions" />
|
<PermissChoose :first-width="3" max-height="350px" v-model:value="form.data.permissions" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
|
|
||||||
|
@ -245,17 +241,16 @@ const form = reactive({
|
||||||
|
|
||||||
treeData: [], // 关联菜单
|
treeData: [], // 关联菜单
|
||||||
assetsType: [] as assetType[], // 资产类型
|
assetsType: [] as assetType[], // 资产类型
|
||||||
premissonList: [], // 权限列表
|
|
||||||
|
|
||||||
init: () => {
|
init: () => {
|
||||||
// 获取菜单详情
|
// 获取菜单详情
|
||||||
routeParams.id &&
|
routeParams.id &&
|
||||||
getMenuInfo_api(routeParams.id).then((resp) => {
|
getMenuInfo_api(routeParams.id).then((resp) => {
|
||||||
console.log('菜单详情', resp);
|
form.data = resp.result as formType
|
||||||
});
|
});
|
||||||
// 获取关联菜单
|
// 获取关联菜单
|
||||||
getMenuTree_api({ paging: false }).then((resp) => {
|
getMenuTree_api({ paging: false }).then((resp: any) => {
|
||||||
console.log('关联菜单', resp);
|
form.treeData = resp.result;
|
||||||
});
|
});
|
||||||
// 获取资产类型
|
// 获取资产类型
|
||||||
getAssetsType_api().then((resp: any) => {
|
getAssetsType_api().then((resp: any) => {
|
||||||
|
|
|
@ -1,18 +1,134 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="button-mange-container">
|
<div class="button-mange-container">
|
||||||
|
<JTable
|
||||||
|
ref="tableRef"
|
||||||
|
:columns="table.columns"
|
||||||
|
model="TABLE"
|
||||||
|
:dataSource="table.data"
|
||||||
|
>
|
||||||
|
<template #headerTitle>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
@click="() => dialog.openDialog()"
|
||||||
|
><plus-outlined />新增</a-button
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space :size="16">
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>编辑</template>
|
||||||
|
<a-button
|
||||||
|
style="padding: 0"
|
||||||
|
type="link"
|
||||||
|
@click="() => dialog.openDialog(slotProps)"
|
||||||
|
>
|
||||||
|
<edit-outlined />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>查看</template>
|
||||||
|
<a-button
|
||||||
|
style="padding: 0"
|
||||||
|
type="link"
|
||||||
|
@click="() => dialog.openDialog(slotProps)"
|
||||||
|
>
|
||||||
|
<edit-outlined />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
|
||||||
|
<a-popconfirm
|
||||||
|
title="确认删除"
|
||||||
|
ok-text="确定"
|
||||||
|
cancel-text="取消"
|
||||||
|
:disabled="slotProps.status"
|
||||||
|
>
|
||||||
|
<a-tooltip>
|
||||||
|
<template #title>删除</template>
|
||||||
|
<a-button style="padding: 0" type="link">
|
||||||
|
<delete-outlined />
|
||||||
|
</a-button>
|
||||||
|
</a-tooltip>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
|
||||||
|
<div class="dialog">
|
||||||
|
<ButtonAddDialog ref="dialogRef" @confirm="dialog.confirm" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
EditOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import ButtonAddDialog from '../components/ButtonAddDialog.vue';
|
||||||
|
|
||||||
|
import { getMenuInfo_api } from '@/api/system/menu';
|
||||||
|
|
||||||
|
// 路由
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const routeParams = {
|
const routeParams = {
|
||||||
id: route.params.id === ':id' ? '' : route.params.id,
|
id: route.params.id === ':id' ? '' : (route.params.id as string),
|
||||||
...route.query,
|
...route.query,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const dialogRef = ref<any>(null);
|
||||||
|
const dialog = {
|
||||||
|
// 打开弹窗
|
||||||
|
openDialog: (row?: object) => {
|
||||||
|
dialogRef.value && dialogRef.value.openDialog(row);
|
||||||
|
},
|
||||||
|
confirm: () => {},
|
||||||
|
};
|
||||||
|
// 表格相关
|
||||||
|
const table = reactive({
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
title: '编码',
|
||||||
|
dataIndex: 'id',
|
||||||
|
key: 'id',
|
||||||
|
width: 220,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '说明',
|
||||||
|
dataIndex: 'description',
|
||||||
|
key: 'description',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'action',
|
||||||
|
key: 'action',
|
||||||
|
scopedSlots: true,
|
||||||
|
width: 240,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
data: [] as tableDataItem[],
|
||||||
|
getList: () => {
|
||||||
|
routeParams.id &&
|
||||||
|
getMenuInfo_api(routeParams.id).then((resp: any) => {
|
||||||
|
table.data = resp.result.buttons as tableDataItem[];
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
table.getList();
|
||||||
|
type tableDataItem = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
permissions: object[];
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped></style>
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="dialog.visible"
|
||||||
|
title="新增"
|
||||||
|
width="660px"
|
||||||
|
@ok="dialog.handleOk"
|
||||||
|
>
|
||||||
|
<a-form :model="form.data" class="basic-form">
|
||||||
|
<a-form-item
|
||||||
|
label="编码"
|
||||||
|
name="id"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入编码' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="form.data.id" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input v-model:value="form.data.name" />
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="权限">
|
||||||
|
<PermissChoose
|
||||||
|
:first-width="8"
|
||||||
|
max-height="350px"
|
||||||
|
v-model:value="form.data.permissions"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item label="说明" name="describe">
|
||||||
|
<a-textarea
|
||||||
|
v-model:value="form.data.describe"
|
||||||
|
:rows="4"
|
||||||
|
placeholder="请输入说明"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import PermissChoose from '../components/PermissChoose.vue';
|
||||||
|
|
||||||
|
const emits = defineEmits(['confirm']);
|
||||||
|
const dialog = reactive({
|
||||||
|
visible: false,
|
||||||
|
handleOk: () => {
|
||||||
|
dialog.changeVisible();
|
||||||
|
},
|
||||||
|
changeVisible: (formValue?: formType, show?: boolean) => {
|
||||||
|
dialog.visible = show === undefined ? !dialog.visible : show;
|
||||||
|
form.data = formValue || { ...initForm };
|
||||||
|
console.log(1111111111, form.data);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const initForm = {
|
||||||
|
name: '',
|
||||||
|
id: '',
|
||||||
|
permissions: [],
|
||||||
|
describe: '',
|
||||||
|
} as formType;
|
||||||
|
const form = reactive({
|
||||||
|
data: { ...initForm },
|
||||||
|
});
|
||||||
|
|
||||||
|
// 将打开弹窗的操作暴露给父组件
|
||||||
|
defineExpose({
|
||||||
|
openDialog: dialog.changeVisible,
|
||||||
|
});
|
||||||
|
|
||||||
|
type formType = {
|
||||||
|
name: string;
|
||||||
|
id: string;
|
||||||
|
permissions: any[];
|
||||||
|
describe: string;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.basic-form {
|
||||||
|
.ant-form-item {
|
||||||
|
display: block;
|
||||||
|
:deep(.ant-form-item-label) {
|
||||||
|
overflow: inherit;
|
||||||
|
label::after {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -5,43 +5,207 @@
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
allowClear
|
allowClear
|
||||||
placeholder="请输入权限名称"
|
placeholder="请输入权限名称"
|
||||||
|
@input="search.search"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<div class="permission-table">
|
||||||
|
<a-row :gutter="24" class="table-head">
|
||||||
|
<a-col :span="props.firstWidth">权限名称</a-col
|
||||||
|
><a-col :span="24 - props.firstWidth">权限操作</a-col>
|
||||||
|
</a-row>
|
||||||
|
<div class="table-body" :style="{ 'max-height': props.maxHeight }">
|
||||||
|
<a-row
|
||||||
|
:gutter="24"
|
||||||
|
class="row"
|
||||||
|
v-for="rowItem in permission.list"
|
||||||
|
>
|
||||||
|
<a-col :span="props.firstWidth" class="item-name">
|
||||||
|
<a-checkbox
|
||||||
|
v-model:checked="rowItem.checkAll"
|
||||||
|
:indeterminate="rowItem.indeterminate"
|
||||||
|
@change="() => permission.selectAllOpions(rowItem)"
|
||||||
|
>
|
||||||
|
{{ rowItem.name }}
|
||||||
|
</a-checkbox>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24 - props.firstWidth">
|
||||||
|
<a-checkbox-group
|
||||||
|
v-model:value="rowItem.checkedList"
|
||||||
|
:options="rowItem.options"
|
||||||
|
@change="((checkValue:string[])=>permission.selectOption(rowItem, checkValue))"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { exportPermission_api } from '@/api/system/permission';
|
import { exportPermission_api } from '@/api/system/permission';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps<{
|
||||||
value: Array,
|
value: any[];
|
||||||
});
|
firstWidth: number;
|
||||||
|
maxHeight: string;
|
||||||
|
}>();
|
||||||
|
const emits = defineEmits(['update:value']);
|
||||||
const searchValue = ref<string>('');
|
const searchValue = ref<string>('');
|
||||||
|
|
||||||
|
const search = reactive({
|
||||||
|
value: '',
|
||||||
|
searchTimer: null as null | number,
|
||||||
|
search: () => {
|
||||||
|
if (search.searchTimer) {
|
||||||
|
clearTimeout(search.searchTimer);
|
||||||
|
}
|
||||||
|
search.searchTimer = setTimeout(() => {
|
||||||
|
nextTick(() => permission.getList());
|
||||||
|
search.searchTimer = null;
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
});
|
||||||
const permission = reactive({
|
const permission = reactive({
|
||||||
list: [] as permissionType[],
|
list: [] as permissionType[],
|
||||||
// 获取权限列表
|
// 获取权限列表
|
||||||
getList: () => {
|
getList: () => {
|
||||||
|
const params: paramsType = {
|
||||||
exportPermission_api({ paging: false }).then((resp) => {
|
paging: false,
|
||||||
permission.list = resp.result as permissionType[]
|
};
|
||||||
|
if (search.value) {
|
||||||
|
params.terms = [
|
||||||
|
{ column: 'name$like', value: `%${search.value}%` },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
exportPermission_api(params).then((resp) => {
|
||||||
|
permission.list = permission.makeList(
|
||||||
|
props.value,
|
||||||
|
resp.result as any[],
|
||||||
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
// 全选/取消全选
|
||||||
|
selectAllOpions: (row: permissionType) => {
|
||||||
|
const newValue = props.value.filter(
|
||||||
|
(item) => item.permission !== row.id,
|
||||||
|
);
|
||||||
|
row = toRaw(row);
|
||||||
|
if (row.checkAll) {
|
||||||
|
row.checkedList = row.options.map((item) => item.value);
|
||||||
|
|
||||||
|
newValue.push({
|
||||||
|
permission: row.id,
|
||||||
|
actions: row.checkedList,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
row.checkedList = [];
|
||||||
|
}
|
||||||
|
emits('update:value', newValue);
|
||||||
|
},
|
||||||
|
// 单选
|
||||||
|
selectOption: (row: permissionType, newValue: string[]) => {
|
||||||
|
const newProp = props.value.filter(
|
||||||
|
(item) => item.permission !== row.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newValue.length === row.options.length) {
|
||||||
|
row.checkAll = true;
|
||||||
|
row.indeterminate = false;
|
||||||
|
newProp.push({
|
||||||
|
permission: row.id,
|
||||||
|
actions: newValue,
|
||||||
|
});
|
||||||
|
} else if (newValue.length > 0) {
|
||||||
|
row.checkAll = false;
|
||||||
|
row.indeterminate = true;
|
||||||
|
newProp.push({
|
||||||
|
permission: row.id,
|
||||||
|
actions: newValue,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
emits('update:value', newProp);
|
||||||
|
},
|
||||||
|
makeList: (checkedValue: any[], sourceList: any[]): permissionType[] => {
|
||||||
|
console.log(checkedValue);
|
||||||
|
|
||||||
|
const result = sourceList.map((item) => {
|
||||||
|
const checked = checkedValue.find(
|
||||||
|
(checkedItem) => checkedItem.permission === item.id,
|
||||||
|
);
|
||||||
|
const options = item.actions.map((actionItem: any) => ({
|
||||||
|
label: actionItem.name,
|
||||||
|
value: actionItem.action,
|
||||||
|
}));
|
||||||
|
console.log(item, checked);
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.name,
|
||||||
|
checkedList: (checked && checked.actions) || [],
|
||||||
|
checkAll:
|
||||||
|
(checked &&
|
||||||
|
item.actions &&
|
||||||
|
checked?.actions.length === item.actions.length) ||
|
||||||
|
false,
|
||||||
|
indeterminate:
|
||||||
|
(checked &&
|
||||||
|
item.actions &&
|
||||||
|
checked.actions.length < item.actions.length) ||
|
||||||
|
false,
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
}) as permissionType[];
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
permission.getList()
|
permission.getList();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type permissionType = {
|
type permissionType = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
actions: object[]
|
checkedList: string[];
|
||||||
}
|
checkAll: boolean;
|
||||||
|
indeterminate: boolean;
|
||||||
|
options: any[];
|
||||||
|
};
|
||||||
|
type paramsType = {
|
||||||
|
paging: boolean;
|
||||||
|
terms?: object[];
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped></style>
|
<style lang="less" scoped>
|
||||||
|
.permission-choose-container {
|
||||||
|
.permission-table {
|
||||||
|
margin-top: 12px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #d9d9d9;
|
||||||
|
color: rgba(0, 0, 0, 0.85);
|
||||||
|
.table-head {
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #d9d9d9;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
.table-body {
|
||||||
|
overflow: auto;
|
||||||
|
.row {
|
||||||
|
margin: 0 !important;
|
||||||
|
|
||||||
|
border-bottom: 1px solid #d9d9d9;
|
||||||
|
|
||||||
|
> div {
|
||||||
|
padding: 8px 12px;
|
||||||
|
}
|
||||||
|
.item-name {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-right: 1px solid #d9d9d9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
<a-button>菜单实例</a-button>
|
<a-button>菜单实例</a-button>
|
||||||
</template>
|
</template>
|
||||||
<template #createTime="slotProps">
|
<template #createTime="slotProps">
|
||||||
{{ slotProps.createTime }}
|
{{ moment(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
type="link"
|
type="link"
|
||||||
@click="table.toDetails(slotProps)"
|
@click="table.toDetails(slotProps)"
|
||||||
>
|
>
|
||||||
<edit-outlined />
|
<search-outlined />
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
|
@ -40,7 +40,7 @@
|
||||||
type="link"
|
type="link"
|
||||||
@click="table.toDetails(slotProps)"
|
@click="table.toDetails(slotProps)"
|
||||||
>
|
>
|
||||||
<edit-outlined />
|
<plus-circle-outlined />
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
|
||||||
|
@ -66,6 +66,13 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { getMenuTree_api } from '@/api/system/menu';
|
import { getMenuTree_api } from '@/api/system/menu';
|
||||||
|
import {
|
||||||
|
SearchOutlined,
|
||||||
|
DeleteOutlined,
|
||||||
|
PlusOutlined,
|
||||||
|
PlusCircleOutlined
|
||||||
|
} from '@ant-design/icons-vue';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue