update: 菜单管理
This commit is contained in:
parent
e3183975c3
commit
730a77d2c4
|
@ -170,22 +170,18 @@
|
|||
placeholder="请选择关联菜单"
|
||||
multiple
|
||||
show-search
|
||||
tree-default-expand-all
|
||||
: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-form-item>
|
||||
</a-form-item>
|
||||
<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>
|
||||
|
||||
|
@ -245,17 +241,16 @@ const form = reactive({
|
|||
|
||||
treeData: [], // 关联菜单
|
||||
assetsType: [] as assetType[], // 资产类型
|
||||
premissonList: [], // 权限列表
|
||||
|
||||
init: () => {
|
||||
// 获取菜单详情
|
||||
routeParams.id &&
|
||||
getMenuInfo_api(routeParams.id).then((resp) => {
|
||||
console.log('菜单详情', resp);
|
||||
form.data = resp.result as formType
|
||||
});
|
||||
// 获取关联菜单
|
||||
getMenuTree_api({ paging: false }).then((resp) => {
|
||||
console.log('关联菜单', resp);
|
||||
getMenuTree_api({ paging: false }).then((resp: any) => {
|
||||
form.treeData = resp.result;
|
||||
});
|
||||
// 获取资产类型
|
||||
getAssetsType_api().then((resp: any) => {
|
||||
|
|
|
@ -1,18 +1,134 @@
|
|||
<template>
|
||||
<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>
|
||||
</template>
|
||||
|
||||
<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 routeParams = {
|
||||
id: route.params.id === ':id' ? '' : route.params.id,
|
||||
id: route.params.id === ':id' ? '' : (route.params.id as string),
|
||||
...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>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
<style scoped></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"
|
||||
allowClear
|
||||
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>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { exportPermission_api } from '@/api/system/permission';
|
||||
|
||||
const props = defineProps({
|
||||
value: Array,
|
||||
});
|
||||
const props = defineProps<{
|
||||
value: any[];
|
||||
firstWidth: number;
|
||||
maxHeight: string;
|
||||
}>();
|
||||
const emits = defineEmits(['update:value']);
|
||||
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({
|
||||
list: [] as permissionType[],
|
||||
// 获取权限列表
|
||||
getList: () => {
|
||||
|
||||
exportPermission_api({ paging: false }).then((resp) => {
|
||||
permission.list = resp.result as permissionType[]
|
||||
const params: paramsType = {
|
||||
paging: false,
|
||||
};
|
||||
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 = {
|
||||
id: string;
|
||||
name: string;
|
||||
actions: object[]
|
||||
}
|
||||
|
||||
checkedList: string[];
|
||||
checkAll: boolean;
|
||||
indeterminate: boolean;
|
||||
options: any[];
|
||||
};
|
||||
type paramsType = {
|
||||
paging: boolean;
|
||||
terms?: object[];
|
||||
};
|
||||
</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>
|
||||
</template>
|
||||
<template #createTime="slotProps">
|
||||
{{ slotProps.createTime }}
|
||||
{{ moment(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') }}
|
||||
</template>
|
||||
<template #action="slotProps">
|
||||
<a-space :size="16">
|
||||
|
@ -30,7 +30,7 @@
|
|||
type="link"
|
||||
@click="table.toDetails(slotProps)"
|
||||
>
|
||||
<edit-outlined />
|
||||
<search-outlined />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
<a-tooltip>
|
||||
|
@ -40,7 +40,7 @@
|
|||
type="link"
|
||||
@click="table.toDetails(slotProps)"
|
||||
>
|
||||
<edit-outlined />
|
||||
<plus-circle-outlined />
|
||||
</a-button>
|
||||
</a-tooltip>
|
||||
|
||||
|
@ -66,6 +66,13 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { getMenuTree_api } from '@/api/system/menu';
|
||||
import {
|
||||
SearchOutlined,
|
||||
DeleteOutlined,
|
||||
PlusOutlined,
|
||||
PlusCircleOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import moment from 'moment';
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
|
|
Loading…
Reference in New Issue