Files
x_admin/admin/src/views/system/menu/edit.vue
2025-12-22 12:02:47 +08:00

368 lines
13 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="edit-popup">
<popup
ref="popupRef"
:title="popupTitle"
:async="true"
width="550px"
@confirm="handleSubmit"
@close="handleClose"
>
<el-form ref="formRef" :model="formData" label-width="80px" :rules="formRules">
<el-form-item label="菜单类型" prop="menuType" required>
<el-radio-group v-model="formData.menuType">
<el-radio :value="MenuEnum.CATALOGUE">目录</el-radio>
<el-radio :value="MenuEnum.MENU">菜单</el-radio>
<el-radio :value="MenuEnum.BUTTON">按钮</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="父级菜单" prop="pid">
<el-tree-select
class="flex-1"
v-model="formData.pid"
:data="menuOptions"
clearable
node-key="id"
:props="{
label: 'menuName'
}"
:default-expand-all="true"
placeholder="请选择父级菜单"
check-strictly
:empty-values="[undefined, null]"
/>
</el-form-item>
<el-form-item label="菜单名称" prop="menuName">
<el-input v-model="formData.menuName" placeholder="请输入菜单名称" clearable />
</el-form-item>
<el-form-item
v-if="formData.menuType != MenuEnum.BUTTON"
label="菜单图标"
prop="menuIcon"
>
<icon-picker class="flex-1" v-model="formData.menuIcon" />
</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.BUTTON"
label="路由路径"
prop="paths"
>
<div class="flex-1">
<el-input v-model="formData.paths" placeholder="请输入路由路径" clearable />
<div class="form-tips">
访问的路由地址`admin`如外网地址需内链访问则以`http(s)://`开头
</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
label="选中菜单"
prop="selected"
v-if="formData.menuType == MenuEnum.MENU"
>
<div class="flex-1">
<el-input
v-model="formData.selected"
placeholder="请输入路由路径"
clearable
/>
<div class="form-tips">
访问详情页面编辑页面时菜单高亮显示`/consumer/lists`
</div>
</div>
</el-form-item>
<el-form-item
v-if="formData.menuType != MenuEnum.CATALOGUE"
label="接口权限"
prop="perms"
>
<div class="flex-1">
<!-- {{ formData.permsArr }} -->
<!-- <el-input v-model="formData.perms" placeholder="请输入接口权限" clearable /> -->
<el-select
v-model="formData.perms"
clearable
filterable
placeholder="请选择接口权限"
:style="{ width: '100%' }"
>
<el-option
v-for="(item, index) in permissionOptions"
:key="index"
:label="item.label"
:value="item.value"
></el-option>
</el-select>
<!-- <div class="form-tips">
请求路径`api/admin/system/admin/list`权限为`admin:system:admin:list`
</div> -->
</div>
</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 :value="1">缓存</el-radio>
<el-radio :value="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"
required
>
<div>
<el-radio-group v-model="formData.isShow">
<el-radio :value="1">显示</el-radio>
<el-radio :value="0">隐藏</el-radio>
</el-radio-group>
<div class="form-tips">
选择隐藏则路由将不会出现在侧边栏但仍然可以访问
</div>
</div>
</el-form-item>
<el-form-item
v-if="formData.menuType != MenuEnum.BUTTON"
label="菜单状态"
prop="isDisable"
required
>
<div>
<el-radio-group v-model="formData.isDisable">
<el-radio :value="0">正常</el-radio>
<el-radio :value="1">停用</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>
</popup>
</div>
</template>
<script lang="ts" setup>
import { ref, computed, shallowRef, reactive } from 'vue'
import type { FormInstance } from 'element-plus'
import { menuLists, menuEdit, menuAdd, menuDetail } from '@/api/perms/menu'
import { getApiList } from '@/api/setting/website'
import { getModulesKey } from '@/router'
import { MenuEnum } from '@/enums/appEnums'
import Popup from '@/components/popup/index.vue'
import feedback from '@/utils/feedback'
import { arrayToTree } from '@/utils/util'
const emit = defineEmits(['success', 'close'])
const formRef = shallowRef<FormInstance>()
const popupRef = shallowRef<InstanceType<typeof Popup>>()
const mode = ref('add')
const popupTitle = computed(() => {
return mode.value == 'edit' ? '编辑菜单' : '新增菜单'
})
const permissionOptions = ref([])
const componentsOptions = ref(getModulesKey())
const querySearch = (queryString: string, cb: any) => {
const results = queryString
? componentsOptions.value.filter((item) =>
item.toLowerCase().includes(queryString.toLowerCase())
)
: componentsOptions.value
cb(results.map((item) => ({ value: item })))
}
const formData = reactive({
id: '',
//父级id
pid: '0',
//类型
menuType: MenuEnum.CATALOGUE,
//图标
menuIcon: '',
//名称
menuName: '',
//排序号
menuSort: 0,
// 路由路径
paths: '',
//权限链接
perms: '',
// permsArr: [],
//前端组件
component: '',
//选中路径
selected: '',
//路由参数
params: '',
//是否缓存 0=否, 1=是
isCache: 1,
//是否显示 0=否, 1=是
isShow: 1,
//是否禁用 0=否, 1=是
isDisable: 0
})
const formRules = {
pid: [
{
required: true,
message: '请选择父级菜单',
trigger: ['blur', 'change']
}
],
menuName: [
{
required: true,
message: '请输入菜单名称',
trigger: 'blur'
}
],
paths: [
{
required: true,
message: '请输入路由地址',
trigger: 'blur'
}
]
// component: [
// {
// required: true,
// message: '请输入组件地址',
// trigger: 'blur'
// }
// ]
}
const menuOptions = ref<any[]>([])
const getMenu = async () => {
const data: any = await menuLists()
const menu: any = { id: '0', menuName: '顶级', children: [] }
menu.children = arrayToTree(
data.filter((item) => item.menuType != MenuEnum.BUTTON),
'0'
)
menuOptions.value.push(menu)
}
function getApiListFn() {
getApiList().then((res: any) => {
const arr = []
res.forEach((item: string) => {
if (item.indexOf('/api/admin/') == 0) {
const per = item.replace(/\/api\//, '').replace(/\//g, ':')
arr.push({
value: per,
label: item
})
}
})
// console.log(pathsToTree(arr))
permissionOptions.value = arr.sort()
})
}
const handleSubmit = async () => {
await formRef.value?.validate()
const data = { ...formData }
// if (data.permsArr) {
// data.perms = data.permsArr.join(',')
// } else {
// data.perms = ''
// }
mode.value == 'edit' ? await menuEdit(data) : await menuAdd(data)
popupRef.value?.close()
feedback.msgSuccess('操作成功')
emit('success')
}
const open = (type = 'add') => {
mode.value = type
popupRef.value?.open()
}
const setFormData = (data: Record<any, any>) => {
for (const key in formData) {
if (data[key] != null && data[key] != undefined) {
//@ts-ignore
// if (key == 'perms') {
// formData['permsArr'] = data[key].split(',')
// } else {
// formData[key] = data[key]
// }
formData[key] = data[key]
}
}
}
const getDetail = async (row: Record<string, any>) => {
const data = await menuDetail({
id: row.id
})
setFormData(data)
}
const handleClose = () => {
emit('close')
}
getMenu()
getApiListFn()
defineExpose({
open,
setFormData,
getDetail
})
</script>