|
@@ -1,7 +1,6 @@
|
|
|
<script setup lang="ts">
|
|
|
import {ref} from "vue";
|
|
|
-import {isDev} from "@/utils/public";
|
|
|
-import {IntergrationMenu} from "@/api/settings/menu-settings";
|
|
|
+import {IntergrationMenu, updateMenuSettings} from "@/api/settings/menu-settings";
|
|
|
import {
|
|
|
ElButton,
|
|
|
ElCascader,
|
|
@@ -13,22 +12,27 @@ import {
|
|
|
ElInput,
|
|
|
ElPopover,
|
|
|
ElInputNumber,
|
|
|
- ElSwitch
|
|
|
+ ElSwitch,
|
|
|
+ FormRules,
|
|
|
+ ElSelectV2
|
|
|
} from "element-plus";
|
|
|
import XEUtils from "xe-utils";
|
|
|
import icon from '@/icons/iconfont.css'
|
|
|
import {eachAndReturnList} from "@/utils/cyRefList";
|
|
|
+import {useCompRef} from "@/utils/useCompRef";
|
|
|
+import {QuestionFilled} from "@element-plus/icons-vue";
|
|
|
|
|
|
const props = defineProps<{
|
|
|
- menuTreeData: any[],
|
|
|
+ cascaderData: any[],
|
|
|
+}>()
|
|
|
+
|
|
|
+const emits = defineEmits<{
|
|
|
+ (e: "saveCallback"): void,
|
|
|
}>()
|
|
|
|
|
|
-declare type VueIntergrationPlatformMenu = {
|
|
|
- parentCascader: number[]
|
|
|
-} & IntergrationMenu
|
|
|
|
|
|
-const showDialog = ref(isDev)
|
|
|
-const currentData = ref<VueIntergrationPlatformMenu>({
|
|
|
+const showDialog = ref(false)
|
|
|
+const currentData = ref<IntergrationMenu>({
|
|
|
children: [],
|
|
|
component: "",
|
|
|
icon: "",
|
|
@@ -37,7 +41,7 @@ const currentData = ref<VueIntergrationPlatformMenu>({
|
|
|
metaPassRule: false,
|
|
|
metaTitle: "",
|
|
|
name: "",
|
|
|
- parentCascader: [],
|
|
|
+ cascaders: [],
|
|
|
parentId: 0,
|
|
|
path: "",
|
|
|
pathParams: "",
|
|
@@ -46,27 +50,84 @@ const currentData = ref<VueIntergrationPlatformMenu>({
|
|
|
type: 0
|
|
|
})
|
|
|
|
|
|
-function save() {
|
|
|
- if (currentData.value.parentCascader.length > 0) {
|
|
|
- currentData.value.parentId = currentData.value.parentCascader[currentData.value.parentCascader.length - 1]
|
|
|
+const fromRef = useCompRef(ElForm)
|
|
|
+
|
|
|
+
|
|
|
+const rules: FormRules<IntergrationMenu> = reactive({})
|
|
|
+type D = keyof IntergrationMenu
|
|
|
+
|
|
|
+function addRequired(key: D[]) {
|
|
|
+ key.forEach(item => {
|
|
|
+ rules[item] = [{required: true, trigger: 'change', message: '必填'}]
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+addRequired(['path', 'component', 'name', 'metaTitle'])
|
|
|
+
|
|
|
+async function save() {
|
|
|
+ await fromRef.value?.validate()
|
|
|
+ if (currentData.value.cascaders?.length > 0) {
|
|
|
+ currentData.value.parentId = currentData.value!.cascaders?.[currentData.value.cascaders!.length - 1]
|
|
|
}
|
|
|
+ await updateMenuSettings(currentData.value)
|
|
|
+ showDialog.value = false
|
|
|
+ emits('saveCallback')
|
|
|
}
|
|
|
|
|
|
+const icons = ref([])
|
|
|
+const routerKey = ref([])
|
|
|
+
|
|
|
+const layout = [
|
|
|
+ {
|
|
|
+ value: 'Layout',
|
|
|
+ label: 'Layout'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ value: 'EmptyRouter',
|
|
|
+ label: 'EmptyRouter'
|
|
|
+ }
|
|
|
+]
|
|
|
+
|
|
|
+function dialogOpen() {
|
|
|
+ const regex = /.(?<icon>.*):before/g;
|
|
|
+ const matches = (icon as string).matchAll(regex);
|
|
|
+ icons.value = eachAndReturnList((matches as string[]), (item) => {
|
|
|
+ return item[1]
|
|
|
+ })
|
|
|
+
|
|
|
+ const view = import.meta.glob('/src/views/**/*.{vue,tsx}');
|
|
|
+
|
|
|
+ routerKey.value = eachAndReturnList(Object.keys(view), (item) => {
|
|
|
+ const tmp = item.replace("/src", "..")
|
|
|
+ return {
|
|
|
+ value: tmp,
|
|
|
+ label: tmp,
|
|
|
+ }
|
|
|
+ })
|
|
|
+ routerKey.value.push(...layout)
|
|
|
+}
|
|
|
+
|
|
|
+function handlePath() {
|
|
|
+ const isStartsWith = currentData.value.path.startsWith("/");
|
|
|
+ if (currentData.value.cascaders === null || currentData.value.cascaders!.length === 0) {
|
|
|
+ if (!isStartsWith) {
|
|
|
+ currentData.value.path = "/" + currentData.value.path
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ if (isStartsWith) {
|
|
|
+ currentData.value.path = currentData.value.path.substring(1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-const regex = /.(?<icon>.*):before/g;
|
|
|
-const matches = (icon as string).matchAll(regex);
|
|
|
-const icons = ref(eachAndReturnList((matches as string[]), (item) => {
|
|
|
- return item[1]
|
|
|
-}))
|
|
|
|
|
|
function iconClick(icon) {
|
|
|
currentData.value.icon = `iconfont ${icon}`
|
|
|
}
|
|
|
|
|
|
defineExpose({
|
|
|
- openDialog: (row: VueIntergrationPlatformMenu) => {
|
|
|
+ openDialog: (row: IntergrationMenu) => {
|
|
|
currentData.value = XEUtils.clone(row, true)
|
|
|
- currentData.value.parentCascader = []
|
|
|
showDialog.value = true;
|
|
|
}
|
|
|
})
|
|
@@ -75,59 +136,98 @@ defineExpose({
|
|
|
|
|
|
<template>
|
|
|
<el-dialog v-model="showDialog"
|
|
|
+ @open="dialogOpen"
|
|
|
title="菜单编辑"
|
|
|
destroy-on-close
|
|
|
draggable>
|
|
|
- <el-form label-width="80px" size="default">
|
|
|
+ <el-form
|
|
|
+ ref="fromRef"
|
|
|
+ :model="currentData"
|
|
|
+ :rules="rules"
|
|
|
+ label-width="80px"
|
|
|
+ size="default">
|
|
|
<el-row :gutter="10">
|
|
|
+
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="类型" prop="type">
|
|
|
+ <el-switch v-model="currentData.type"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="2"
|
|
|
+ active-text="目录"
|
|
|
+ inactive-text="页面"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
<el-col :span="24">
|
|
|
- <el-form-item label="父节点">
|
|
|
+ <el-form-item label="父节点" prop="cascaders">
|
|
|
<el-cascader
|
|
|
- v-model="currentData.parentCascader"
|
|
|
+ @change="handlePath"
|
|
|
+ v-model="currentData.cascaders"
|
|
|
style="width: 100%"
|
|
|
:props="{ checkStrictly: true, value: 'id', label: 'metaTitle' }"
|
|
|
- :options="menuTreeData"
|
|
|
+ :options="cascaderData"
|
|
|
clearable/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
-
|
|
|
<el-col :span="24">
|
|
|
- <el-form-item label="完整路径">
|
|
|
- <el-input v-model="currentData.path"/>
|
|
|
+ <el-form-item prop="path">
|
|
|
+ <template #label>
|
|
|
+ 路径
|
|
|
+ <el-tooltip content="如果是在根目录则不需要已 / 开头" placement="top">
|
|
|
+ <el-icon>
|
|
|
+ <QuestionFilled/>
|
|
|
+ </el-icon>
|
|
|
+ </el-tooltip>
|
|
|
+ </template>
|
|
|
+ <el-input v-model="currentData.path"
|
|
|
+ @blur="handlePath"
|
|
|
+ />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="24">
|
|
|
- <el-form-item label="组件路径">
|
|
|
- <el-input v-model="currentData.component"/>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
+ <el-form-item label="组件路径" prop="component">
|
|
|
+ <el-select-v2
|
|
|
+ v-model="currentData.component"
|
|
|
+ :options="routerKey"
|
|
|
+ style="width: 100%"
|
|
|
+ filterable
|
|
|
+ />
|
|
|
|
|
|
- <el-col :span="24">
|
|
|
- <el-form-item label="路径参数">
|
|
|
- <el-input v-model="currentData.pathParams"/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="类型">
|
|
|
- <el-switch v-model="currentData.type"
|
|
|
- :active-value="1"
|
|
|
- :inactive-value="2"
|
|
|
- active-text="目录"
|
|
|
- inactive-text="页面"
|
|
|
- />
|
|
|
+ <el-form-item prop="name">
|
|
|
+ <template #label>
|
|
|
+ 名称
|
|
|
+ <el-tooltip content="这个名称必须是唯一" placement="top">
|
|
|
+ <el-icon>
|
|
|
+ <QuestionFilled/>
|
|
|
+ </el-icon>
|
|
|
+ </el-tooltip>
|
|
|
+ </template>
|
|
|
+ <el-input v-model="currentData.name"
|
|
|
+ clearable maxlength="50"/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="路由名称">
|
|
|
- <el-input v-model="currentData.name"
|
|
|
+ <el-form-item label="标题" prop="metaTitle">
|
|
|
+ <el-input v-model="currentData.metaTitle"
|
|
|
clearable maxlength="50"/>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
+ <el-col :span="24">
|
|
|
+ <el-form-item label="路径参数" prop="pathParams">
|
|
|
+ <el-input v-model="currentData.pathParams"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="菜单图标">
|
|
|
<el-popover :width="0" trigger="click">
|
|
@@ -154,12 +254,6 @@ defineExpose({
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="菜单名称">
|
|
|
- <el-input v-model="currentData.metaTitle"
|
|
|
- clearable maxlength="50"/>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="重定向">
|
|
@@ -196,6 +290,38 @@ defineExpose({
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="卡片类型">
|
|
|
+ <el-switch v-model="currentData.mainCard"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="是"
|
|
|
+ inactive-text="否"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="超出滚动">
|
|
|
+ <el-switch v-model="currentData.mainOverflowAuto"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="是"
|
|
|
+ inactive-text="否"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
+
|
|
|
+ <el-col :span="12">
|
|
|
+ <el-form-item label="无需登录">
|
|
|
+ <el-switch v-model="currentData.metaNeedToken"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ active-text="是"
|
|
|
+ inactive-text="否"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-col>
|
|
|
|
|
|
</el-row>
|
|
|
</el-form>
|