2026-01-31 11:23:56 +08:00

284 lines
10 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>
<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>