You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
370 lines
12 KiB
370 lines
12 KiB
<template> |
|
<div class="container"> |
|
<header class="flex between mb-20"> |
|
<div> |
|
<el-button type="primary" @click="handleAdd"> |
|
<template #icon> |
|
<icon name="el-icon-Plus" /> |
|
</template> |
|
新增</el-button |
|
> |
|
</div> |
|
<div></div> |
|
</header> |
|
<div class="table-container"> |
|
<el-table :data="menus" row-key="id"> |
|
<el-table-column label="菜单名称" prop="menuName" /> |
|
<el-table-column label="类型"> |
|
<template #default="{ row }"> |
|
<div |
|
class="flex v-center gap-4" |
|
v-if="row.menuType === MenuEnum.CATALOGUE" |
|
> |
|
<icon name="el-icon-Folder" /> |
|
<span>目录</span> |
|
</div> |
|
<div |
|
class="flex v-center gap-4" |
|
v-else-if="row.menuType === MenuEnum.MENU" |
|
> |
|
<icon name="local-icon-menu" /> |
|
<span>菜单</span> |
|
</div> |
|
<div |
|
class="flex v-center gap-4" |
|
v-else-if="row.menuType === MenuEnum.BUTTON" |
|
> |
|
<icon name="el-icon-SwitchButton" /> |
|
<span>按钮</span> |
|
</div> |
|
</template> |
|
</el-table-column> |
|
<el-table-column label="图标"> |
|
<template #default="{ row }"> |
|
<icon :name="row.icon" :size="16" v-if="row.icon" /> |
|
</template> |
|
</el-table-column> |
|
<el-table-column label="权限标识" prop="perms" /> |
|
<el-table-column label="排序" prop="menuSort" /> |
|
<el-table-column label="最后更新时间" prop="updateTime" /> |
|
<el-table-column label="操作"> |
|
<template #default="{ row }"> |
|
<el-button type="primary" link @click="handleEdit(row)" |
|
>编辑</el-button |
|
> |
|
<el-button type="danger" link @click="handleDelete(row)" |
|
>删除</el-button |
|
> |
|
</template> |
|
</el-table-column> |
|
</el-table> |
|
</div> |
|
</div> |
|
|
|
<el-dialog |
|
v-model="show" |
|
:title="mode === 'add' ? '新增菜单' : '编辑菜单'" |
|
width="600" |
|
> |
|
<el-form label-width="120" ref="formRef" :model="formData"> |
|
<el-form-item |
|
label="菜单类型" |
|
prop="menuType" |
|
:rules="{ |
|
required: true, |
|
message: '请选择菜单类型', |
|
trigger: ['blur'], |
|
}" |
|
> |
|
<el-radio-group v-model="formData.menuType"> |
|
<el-radio :label="MenuEnum.CATALOGUE">目录</el-radio> |
|
<el-radio :label="MenuEnum.MENU">菜单</el-radio> |
|
<el-radio :label="MenuEnum.BUTTON">按钮</el-radio> |
|
</el-radio-group> |
|
</el-form-item> |
|
<el-form-item |
|
label="父级菜单" |
|
prop="menuType" |
|
:rules="{ |
|
required: true, |
|
message: '请选择父级菜单', |
|
trigger: ['blur'], |
|
}" |
|
> |
|
<el-tree-select |
|
class="flex-1" |
|
v-model="formData.pid" |
|
:data="menuOptions" |
|
clearable |
|
node-key="id" |
|
:props="{ |
|
label: 'menuName', |
|
}" |
|
:default-expanded-keys="[MENU_ROOT_ID]" |
|
placeholder="请选择父级菜单" |
|
check-strictly |
|
/> |
|
</el-form-item> |
|
<el-form-item |
|
label="菜单名称" |
|
prop="menuName" |
|
:rules="{ |
|
required: true, |
|
message: '请输入菜单名称', |
|
trigger: ['blur'], |
|
}" |
|
> |
|
<el-input |
|
v-model="formData.menuName" |
|
placeholder="请输入菜单名称" |
|
clearable |
|
/> |
|
</el-form-item> |
|
<el-form-item |
|
label="菜单图标" |
|
v-if="formData.menuType === MenuEnum.CATALOGUE" |
|
:rules="{ |
|
required: true, |
|
message: '请选择图标', |
|
}" |
|
prop="icon" |
|
> |
|
<icon-picker class="flex-1" v-model="formData.icon" /> |
|
</el-form-item> |
|
<el-form-item |
|
label="菜单路径" |
|
v-if="formData.menuType === MenuEnum.MENU" |
|
:rules="{ |
|
required: true, |
|
message: '请输入菜单路径', |
|
trigger: ['blur'], |
|
}" |
|
> |
|
<el-input |
|
v-model="formData.paths" |
|
placeholder="请输入路由路径" |
|
clearable |
|
/> |
|
</el-form-item> |
|
<el-form-item |
|
v-if="formData.menuType == MenuEnum.MENU" |
|
label="组件路径" |
|
prop="component" |
|
> |
|
<div class="flex-1"> |
|
<el-autocomplete |
|
class="w-full" |
|
v-model="formData.component" |
|
:fetch-suggestions="querySearch" |
|
clearable |
|
placeholder="请输入组件路径" |
|
/> |
|
<!-- <el-input v-model="formData.component" placeholder="请输入组件路径" /> --> |
|
<div class="form-tips"> |
|
访问的组件路径,如:`permission/admin/index`,默认在`views`目录下 |
|
</div> |
|
</div> |
|
</el-form-item> |
|
<el-form-item |
|
v-if="formData.menuType != MenuEnum.CATALOGUE" |
|
label="权限字符" |
|
prop="perms" |
|
> |
|
<div class="flex-1"> |
|
<el-input |
|
v-model="formData.perms" |
|
placeholder="请输入权限字符" |
|
clearable |
|
/> |
|
<div class="form-tips"> |
|
将作为server端API验权使用,如`system:admin:list`,请谨慎修改 |
|
</div> |
|
</div> |
|
</el-form-item> |
|
<!-- <el-form-item |
|
v-if="formData.menuType == MenuEnum.MENU" |
|
label="路由参数" |
|
prop="params" |
|
> |
|
<div> |
|
<div class="flex-1"> |
|
<el-input |
|
v-model="formData.params" |
|
placeholder="请输入路由参数" |
|
clearable |
|
/> |
|
</div> |
|
<div class="form-tips"> |
|
访问路由的默认传递参数,如:`{"id": 1, "name": |
|
"admin"}`或`id=1&name=admin` |
|
</div> |
|
</div> |
|
</el-form-item> --> |
|
<el-form-item |
|
v-if="formData.menuType === MenuEnum.MENU" |
|
label="打开新页面" |
|
prop="openNewPage" |
|
:rules="{ |
|
required: true, |
|
message: '请选择是否打开新页面', |
|
trigger: ['blur'], |
|
}" |
|
> |
|
<el-radio-group v-model="formData.openNewPage"> |
|
<el-radio :value="false">否</el-radio> |
|
<el-radio :value="true">是</el-radio> |
|
</el-radio-group> |
|
</el-form-item> |
|
<el-form-item |
|
v-if="formData.menuType == MenuEnum.MENU" |
|
label="是否缓存" |
|
prop="isCache" |
|
required |
|
> |
|
<div> |
|
<el-radio-group v-model="formData.isCache"> |
|
<el-radio :label="1">缓存</el-radio> |
|
<el-radio :label="0">不缓存</el-radio> |
|
</el-radio-group> |
|
<div class="form-tips">选择缓存则会被`keep-alive`缓存</div> |
|
</div> |
|
</el-form-item> |
|
|
|
<el-form-item |
|
v-if="formData.menuType != MenuEnum.BUTTON" |
|
label="是否显示" |
|
prop="isShow" |
|
:rules="{ |
|
required: true, |
|
message: '请选择是否显示', |
|
trigger: ['blur'], |
|
}" |
|
> |
|
<div> |
|
<el-radio-group v-model="formData.isShow"> |
|
<el-radio :label="1">显示</el-radio> |
|
<el-radio :label="0">隐藏</el-radio> |
|
</el-radio-group> |
|
<div class="form-tips"> |
|
选择隐藏则路由将不会出现在侧边栏,但仍然可以访问 |
|
</div> |
|
</div> |
|
</el-form-item> |
|
<el-form-item label="菜单排序" prop="menuSort"> |
|
<div> |
|
<el-input-number v-model="formData.menuSort" :max="9999" /> |
|
<div class="form-tips">数值越小越排前</div> |
|
</div> |
|
</el-form-item> |
|
</el-form> |
|
<footer class="flex end"> |
|
<el-button @click="show = false">取消</el-button> |
|
<el-button type="primary" @click="submit" :disabled="disabled" |
|
>确定</el-button |
|
> |
|
</footer> |
|
</el-dialog> |
|
</template> |
|
<script lang="ts" setup> |
|
import { MenuEnum, MENU_ROOT_ID } from "@/enums/appEnums"; |
|
import { listMenu, addMenu, updateMenu, delMenu } from "@/api/system/menu"; |
|
import feedback from "@/utils/feedback"; |
|
import { watch } from "vue"; |
|
|
|
const menus = ref([]); |
|
const menuOptions = ref([ |
|
{ |
|
id: MENU_ROOT_ID, |
|
menuName: "顶级", |
|
children: [], |
|
}, |
|
]); |
|
function getList() { |
|
listMenu().then((data) => { |
|
menus.value = data; |
|
if (!menuOptions.value[0].children.length) { |
|
menuOptions.value[0].children = data; |
|
} |
|
}); |
|
} |
|
getList(); |
|
|
|
const show = ref(false); |
|
const formData = ref({ |
|
openNewPage: false, |
|
isShow: 1, |
|
isCache: 1, |
|
}); |
|
const formRef = ref(); |
|
|
|
const querySearch = (queryString, cb) => { |
|
const results = queryString |
|
? menus.value.filter((item) => |
|
item.menuName.toLowerCase().includes(queryString.toLowerCase()) |
|
) |
|
: []; |
|
cb(results.map((item) => ({ value: item }))); |
|
}; |
|
|
|
const mode = ref<string>("add"); |
|
watch(mode, (val) => { |
|
if (val === "add") { |
|
initFormData(); |
|
} |
|
}); |
|
|
|
function initFormData() { |
|
formData.value = { |
|
openNewPage: false, |
|
isShow: 1, |
|
isCache: 1, |
|
}; |
|
} |
|
|
|
function handleAdd() { |
|
show.value = true; |
|
mode.value = "add"; |
|
} |
|
|
|
const disabled = ref(false); |
|
async function submit() { |
|
await formRef.value.validate(); |
|
disabled.value = true; |
|
try { |
|
if (mode.value === "add") { |
|
await addMenu(formData.value); |
|
initFormData(); |
|
} else { |
|
await updateMenu(formData.value); |
|
} |
|
} catch(e) { |
|
console.error(e) |
|
disabled.value = false; |
|
return |
|
} |
|
disabled.value = false; |
|
show.value = false; |
|
getList(); |
|
feedback.msgSuccess('操作成功') |
|
} |
|
|
|
function handleEdit(row) { |
|
show.value = true; |
|
mode.value = "edit"; |
|
formData.value = { ...row }; |
|
} |
|
|
|
const handleDelete = async (row) => { |
|
await feedback.confirm(`确定要删除 ${row.menuName}?`); |
|
await delMenu(row.id); |
|
feedback.msgSuccess("删除成功"); |
|
getList(); |
|
}; |
|
</script> |
|
<style lang="scss" scoped> |
|
.form-tips { |
|
font-size: 12px; |
|
line-height: 1.2; |
|
color: #666; |
|
} |
|
</style> |