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