1 changed files with 463 additions and 0 deletions
@ -0,0 +1,463 @@ |
|||
<script setup lang="ts"> |
|||
defineOptions({ |
|||
name: "cmenu", |
|||
inheritAttrs: false, |
|||
}); |
|||
|
|||
import { MenuQuery, MenuForm, MenuVO } from "@/api/menu/types"; |
|||
import { |
|||
listMenus, |
|||
getMenuForm, |
|||
listMenuOptions, |
|||
addMenu, |
|||
deleteMenu, |
|||
updateMenu, |
|||
} from "@/api/menu"; |
|||
|
|||
import { MenuTypeEnum } from "@/enums/MenuTypeEnum"; |
|||
|
|||
import SvgIcon from "@/components/SvgIcon/index.vue"; |
|||
import IconSelect from "@/components/IconSelect/index.vue"; |
|||
|
|||
const queryFormRef = ref(ElForm); |
|||
const menuFormRef = ref(ElForm); |
|||
|
|||
const loading = ref(false); |
|||
const dialog = reactive<DialogOption>({ |
|||
visible: false, |
|||
}); |
|||
|
|||
const queryParams = reactive<MenuQuery>({}); |
|||
const menuList = ref<MenuVO[]>([]); |
|||
|
|||
const menuOptions = ref<OptionType[]>([]); |
|||
|
|||
const formData = reactive<MenuForm>({ |
|||
parentId: 0, |
|||
visible: 1, |
|||
sort: 1, |
|||
type: MenuTypeEnum.MENU, |
|||
outside:1 |
|||
}); |
|||
|
|||
const rules = reactive({ |
|||
parentId: [{ required: true, message: "请选择顶级菜单", trigger: "blur" }], |
|||
name: [{ required: true, message: "请输入菜单名称", trigger: "blur" }], |
|||
type: [{ required: true, message: "请选择菜单类型", trigger: "blur" }], |
|||
path: [{ required: true, message: "请输入路由路径", trigger: "blur" }], |
|||
component: [ |
|||
{ required: true, message: "请输入组件完整路径", trigger: "blur" }, |
|||
], |
|||
}); |
|||
|
|||
// 选择表格的行菜单ID |
|||
const selectedRowMenuId = ref<number | undefined>(); |
|||
|
|||
const menuCacheData = reactive({ |
|||
type: "", |
|||
path: "", |
|||
}); |
|||
|
|||
/** |
|||
* 查询 |
|||
*/ |
|||
function handleQuery() { |
|||
// 重置父组件 |
|||
loading.value = true; |
|||
listMenus(queryParams) |
|||
.then(({ data }) => { |
|||
menuList.value = data; |
|||
}) |
|||
.then(() => { |
|||
loading.value = false; |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 查询重置 |
|||
*/ |
|||
function resetQuery() { |
|||
queryFormRef.value.resetFields(); |
|||
handleQuery(); |
|||
} |
|||
|
|||
/** |
|||
* 行点击事件 |
|||
* |
|||
* @param row |
|||
*/ |
|||
function onRowClick(row: MenuVO) { |
|||
selectedRowMenuId.value = row.id; |
|||
} |
|||
|
|||
/** |
|||
* 打开表单弹窗 |
|||
* |
|||
* @param parentId 父菜单ID |
|||
* @param menuId 菜单ID |
|||
*/ |
|||
function openDialog(parentId?: number, menuId?: number) { |
|||
listMenuOptions() |
|||
.then(({ data }) => { |
|||
menuOptions.value = [{ value: 0, label: "顶级菜单", children: data }]; |
|||
}) |
|||
.then(() => { |
|||
dialog.visible = true; |
|||
if (menuId) { |
|||
dialog.title = "编辑菜单"; |
|||
getMenuForm({id:menuId.toString()}).then(({ data }) => { |
|||
Object.assign(formData, data); |
|||
menuCacheData.type = data.type; |
|||
menuCacheData.path = data.path ?? ""; |
|||
}); |
|||
} else { |
|||
dialog.title = "新增菜单"; |
|||
formData.parentId = parentId; |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 菜单类型 change |
|||
*/ |
|||
function onMenuTypeChange() { |
|||
// 如果菜单类型改变,清空路由路径;未改变在切换后还原路由路径 |
|||
if (formData.type !== menuCacheData.type) { |
|||
formData.path = ""; |
|||
} else { |
|||
formData.path = menuCacheData.path; |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 菜单提交 |
|||
*/ |
|||
function submitForm() { |
|||
menuFormRef.value.validate((isValid: boolean) => { |
|||
if (isValid) { |
|||
const menuId = formData.id; |
|||
if (menuId) { |
|||
updateMenu(formData).then(() => { |
|||
ElMessage.success("修改成功"); |
|||
closeDialog(); |
|||
handleQuery(); |
|||
}); |
|||
} else { |
|||
addMenu(formData).then(() => { |
|||
ElMessage.success("新增成功"); |
|||
closeDialog(); |
|||
handleQuery(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
} |
|||
|
|||
/** |
|||
* 删除菜单 |
|||
*/ |
|||
function handleDelete(menuId: number) { |
|||
if (!menuId) { |
|||
ElMessage.warning("请勾选删除项"); |
|||
return false; |
|||
} |
|||
|
|||
ElMessageBox.confirm("确认删除已选中的数据项?", "警告", { |
|||
confirmButtonText: "确定", |
|||
cancelButtonText: "取消", |
|||
type: "warning", |
|||
}) |
|||
.then(() => { |
|||
deleteMenu({id:menuId.toString()}).then(() => { |
|||
ElMessage.success("删除成功"); |
|||
handleQuery(); |
|||
}); |
|||
}) |
|||
.catch(() => ElMessage.info("已取消删除")); |
|||
} |
|||
|
|||
/** |
|||
* 关闭弹窗 |
|||
*/ |
|||
function closeDialog() { |
|||
dialog.visible = false; |
|||
resetForm(); |
|||
} |
|||
|
|||
/** |
|||
* 重置表单 |
|||
*/ |
|||
function resetForm() { |
|||
menuFormRef.value.resetFields(); |
|||
menuFormRef.value.clearValidate(); |
|||
|
|||
formData.id = undefined; |
|||
formData.parentId = 0; |
|||
formData.visible = 1; |
|||
formData.sort = 1; |
|||
} |
|||
|
|||
onMounted(() => { |
|||
handleQuery(); |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<div class="app-container"> |
|||
<div class="search"> |
|||
<el-form ref="queryFormRef" :model="queryParams" :inline="true"> |
|||
<el-form-item label="关键字" prop="keywords"> |
|||
<el-input |
|||
v-model="queryParams.keywords" |
|||
placeholder="菜单名称" |
|||
clearable |
|||
@keyup.enter="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" @click="handleQuery" |
|||
><template #icon><i-ep-search /></template>搜索</el-button |
|||
> |
|||
<el-button @click="resetQuery"> |
|||
<template #icon><i-ep-refresh /></template> |
|||
重置</el-button |
|||
> |
|||
</el-form-item> |
|||
</el-form> |
|||
</div> |
|||
|
|||
<el-card shadow="never"> |
|||
<template #header> |
|||
<el-button type="success" @click="openDialog(0)" v-hasPerm="['121646328009732096']"> |
|||
<template #icon><i-ep-plus /></template> |
|||
新增</el-button |
|||
> |
|||
</template> |
|||
|
|||
<el-table |
|||
v-loading="loading" |
|||
:data="menuList" |
|||
highlight-current-row |
|||
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }" |
|||
row-key="id" |
|||
default-expand-all |
|||
border |
|||
@row-click="onRowClick" |
|||
> |
|||
<el-table-column label="菜单名称" min-width="200"> |
|||
<template #default="scope"> |
|||
<svg-icon |
|||
:icon-class=" |
|||
scope.row.type === MenuTypeEnum.BUTTON |
|||
? 'button' |
|||
: scope.row.icon |
|||
" |
|||
/> |
|||
{{ scope.row.name }} |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column label="菜单类型" align="center" width="100"> |
|||
<template #default="scope"> |
|||
<el-tag |
|||
v-if="scope.row.class === MenuTypeEnum.CATALOG" |
|||
type="warning" |
|||
>目录</el-tag |
|||
> |
|||
<el-tag v-if="scope.row.class === MenuTypeEnum.MENU" type="success" |
|||
>菜单</el-tag |
|||
> |
|||
<el-tag v-if="scope.row.class === MenuTypeEnum.BUTTON" type="danger" |
|||
>按钮</el-tag |
|||
> |
|||
<el-tag v-if="scope.row.class === MenuTypeEnum.EXTLINK" type="info" |
|||
>外链</el-tag |
|||
> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column |
|||
label="权限标识" |
|||
align="center" |
|||
width="200" |
|||
prop="permcode" |
|||
/> |
|||
|
|||
<el-table-column label="状态" align="center" width="100"> |
|||
<template #default="scope"> |
|||
<el-tag v-if="scope.row.visible === 1" type="success">显示</el-tag> |
|||
<el-tag v-else type="info">隐藏</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
|
|||
<el-table-column label="排序" align="center" width="50" prop="sort" /> |
|||
|
|||
<el-table-column fixed="right" align="center" label="操作" width="220"> |
|||
<template #default="scope"> |
|||
<el-button |
|||
v-if="scope.row.class == 'CATALOG' || scope.row.class == 'MENU'" |
|||
v-hasPerm="['121646328009732096']" |
|||
type="primary" |
|||
link |
|||
size="small" |
|||
@click.stop="openDialog(scope.row.id)" |
|||
> |
|||
<i-ep-plus />新增 |
|||
</el-button> |
|||
|
|||
<el-button |
|||
v-hasPerm="['122274485305880576']" |
|||
type="primary" |
|||
link |
|||
size="small" |
|||
@click.stop="openDialog(undefined, scope.row.id)" |
|||
> |
|||
<i-ep-edit />编辑 |
|||
</el-button> |
|||
<el-button |
|||
v-hasPerm="['122274565337395200']" |
|||
type="primary" |
|||
link |
|||
size="small" |
|||
@click.stop="handleDelete(scope.row.id)" |
|||
><i-ep-delete /> |
|||
删除 |
|||
</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
</el-card> |
|||
|
|||
<el-dialog |
|||
v-model="dialog.visible" |
|||
:title="dialog.title" |
|||
destroy-on-close |
|||
append-to-body |
|||
width="750px" |
|||
@close="closeDialog" |
|||
> |
|||
<el-form |
|||
ref="menuFormRef" |
|||
:model="formData" |
|||
:rules="rules" |
|||
label-width="100px" |
|||
> |
|||
<el-form-item label="父级菜单" prop="parentId"> |
|||
<el-tree-select |
|||
v-model="formData.parentId" |
|||
placeholder="选择上级菜单" |
|||
:data="menuOptions" |
|||
filterable |
|||
check-strictly |
|||
:render-after-expand="false" |
|||
/> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="菜单名称" prop="name"> |
|||
<el-input v-model="formData.name" placeholder="请输入菜单名称" /> |
|||
</el-form-item> |
|||
|
|||
<el-form-item label="菜单类型" prop="type"> |
|||
<el-radio-group v-model="formData.type" @change="onMenuTypeChange"> |
|||
<el-radio label="CATALOG">目录</el-radio> |
|||
<el-radio label="MENU">菜单</el-radio> |
|||
<el-radio label="BUTTON">按钮</el-radio> |
|||
<el-radio label="EXTLINK">外链</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
|
|||
<el-form-item |
|||
v-if="formData.type == 'EXTLINK'" |
|||
label="外链地址" |
|||
prop="path" |
|||
> |
|||
<el-input v-model="formData.path" placeholder="请输入外链完整路径" /> |
|||
</el-form-item> |
|||
|
|||
<el-form-item |
|||
v-if="formData.type == 'CATALOG' || formData.type == 'MENU'" |
|||
label="路由路径" |
|||
prop="path" |
|||
> |
|||
<el-input |
|||
v-if="formData.type == 'CATALOG'" |
|||
v-model="formData.path" |
|||
placeholder="/system (目录以/开头)" |
|||
/> |
|||
<el-input v-else v-model="formData.path" placeholder="user" /> |
|||
</el-form-item> |
|||
|
|||
<!-- 组件页面完整路径 --> |
|||
<el-form-item |
|||
v-if="formData.type == MenuTypeEnum.MENU" |
|||
label="页面路径" |
|||
prop="component" |
|||
> |
|||
<el-input |
|||
v-model="formData.component" |
|||
placeholder="system/user/index" |
|||
style="width: 95%" |
|||
> |
|||
<template v-if="formData.parentId != 0" #prepend |
|||
>src/views/</template |
|||
> |
|||
<template v-if="formData.parentId != 0" #append>.vue</template> |
|||
</el-input> |
|||
</el-form-item> |
|||
|
|||
<!-- 权限标识 --> |
|||
<!-- <el-form-item |
|||
v-if="formData.type == 'BUTTON'" |
|||
label="权限标识" |
|||
prop="perm" |
|||
> |
|||
<el-input v-model="formData.perm" placeholder="sys:user:add" /> |
|||
</el-form-item> --> |
|||
|
|||
<el-form-item |
|||
v-if="formData.type !== 'BUTTON'" |
|||
label="图标" |
|||
prop="icon" |
|||
> |
|||
<!-- 图标选择器 --> |
|||
<icon-select v-model="formData.icon" /> |
|||
</el-form-item> |
|||
|
|||
<el-form-item |
|||
v-if="formData.type == MenuTypeEnum.CATALOG" |
|||
label="跳转路由" |
|||
> |
|||
<el-input v-model="formData.redirect" placeholder="跳转路由" /> |
|||
</el-form-item> |
|||
|
|||
<el-form-item v-if="formData.type !== 'BUTTON'" label="状态"> |
|||
<el-radio-group v-model="formData.visible"> |
|||
<el-radio :label="1">显示</el-radio> |
|||
<el-radio :label="0">隐藏</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="可见范围"> |
|||
<el-radio-group v-model="formData.outside"> |
|||
<el-radio :label="1">内部使用</el-radio> |
|||
<el-radio :label="2">外部使用</el-radio> |
|||
<el-radio :label="3">内外使用</el-radio> |
|||
</el-radio-group> |
|||
</el-form-item> |
|||
<el-form-item label="排序" prop="sort"> |
|||
<el-input-number |
|||
v-model="formData.sort" |
|||
style="width: 100px" |
|||
controls-position="right" |
|||
:min="0" |
|||
/> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<template #footer> |
|||
<div class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="closeDialog">取 消</el-button> |
|||
</div> |
|||
</template> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
Loading…
Reference in new issue