mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2026-02-10 19:54:28 +08:00
v3.9.0【优化】typescript版本;【优化】App端消息;【优化】弹出层z-index;
This commit is contained in:
@@ -0,0 +1,288 @@
|
||||
<!--
|
||||
* 菜单 表单弹窗
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-06-12 20:11:39
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<a-drawer
|
||||
:body-style="{ paddingBottom: '80px' }"
|
||||
:maskClosable="true"
|
||||
:title="form.menuId ? '编辑' : '添加'"
|
||||
:open="visible"
|
||||
:width="600"
|
||||
@close="onClose"
|
||||
>
|
||||
<a-form ref="formRef" :labelCol="{ span: labelColSpan }" :labelWrap="true" :model="form" :rules="rules">
|
||||
<a-form-item label="菜单类型" name="menuType">
|
||||
<a-radio-group v-model:value="form.menuType" button-style="solid">
|
||||
<a-radio-button v-for="item in MENU_TYPE_ENUM" :key="item.value" :value="item.value">
|
||||
{{ item.desc }}
|
||||
</a-radio-button>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item :label="form.menuType === MENU_TYPE_ENUM.CATALOG.value ? '上级目录' : '上级菜单'">
|
||||
<MenuTreeSelect ref="parentMenuTreeSelect" v-model:value="form.parentId" />
|
||||
</a-form-item>
|
||||
<!-- 目录 菜单 start -->
|
||||
<template v-if="form.menuType === MENU_TYPE_ENUM.CATALOG.value || form.menuType === MENU_TYPE_ENUM.MENU.value">
|
||||
<a-form-item label="菜单名称" name="menuName">
|
||||
<a-input v-model:value="form.menuName" placeholder="请输入菜单名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="菜单图标" name="icon">
|
||||
<IconSelect @updateIcon="selectIcon">
|
||||
<template #iconSelect>
|
||||
<a-input v-model:value="form.icon" placeholder="请输入菜单图标" style="width: 200px" />
|
||||
<component :is="$antIcons[form.icon]" class="smart-margin-left15" style="font-size: 20px" />
|
||||
</template>
|
||||
</IconSelect>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.menuType === MENU_TYPE_ENUM.MENU.value" label="路由地址" name="path">
|
||||
<a-input v-model:value="form.path" placeholder="请输入路由地址" />
|
||||
</a-form-item>
|
||||
<template v-if="form.menuType === MENU_TYPE_ENUM.MENU.value">
|
||||
<a-form-item v-if="form.frameFlag" label="外链地址" name="frameUrl">
|
||||
<a-input v-model:value="form.frameUrl" placeholder="请输入外链地址" />
|
||||
</a-form-item>
|
||||
<a-form-item v-else label="组件地址" name="component" help="比如 商品列表:/business/erp/goods/goods-list.vue">
|
||||
<a-input v-model:value="form.component" placeholder="请输入组件地址 默认带有开头/@/views" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item v-if="form.menuType === MENU_TYPE_ENUM.MENU.value" label="是否缓存" name="cacheFlag">
|
||||
<a-switch v-model:checked="form.cacheFlag" checked-children="开启缓存" un-checked-children="不缓存" />
|
||||
</a-form-item>
|
||||
<a-form-item v-if="form.menuType === MENU_TYPE_ENUM.MENU.value" label="是否外链" name="frameFlag">
|
||||
<a-switch v-model:checked="form.frameFlag" checked-children="是外链" un-checked-children="不是外链" />
|
||||
</a-form-item>
|
||||
<a-form-item label="显示状态" name="frameFlag">
|
||||
<a-switch v-model:checked="form.visibleFlag" checked-children="显示" un-checked-children="不显示" />
|
||||
</a-form-item>
|
||||
<a-form-item label="禁用状态" name="frameFlag">
|
||||
<a-switch v-model:checked="form.disabledFlag" :checkedValue="false" :unCheckedValue="true" checked-children="启用" un-checked-children="禁用" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- 目录 菜单 end -->
|
||||
<!-- 按钮 start -->
|
||||
<template v-if="form.menuType === MENU_TYPE_ENUM.POINTS.value">
|
||||
<a-form-item label="功能点名称" name="menuName">
|
||||
<a-input v-model:value="form.menuName" placeholder="请输入功能点名称" />
|
||||
</a-form-item>
|
||||
<a-form-item label="功能点关联菜单">
|
||||
<MenuTreeSelect ref="contextMenuTreeSelect" v-model:value="form.contextMenuId" />
|
||||
</a-form-item>
|
||||
<a-form-item label="功能点状态" name="frameFlag">
|
||||
<a-switch v-model:checked="form.disabledFlag" :checkedValue="false" :unCheckedValue="true" checked-children="启用" un-checked-children="禁用" />
|
||||
</a-form-item>
|
||||
<a-form-item label="权限类型" name="permsType">
|
||||
<a-radio-group v-model:value="form.permsType">
|
||||
<a-radio v-for="item in MENU_PERMS_TYPE_ENUM" :key="item.value" :value="item.value">
|
||||
{{ item.desc }}
|
||||
</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item label="前端权限" name="webPerms" help="用于前端按钮等功能的展示和隐藏,搭配v-privilege使用">
|
||||
<a-input v-model:value="form.webPerms" placeholder="请输入前端权限" />
|
||||
</a-form-item>
|
||||
<a-form-item label="后端权限" name="apiPerms" help="后端@SaCheckPermission中的权限字符串,多个以英文逗号,分割">
|
||||
<a-input v-model:value="form.apiPerms" placeholder="请输入后端权限" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
<!-- 按钮 end -->
|
||||
<a-form-item label="排序" name="sort" help="值越小越靠前">
|
||||
<a-input-number v-model:value="form.sort" :min="0" placeholder="请输入排序" style="width: 100px" />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<div class="footer">
|
||||
<a-button style="margin-right: 8px" @click="onClose">取消</a-button>
|
||||
<a-button style="margin-right: 8px" type="primary" @click="onSubmit(false)">提交 </a-button>
|
||||
<a-button v-if="!form.menuId" type="primary" @click="onSubmit(true)">提交并添加下一个 </a-button>
|
||||
</div>
|
||||
</a-drawer>
|
||||
</template>
|
||||
<script setup>
|
||||
import { message } from 'ant-design-vue';
|
||||
import _ from 'lodash';
|
||||
import { computed, nextTick, reactive, ref } from 'vue';
|
||||
import MenuTreeSelect from './menu-tree-select.vue';
|
||||
import { menuApi } from '/@/api/system/menu-api';
|
||||
import IconSelect from '/@/components/framework/icon-select/index.vue';
|
||||
import { MENU_DEFAULT_PARENT_ID, MENU_PERMS_TYPE_ENUM, MENU_TYPE_ENUM } from '/@/constants/system/menu-const';
|
||||
import { smartSentry } from '/@/lib/smart-sentry';
|
||||
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||
|
||||
// ----------------------- 以下是字段定义 emits props ------------------------
|
||||
// emit
|
||||
const emit = defineEmits(['reloadList']);
|
||||
|
||||
// ----------------------- 展开、隐藏编辑窗口 ------------------------
|
||||
|
||||
// 是否展示抽屉
|
||||
const visible = ref(false);
|
||||
|
||||
const labelColSpan = computed(() => {
|
||||
if (form.menuType === MENU_TYPE_ENUM.POINTS.value) {
|
||||
return 6;
|
||||
}
|
||||
return 4;
|
||||
});
|
||||
|
||||
const contextMenuTreeSelect = ref();
|
||||
const parentMenuTreeSelect = ref();
|
||||
|
||||
//展开编辑窗口
|
||||
async function showDrawer(rowData) {
|
||||
Object.assign(form, formDefault);
|
||||
if (rowData && !_.isEmpty(rowData)) {
|
||||
Object.assign(form, rowData);
|
||||
if (form.parentId === MENU_DEFAULT_PARENT_ID) {
|
||||
form.parentId = null;
|
||||
}
|
||||
}
|
||||
visible.value = true;
|
||||
refreshParentAndContext();
|
||||
}
|
||||
|
||||
function refreshParentAndContext() {
|
||||
nextTick(() => {
|
||||
if (contextMenuTreeSelect.value) {
|
||||
contextMenuTreeSelect.value.queryMenuTree();
|
||||
}
|
||||
if (parentMenuTreeSelect.value) {
|
||||
parentMenuTreeSelect.value.queryMenuTree();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 隐藏窗口
|
||||
function onClose() {
|
||||
Object.assign(form, formDefault);
|
||||
formRef.value.resetFields();
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
// ----------------------- form表单相关操作 ------------------------
|
||||
|
||||
const formRef = ref();
|
||||
const formDefault = {
|
||||
menuId: undefined,
|
||||
menuName: undefined,
|
||||
menuType: MENU_TYPE_ENUM.CATALOG.value,
|
||||
icon: undefined,
|
||||
parentId: undefined,
|
||||
path: undefined,
|
||||
permsType: MENU_PERMS_TYPE_ENUM.SA_TOKEN.value,
|
||||
webPerms: undefined,
|
||||
apiPerms: undefined,
|
||||
sort: undefined,
|
||||
visibleFlag: true,
|
||||
cacheFlag: true,
|
||||
component: undefined,
|
||||
contextMenuId: undefined,
|
||||
disabledFlag: false,
|
||||
frameFlag: false,
|
||||
frameUrl: undefined,
|
||||
};
|
||||
let form = reactive({ ...formDefault });
|
||||
|
||||
function continueResetForm() {
|
||||
refreshParentAndContext();
|
||||
const menuType = form.menuType;
|
||||
const parentId = form.parentId;
|
||||
const webPerms = form.webPerms;
|
||||
Object.assign(form, formDefault);
|
||||
formRef.value.resetFields();
|
||||
form.menuType = menuType;
|
||||
form.parentId = parentId;
|
||||
// 移除最后一个:后面的内容
|
||||
if (webPerms && webPerms.lastIndexOf(':')) {
|
||||
form.webPerms = webPerms.substring(0, webPerms.lastIndexOf(':') + 1);
|
||||
}
|
||||
}
|
||||
|
||||
const rules = {
|
||||
menuType: [{ required: true, message: '菜单类型不能为空' }],
|
||||
permsType: [{ required: true, message: '权限类型不能为空' }],
|
||||
menuName: [
|
||||
{ required: true, message: '菜单名称不能为空' },
|
||||
{ max: 20, message: '菜单名称不能大于20个字符', trigger: 'blur' },
|
||||
],
|
||||
frameUrl: [
|
||||
{ required: true, message: '外链地址不能为空' },
|
||||
{ max: 500, message: '外链地址不能大于500个字符', trigger: 'blur' },
|
||||
],
|
||||
path: [
|
||||
{ required: true, message: '路由地址不能为空' },
|
||||
{ max: 100, message: '路由地址不能大于100个字符', trigger: 'blur' },
|
||||
],
|
||||
};
|
||||
|
||||
function validateForm(formRef) {
|
||||
return new Promise((resolve) => {
|
||||
formRef
|
||||
.validate()
|
||||
.then(() => {
|
||||
resolve(true);
|
||||
})
|
||||
.catch(() => {
|
||||
resolve(false);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const onSubmit = async (continueFlag) => {
|
||||
let validateFormRes = await validateForm(formRef.value);
|
||||
if (!validateFormRes) {
|
||||
message.error('参数验证错误,请仔细填写表单数据!');
|
||||
return;
|
||||
}
|
||||
SmartLoading.show();
|
||||
try {
|
||||
let params = _.cloneDeep(form);
|
||||
// 若无父级ID 默认设置为0
|
||||
if (!params.parentId) {
|
||||
params.parentId = 0;
|
||||
}
|
||||
if (params.menuId) {
|
||||
await menuApi.updateMenu(params);
|
||||
} else {
|
||||
await menuApi.addMenu(params);
|
||||
}
|
||||
message.success(`${params.menuId ? '修改' : '添加'}成功`);
|
||||
if (continueFlag) {
|
||||
continueResetForm();
|
||||
} else {
|
||||
onClose();
|
||||
}
|
||||
emit('reloadList');
|
||||
} catch (error) {
|
||||
smartSentry.captureError(error);
|
||||
} finally {
|
||||
SmartLoading.hide();
|
||||
}
|
||||
};
|
||||
|
||||
function selectIcon(icon) {
|
||||
form.icon = icon;
|
||||
}
|
||||
|
||||
// ----------------------- 以下是暴露的方法内容 ------------------------
|
||||
defineExpose({
|
||||
showDrawer,
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.footer {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
border-top: 1px solid #e9e9e9;
|
||||
padding: 10px 16px;
|
||||
background: #fff;
|
||||
text-align: left;
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,49 @@
|
||||
<!--
|
||||
* 菜单 表单 树形下拉框
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-06-12 20:11:39
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<a-tree-select
|
||||
:value="props.value"
|
||||
:treeData="treeData"
|
||||
:fieldNames="{ label: 'menuName', key: 'menuId', value: 'menuId' }"
|
||||
show-search
|
||||
style="width: 100%"
|
||||
:dropdown-style="{ maxHeight: '400px', overflow: 'auto' }"
|
||||
placeholder="请选择菜单"
|
||||
allow-clear
|
||||
tree-default-expand-all
|
||||
treeNodeFilterProp="menuName"
|
||||
@change="treeSelectChange"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { menuApi } from '/@/api/system/menu-api';
|
||||
|
||||
const props = defineProps({
|
||||
value: Number,
|
||||
});
|
||||
|
||||
let treeData = ref([]);
|
||||
async function queryMenuTree() {
|
||||
let res = await menuApi.queryMenuTree(true);
|
||||
treeData.value = res.data;
|
||||
}
|
||||
|
||||
onMounted(queryMenuTree);
|
||||
|
||||
const emit = defineEmits(['update:value']);
|
||||
function treeSelectChange(e) {
|
||||
emit('update:value', e);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
queryMenuTree,
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 此文件是处理 菜单数据的类,主要用于:
|
||||
* 1、菜单树形表格的构造
|
||||
* 2、菜单的前端过滤
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-06-15 16:47:20
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
*/
|
||||
|
||||
import _ from 'lodash';
|
||||
/**
|
||||
* 过滤菜单
|
||||
* @param {*} menuList
|
||||
* @param {*} queryForm
|
||||
* @returns
|
||||
*/
|
||||
export const filterMenuByQueryForm = (menuList, queryForm) => {
|
||||
if (!menuList || menuList.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let filterResult = [];
|
||||
for (const menu of menuList) {
|
||||
if (isMenuExistKeywords(menu, queryForm.keywords) && isMenuExistMenuType(menu, queryForm.menuType) && isMenuExistMenuFlag(menu, queryForm)) {
|
||||
filterResult.push(menu);
|
||||
}
|
||||
}
|
||||
return filterResult;
|
||||
};
|
||||
|
||||
/**
|
||||
* 构建菜单表格树形数据
|
||||
*/
|
||||
export const buildMenuTableTree = (menuList) => {
|
||||
let topMenuList = [];
|
||||
const menuIdSet = new Set();
|
||||
for (const menu of menuList) {
|
||||
menuIdSet.add(menu.menuId);
|
||||
}
|
||||
|
||||
for (const menu of menuList) {
|
||||
const parentId = menu.parentId;
|
||||
// 不存在父节点,则为顶级菜单
|
||||
if (!menuIdSet.has(parentId)) {
|
||||
topMenuList.push(menu);
|
||||
}
|
||||
}
|
||||
|
||||
recursiveMenuTree(menuList, topMenuList);
|
||||
return topMenuList;
|
||||
};
|
||||
|
||||
/**
|
||||
* 递归遍历菜单树形数据
|
||||
* @param {*} menuList
|
||||
* @param {*} parentArray
|
||||
*/
|
||||
function recursiveMenuTree(menuList, parentArray) {
|
||||
for (const parent of parentArray) {
|
||||
const children = menuList.filter((e) => e.parentId === parent.menuId);
|
||||
if (children.length > 0) {
|
||||
parent.children = children;
|
||||
recursiveMenuTree(menuList, parent.children);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤菜单状态
|
||||
* @param {*} menu
|
||||
* @param {*} queryForm
|
||||
* @returns
|
||||
*/
|
||||
function isMenuExistMenuFlag(menu, queryForm) {
|
||||
let frameFlagCondition = false;
|
||||
if (!_.isNil(queryForm.frameFlag)) {
|
||||
frameFlagCondition = !_.isNil(menu.frameFlag) && menu.frameFlag === (queryForm.frameFlag === 1);
|
||||
} else {
|
||||
frameFlagCondition = true;
|
||||
}
|
||||
|
||||
let cacheFlagCondition = false;
|
||||
if (!_.isNil(queryForm.cacheFlag)) {
|
||||
cacheFlagCondition = !_.isNil(menu.cacheFlag) && menu.cacheFlag === (queryForm.cacheFlag === 1);
|
||||
} else {
|
||||
cacheFlagCondition = true;
|
||||
}
|
||||
|
||||
let visibleFlagCondition = false;
|
||||
if (!_.isNil(queryForm.visibleFlag)) {
|
||||
visibleFlagCondition = !_.isNil(menu.visibleFlag) && menu.visibleFlag === (queryForm.visibleFlag === 1);
|
||||
} else {
|
||||
visibleFlagCondition = true;
|
||||
}
|
||||
|
||||
let disabledFlagCondition = false;
|
||||
if (!_.isNil(queryForm.disabledFlag)) {
|
||||
disabledFlagCondition = !_.isNil(menu.disabledFlag) && menu.disabledFlag === (queryForm.disabledFlag === 1);
|
||||
} else {
|
||||
disabledFlagCondition = true;
|
||||
}
|
||||
|
||||
return frameFlagCondition && cacheFlagCondition && visibleFlagCondition && disabledFlagCondition;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤菜单类型
|
||||
* @param {*} menu
|
||||
* @param {*} menuType
|
||||
* @returns
|
||||
*/
|
||||
function isMenuExistMenuType(menu, menuType) {
|
||||
if (!menuType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (menu.menuType && menu.menuType === menuType) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 过滤关键字
|
||||
*/
|
||||
function isMenuExistKeywords(menu, keywords) {
|
||||
if (!keywords) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (menu.component && menu.component.indexOf(keywords) > -1) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (menu.menuName && menu.menuName.indexOf(keywords) > -1) {
|
||||
return true;
|
||||
}
|
||||
if (menu.path && menu.path.indexOf(keywords) > -1) {
|
||||
return true;
|
||||
}
|
||||
if (menu.apiPerms && menu.apiPerms.indexOf(keywords) > -1) {
|
||||
return true;
|
||||
}
|
||||
if (menu.webPerms && menu.webPerms.indexOf(keywords) > -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 菜单表格列
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-05-12 19:46:11
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
*/
|
||||
import { ref } from 'vue';
|
||||
export const columns = ref([
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'menuName',
|
||||
key: 'ID',
|
||||
width: 200,
|
||||
},
|
||||
{
|
||||
title: '类型',
|
||||
dataIndex: 'menuType',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '图标',
|
||||
dataIndex: 'icon',
|
||||
width: 50,
|
||||
},
|
||||
{
|
||||
title: '路径',
|
||||
dataIndex: 'path',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '组件',
|
||||
dataIndex: 'component',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '后端权限',
|
||||
dataIndex: 'apiPerms',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '前端权限',
|
||||
dataIndex: 'webPerms',
|
||||
ellipsis: true,
|
||||
},
|
||||
{
|
||||
title: '顺序',
|
||||
dataIndex: 'sort',
|
||||
width: 80,
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'operate',
|
||||
width: 100,
|
||||
},
|
||||
]);
|
||||
258
smart-admin-web-javascript/src/views/system/menu/menu-list.vue
Normal file
258
smart-admin-web-javascript/src/views/system/menu/menu-list.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<!--
|
||||
* 菜单列表
|
||||
*
|
||||
* @Author: 1024创新实验室-主任:卓大
|
||||
* @Date: 2022-06-12 20:11:39
|
||||
* @Wechat: zhuda1024
|
||||
* @Email: lab1024@163.com
|
||||
* @Copyright 1024创新实验室 ( https://1024lab.net ),Since 2012
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
<a-form class="smart-query-form">
|
||||
<a-row class="smart-query-form-row">
|
||||
<a-form-item label="关键字" class="smart-query-form-item">
|
||||
<a-input style="width: 300px" v-model:value="queryForm.keywords" placeholder="菜单名称/路由地址/组件路径/权限字符串" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="类型" class="smart-query-form-item">
|
||||
<SmartEnumSelect width="120px" v-model:value="queryForm.menuType" placeholder="请选择类型" enum-name="MENU_TYPE_ENUM" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="禁用" class="smart-query-form-item">
|
||||
<SmartEnumSelect width="120px" enum-name="FLAG_NUMBER_ENUM" v-model:value="queryForm.disabledFlag" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item class="smart-query-form-item smart-margin-left10">
|
||||
<a-button-group>
|
||||
<a-button type="primary" @click="query">
|
||||
<template #icon>
|
||||
<SearchOutlined />
|
||||
</template>
|
||||
查询
|
||||
</a-button>
|
||||
|
||||
<a-button @click="resetQuery">
|
||||
<template #icon>
|
||||
<ReloadOutlined />
|
||||
</template>
|
||||
重置
|
||||
</a-button>
|
||||
</a-button-group>
|
||||
<a-button class="smart-margin-left20" @click="moreQueryConditionFlag = !moreQueryConditionFlag">
|
||||
<template #icon>
|
||||
<MoreOutlined />
|
||||
</template>
|
||||
{{ moreQueryConditionFlag ? '收起' : '展开' }}
|
||||
</a-button>
|
||||
</a-form-item>
|
||||
</a-row>
|
||||
|
||||
<a-row class="smart-query-form-row" v-show="moreQueryConditionFlag">
|
||||
<a-form-item label="外链" class="smart-query-form-item">
|
||||
<SmartEnumSelect width="120px" enum-name="FLAG_NUMBER_ENUM" v-model:value="queryForm.frameFlag" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="缓存" class="smart-query-form-item">
|
||||
<SmartEnumSelect width="120px" enum-name="FLAG_NUMBER_ENUM" v-model:value="queryForm.cacheFlag" />
|
||||
</a-form-item>
|
||||
|
||||
<a-form-item label="显示" class="smart-query-form-item">
|
||||
<SmartEnumSelect width="120px" enum-name="FLAG_NUMBER_ENUM" v-model:value="queryForm.visibleFlag" />
|
||||
</a-form-item>
|
||||
</a-row>
|
||||
</a-form>
|
||||
|
||||
<a-card size="small" :bordered="false" :hoverable="true">
|
||||
<a-row class="smart-table-btn-block">
|
||||
<div class="smart-table-operate-block">
|
||||
<a-button v-privilege="'system:menu:add'" type="primary" @click="showDrawer">
|
||||
<template #icon>
|
||||
<PlusOutlined />
|
||||
</template>
|
||||
添加菜单
|
||||
</a-button>
|
||||
|
||||
<a-button v-privilege="'system:menu:batchDelete'" type="primary" danger @click="batchDelete" :disabled="!hasSelected">
|
||||
<template #icon>
|
||||
<DeleteOutlined />
|
||||
</template>
|
||||
批量删除
|
||||
</a-button>
|
||||
</div>
|
||||
<div class="smart-table-setting-block">
|
||||
<TableOperator v-model="columns" :tableId="TABLE_ID_CONST.SYSTEM.MENU" :refresh="query" />
|
||||
</div>
|
||||
</a-row>
|
||||
|
||||
<a-table
|
||||
:row-selection="{ selectedRowKeys: selectedRowKeys, onChange: onSelectChange }"
|
||||
size="small"
|
||||
:defaultExpandAllRows="true"
|
||||
:dataSource="tableData"
|
||||
bordered
|
||||
:columns="columns"
|
||||
:loading="tableLoading"
|
||||
rowKey="menuId"
|
||||
:pagination="false"
|
||||
>
|
||||
<template #bodyCell="{ text, record, column }">
|
||||
<template v-if="column.dataIndex === 'menuType'">
|
||||
<a-tag :color="menuTypeColorArray[text]">{{ $smartEnumPlugin.getDescByValue('MENU_TYPE_ENUM', text) }}</a-tag>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'component'">
|
||||
<span>{{ record.frameFlag ? record.frameUrl : record.component }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'frameFlag'">
|
||||
<span>{{ $smartEnumPlugin.getDescByValue('FLAG_NUMBER_ENUM', text) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'permsType'">
|
||||
<span>{{ $smartEnumPlugin.getDescByValue('MENU_PERMS_TYPE_ENUM', text) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'cacheFlag'">
|
||||
<span>{{ $smartEnumPlugin.getDescByValue('FLAG_NUMBER_ENUM', text) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'visibleFlag'">
|
||||
<span>{{ $smartEnumPlugin.getDescByValue('FLAG_NUMBER_ENUM', text) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'disabledFlag'">
|
||||
<span>{{ $smartEnumPlugin.getDescByValue('FLAG_NUMBER_ENUM', text) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'icon'">
|
||||
<component :is="$antIcons[text]" />
|
||||
</template>
|
||||
|
||||
<template v-if="column.dataIndex === 'operate'">
|
||||
<div class="smart-table-operate">
|
||||
<a-button v-privilege="'system:menu:update'" type="link" size="small" @click="showDrawer(record)">编辑</a-button>
|
||||
<a-button v-privilege="'system:menu:batchDelete'" danger type="link" @click="singleDelete(record)">删除</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
</a-card>
|
||||
|
||||
<MenuOperateModal ref="menuOperateModal" @reloadList="query" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
|
||||
import { message, Modal } from 'ant-design-vue';
|
||||
import _ from 'lodash';
|
||||
import { computed, createVNode, onMounted, reactive, ref } from 'vue';
|
||||
import MenuOperateModal from './components/menu-operate-modal.vue';
|
||||
import { buildMenuTableTree, filterMenuByQueryForm } from './menu-data-handler';
|
||||
import { columns } from './menu-list-table-columns';
|
||||
import { menuApi } from '/@/api/system/menu-api';
|
||||
import SmartEnumSelect from '/@/components/framework/smart-enum-select/index.vue';
|
||||
import { SmartLoading } from '/@/components/framework/smart-loading';
|
||||
import { smartSentry } from '/@/lib/smart-sentry';
|
||||
import TableOperator from '/@/components/support/table-operator/index.vue';
|
||||
import { TABLE_ID_CONST } from '/@/constants/support/table-id-const';
|
||||
|
||||
// ------------------------ 表格渲染 ------------------------
|
||||
const menuTypeColorArray = ['red', 'blue', 'orange', 'green'];
|
||||
|
||||
// ------------------------ 查询表单 ------------------------
|
||||
const queryFormState = {
|
||||
keywords: '',
|
||||
menuType: undefined,
|
||||
frameFlag: undefined,
|
||||
cacheFlag: undefined,
|
||||
visibleFlag: undefined,
|
||||
disabledFlag: undefined,
|
||||
};
|
||||
const queryForm = reactive({ ...queryFormState });
|
||||
//展开更多查询参数
|
||||
const moreQueryConditionFlag = ref(true);
|
||||
|
||||
// ------------------------ table表格数据和查询方法 ------------------------
|
||||
|
||||
const tableLoading = ref(false);
|
||||
const tableData = ref([]);
|
||||
|
||||
function resetQuery() {
|
||||
Object.assign(queryForm, queryFormState);
|
||||
query();
|
||||
}
|
||||
|
||||
onMounted(query);
|
||||
|
||||
async function query() {
|
||||
try {
|
||||
tableLoading.value = true;
|
||||
let responseModel = await menuApi.queryMenu();
|
||||
// 过滤搜索条件
|
||||
const filtedMenuList = filterMenuByQueryForm(responseModel.data, queryForm);
|
||||
// 递归构造树形结构,并付给 TableTree组件
|
||||
tableData.value = buildMenuTableTree(filtedMenuList);
|
||||
} catch (e) {
|
||||
smartSentry.captureError(e);
|
||||
} finally {
|
||||
tableLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------- 多选操作 --------------
|
||||
const selectedRowKeys = ref([]);
|
||||
let selectedRows = [];
|
||||
const hasSelected = computed(() => selectedRowKeys.value.length > 0);
|
||||
|
||||
function onSelectChange(keyArray, selectRows) {
|
||||
selectedRowKeys.value = keyArray;
|
||||
selectedRows = selectRows;
|
||||
}
|
||||
|
||||
function singleDelete(record) {
|
||||
confirmBatchDelete([record]);
|
||||
}
|
||||
|
||||
function batchDelete() {
|
||||
confirmBatchDelete(selectedRows);
|
||||
}
|
||||
|
||||
function confirmBatchDelete(menuArray) {
|
||||
const menuNameArray = menuArray.map((e) => e.menuName);
|
||||
Modal.confirm({
|
||||
title: '确定要删除如下菜单吗?',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: _.join(menuNameArray, '、'),
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
onOk() {
|
||||
console.log('OK');
|
||||
const menuIdList = menuArray.map((e) => e.menuId);
|
||||
requestBatchDelete(menuIdList);
|
||||
selectedRows = [];
|
||||
},
|
||||
cancelText: '取消',
|
||||
onCancel() {},
|
||||
});
|
||||
|
||||
async function requestBatchDelete(menuIdList) {
|
||||
SmartLoading.show();
|
||||
try {
|
||||
await menuApi.batchDeleteMenu(menuIdList);
|
||||
message.success('删除成功!');
|
||||
query();
|
||||
} catch (e) {
|
||||
smartSentry.captureError(e);
|
||||
} finally {
|
||||
SmartLoading.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------- 添加、修改 右侧抽屉 --------------
|
||||
const menuOperateModal = ref();
|
||||
function showDrawer(rowData) {
|
||||
menuOperateModal.value.showDrawer(rowData);
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user