mirror of
https://gitee.com/niucloud-team/niucloud.git
synced 2026-02-02 07:28:10 +00:00
284 lines
10 KiB
Vue
284 lines
10 KiB
Vue
<template>
|
||
<el-dialog v-model="showDialog" :title="popTitle" width="500px" :destroy-on-close="true">
|
||
<el-form :model="formData" label-width="90px" class="page-form" ref="formRef" :rules="formRules" v-loading="loading">
|
||
<el-form-item :label="t('menuName')" prop="menu_name">
|
||
<el-input v-model.trim="formData.menu_name" maxlength="10" show-word-limit :placeholder="t('menuNamePlaceholder')" class="input-width" />
|
||
</el-form-item>
|
||
<el-form-item :label="t('menuKey')" prop="menu_key" v-if="!formData.id">
|
||
<el-input v-model.trim="formData.menu_key" maxlength="50" show-word-limit :placeholder="t('menuKeyPlaceholder')" class="input-width" />
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('menuType')">
|
||
<el-radio-group v-model="formData.menu_type">
|
||
<el-radio :label="0">{{ t('menuTypeDir') }}</el-radio>
|
||
<el-radio :label="1">{{ t('menuTypeMenu') }}</el-radio>
|
||
<el-radio :label="2">{{ t('menuTypeButton') }}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('addon')" prop="addon" v-show="formData.app_type == 'site'">
|
||
<el-select v-model="formData.addon" :placeholder="t('addon')" class="input-width" @change="addonChange">
|
||
<el-option v-for="(item, index) in addonList" :label="item.title" :value="item.key" :key="index" />
|
||
</el-select>
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('parentMenu')" prop="parent_key">
|
||
<el-tree-select class="input-width" v-if="formData.addon != ''" v-model="formData.parent_key"
|
||
:props="{ label: 'menu_name', value: 'menu_key' }" :data="addonMenuListWithDisabled" check-strictly
|
||
:render-after-expand="false" />
|
||
<el-tree-select class="input-width" v-else v-model="formData.parent_key"
|
||
:props="{ label: 'menu_name', value: 'menu_key' }" :data="sysMenuListWithDisabled" check-strictly
|
||
:render-after-expand="false" />
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('routePath')" prop="router_path" v-show="formData.menu_type == 1">
|
||
<el-input v-model.trim="formData.router_path" :placeholder="t('routePathPlaceholder')" class="input-width" />
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('viewPath')" prop="view_path" v-show="formData.menu_type == 1">
|
||
<el-input v-model.trim="formData.view_path" :placeholder="t('viewPathPlaceholder')" class="input-width" />
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('authId')" prop="api_url" v-show="formData.menu_type != 0">
|
||
<el-input v-model.trim="formData.api_url" :placeholder="t('authIdPlaceholder')" class="input-width">
|
||
<template #append>
|
||
<el-select class="border-none" style="width: 100px" v-model="formData.methods">
|
||
<el-option label="POST" value="post" />
|
||
<el-option label="GET" value="get" />
|
||
<el-option label="PUT" value="put" />
|
||
<el-option label="DELETE" value="delete" />
|
||
</el-select>
|
||
</template>
|
||
</el-input>
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('menuIcon')" prop="icon" v-show="formData.menu_type != 2">
|
||
<div class="input-width">
|
||
<select-icon v-model="formData.icon" />
|
||
</div>
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('status')" v-show="formData.menu_type != 2">
|
||
<el-radio-group v-model="formData.status">
|
||
<el-radio :label="1">{{ t('statusNormal') }}</el-radio>
|
||
<el-radio :label="0">{{ t('statusDeactivate') }}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('isShow')" v-show="formData.menu_type != 2">
|
||
<el-radio-group v-model="formData.is_show">
|
||
<el-radio :label="1">{{ t('show') }}</el-radio>
|
||
<el-radio :label="0">{{ t('hidden') }}</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('menuShortName')">
|
||
<el-input v-model.trim="formData.menu_short_name" :placeholder="t('menuShortNamePlaceholder')" class="input-width" />
|
||
</el-form-item>
|
||
|
||
<el-form-item :label="t('sort')">
|
||
<el-input-number v-model="formData.sort" :min="0"/>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<template #footer>
|
||
<span class="dialog-footer">
|
||
<el-button @click="showDialog = false">{{ t('cancel') }}</el-button>
|
||
<el-button type="primary" :loading="loading" @click="confirm(formRef)">{{ t('confirm') }}</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, reactive, computed } from 'vue'
|
||
import { t } from '@/lang'
|
||
import type { FormInstance } from 'element-plus'
|
||
import { addMenu, editMenu, getMenuInfo, getSystemMenu, getAddonMenu } from '@/app/api/sys'
|
||
import { getAddonDevelop } from '@/app/api/tools'
|
||
|
||
const showDialog = ref(false)
|
||
const loading = ref(false)
|
||
let popTitle: string = ''
|
||
|
||
/**
|
||
* 表单数据
|
||
*/
|
||
const initialFormData = {
|
||
id: 0,
|
||
menu_name: '',
|
||
menu_type: 0,
|
||
parent_key: '',
|
||
icon: '',
|
||
api_url: '',
|
||
router_path: '',
|
||
view_path: '',
|
||
methods: 'post',
|
||
sort: '',
|
||
status: 1,
|
||
is_show: 1,
|
||
menu_key: '',
|
||
app_type: '',
|
||
addon: '',
|
||
menu_short_name: ''
|
||
}
|
||
const formData: Record<string, any> = reactive({ ...initialFormData })
|
||
|
||
const addonList = ref<Array<any>>([])
|
||
const sysMenuList = ref<Array<any>>([])
|
||
const addonMenuList = ref<Array<any>>([])
|
||
const formRef = ref<FormInstance>()
|
||
|
||
const validataMenuKey = (val: string) => {
|
||
return /^([a-zA-Z_$])([a-zA-Z0-9_$])*$/.test(val)
|
||
}
|
||
|
||
// 表单验证规则
|
||
const formRules = computed(() => {
|
||
return {
|
||
menu_name: [
|
||
{ required: true, message: t('menuNamePlaceholder'), trigger: 'blur' }
|
||
],
|
||
menu_key: [
|
||
{ required: true, message: t('menuKeyPlaceholder'), trigger: 'blur' },
|
||
{
|
||
validator: (rule: any, value: string, callback: any) => {
|
||
if (!validataMenuKey(value)) {
|
||
callback(new Error(t('menuKeyValidata')))
|
||
}
|
||
|
||
callback()
|
||
},
|
||
trigger: 'blur'
|
||
}
|
||
],
|
||
router_path: [
|
||
{ required: formData.menu_type == 1, message: t('routePathPlaceholder'), trigger: 'blur' }
|
||
],
|
||
view_path: [
|
||
{ required: formData.menu_type == 1, message: t('viewPathPlaceholder'), trigger: 'blur' }
|
||
],
|
||
api_url: [
|
||
{ required: formData.menu_type == 2, message: t('authIdPlaceholder'), trigger: 'blur' }
|
||
]
|
||
}
|
||
})
|
||
|
||
// 获取插件列表
|
||
const getAddonDevelopFn = async () => {
|
||
const { data } = await getAddonDevelop({})
|
||
addonList.value = [{ title: '系统', key: '' }]
|
||
addonList.value.push(...data)
|
||
}
|
||
|
||
// 获取系统菜单列表
|
||
const getSystemMenuFn = async () => {
|
||
const { data } = await getSystemMenu()
|
||
sysMenuList.value = [{ menu_name: '顶级', menu_key: '' }]
|
||
sysMenuList.value.push(...data)
|
||
}
|
||
|
||
// 获取系统应用列表
|
||
const getAddonMenuFn = async (key: any) => {
|
||
const { data } = await getAddonMenu(key)
|
||
addonMenuList.value = data
|
||
}
|
||
|
||
const markMenuDisabled = (menuList: any[], menuType: number): any[] => {
|
||
return menuList.map(item => {
|
||
const newItem = { ...item }
|
||
// 判断当前节点是否可选
|
||
if (item.menu_key === '') {
|
||
// “顶级”节点(仅存在于 sysMenuList)
|
||
newItem.disabled = menuType === 2 // 按钮不能选顶级
|
||
} else {
|
||
if (menuType === 2) {
|
||
// 当前要创建的是按钮 → 只允许选择 menu_type === 1(菜单)
|
||
newItem.disabled = item.menu_type !== 1
|
||
} else {
|
||
// 当前要创建的是目录/菜单 → 只允许选择 menu_type === 0(目录)
|
||
newItem.disabled = item.menu_type !== 0
|
||
}
|
||
}
|
||
// 递归处理 children
|
||
if (Array.isArray(item.children) && item.children.length > 0) {
|
||
newItem.children = markMenuDisabled(item.children, menuType)
|
||
}
|
||
return newItem
|
||
})
|
||
}
|
||
|
||
const sysMenuListWithDisabled = computed(() => {
|
||
return markMenuDisabled(sysMenuList.value, formData.menu_type)
|
||
})
|
||
|
||
const addonMenuListWithDisabled = computed(() => {
|
||
return markMenuDisabled(addonMenuList.value, formData.menu_type)
|
||
})
|
||
|
||
// 选择应用
|
||
const addonChange = async (val: any) => {
|
||
formData.parent_key = ''
|
||
if (val != '') {
|
||
await getAddonMenuFn(val)
|
||
formData.parent_key = addonMenuList.value[0].menu_key
|
||
}
|
||
}
|
||
|
||
const emit = defineEmits(['complete'])
|
||
|
||
/**
|
||
* 确认
|
||
* @param formEl
|
||
*/
|
||
const confirm = async (formEl: FormInstance | undefined) => {
|
||
if (loading.value || !formEl) return
|
||
const save = formData.id ? editMenu : addMenu
|
||
|
||
await formEl.validate(async (valid, fields) => {
|
||
if (valid) {
|
||
loading.value = true
|
||
|
||
const data = formData
|
||
|
||
save(data).then(res => {
|
||
loading.value = false
|
||
showDialog.value = false
|
||
emit('complete')
|
||
}).catch(() => {
|
||
loading.value = false
|
||
})
|
||
}
|
||
})
|
||
}
|
||
|
||
const setFormData = async (row: any = null) => {
|
||
loading.value = true
|
||
Object.assign(formData, initialFormData)
|
||
popTitle = t('addMenu')
|
||
getAddonDevelopFn()
|
||
getSystemMenuFn()
|
||
if (row.menu_key) {
|
||
popTitle = t('updateMenu')
|
||
const data = await (await getMenuInfo(row.app_type, row.menu_key)).data
|
||
Object.keys(formData).forEach((key: string) => {
|
||
if (data[key] != undefined) formData[key] = data[key]
|
||
})
|
||
if (formData.addon != '') getAddonMenuFn(formData.addon)
|
||
} else {
|
||
Object.keys(formData).forEach((key: string) => {
|
||
if (row[key] != undefined) formData[key] = row[key]
|
||
})
|
||
}
|
||
|
||
loading.value = false
|
||
}
|
||
|
||
defineExpose({
|
||
showDialog,
|
||
setFormData
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss" scoped></style>
|