Commit 1c1cafdc7f58e4f7e0e944c407e8d74c7ca82658

Authored by xingyu4j
1 parent eb5870f3

feat: vxe tree table

yudao-ui-admin-vue3/src/views/system/menu/index.vue
@@ -3,12 +3,27 @@ import * as MenuApi from '@/api/system/menu' @@ -3,12 +3,27 @@ import * as MenuApi from '@/api/system/menu'
3 import { MenuVO } from '@/api/system/menu/types' 3 import { MenuVO } from '@/api/system/menu/types'
4 import { useI18n } from '@/hooks/web/useI18n' 4 import { useI18n } from '@/hooks/web/useI18n'
5 import { useMessage } from '@/hooks/web/useMessage' 5 import { useMessage } from '@/hooks/web/useMessage'
  6 +import { IconSelect } from '@/components/Icon'
  7 +import { Tooltip } from '@/components/Tooltip'
6 import { required } from '@/utils/formRules.js' 8 import { required } from '@/utils/formRules.js'
7 import { onMounted, reactive, ref } from 'vue' 9 import { onMounted, reactive, ref } from 'vue'
8 import { VxeTableInstance } from 'vxe-table' 10 import { VxeTableInstance } from 'vxe-table'
9 -import { DICT_TYPE } from '@/utils/dict' 11 +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
10 import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants' 12 import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'
11 - 13 +import {
  14 + ElRow,
  15 + ElCol,
  16 + ElForm,
  17 + ElFormItem,
  18 + ElInput,
  19 + ElInputNumber,
  20 + ElSelect,
  21 + ElTreeSelect,
  22 + ElOption,
  23 + ElRadioGroup,
  24 + ElRadioButton
  25 +} from 'element-plus'
  26 +import { handleTree } from '@/utils/tree'
12 const { t } = useI18n() // 国际化 27 const { t } = useI18n() // 国际化
13 const message = useMessage() 28 const message = useMessage()
14 const xTable = ref<VxeTableInstance>() 29 const xTable = ref<VxeTableInstance>()
@@ -18,6 +33,7 @@ const actionLoading = ref(false) // 遮罩层 @@ -18,6 +33,7 @@ const actionLoading = ref(false) // 遮罩层
18 const actionType = ref('') // 操作按钮的类型 33 const actionType = ref('') // 操作按钮的类型
19 const dialogVisible = ref(false) // 是否显示弹出层 34 const dialogVisible = ref(false) // 是否显示弹出层
20 const dialogTitle = ref('edit') // 弹出层标题 35 const dialogTitle = ref('edit') // 弹出层标题
  36 +const statusOption = ref() // 状态选项
21 const menuForm = ref<MenuVO>({ 37 const menuForm = ref<MenuVO>({
22 id: 0, 38 id: 0,
23 name: '', 39 name: '',
@@ -33,12 +49,32 @@ const menuForm = ref&lt;MenuVO&gt;({ @@ -33,12 +49,32 @@ const menuForm = ref&lt;MenuVO&gt;({
33 keepAlive: true, 49 keepAlive: true,
34 createTime: '' 50 createTime: ''
35 }) 51 })
  52 +const menuProps = {
  53 + checkStrictly: true,
  54 + children: 'children',
  55 + label: 'name',
  56 + value: 'id'
  57 +}
  58 +interface Tree {
  59 + id: number
  60 + name: string
  61 + children?: Tree[] | any[]
  62 +}
  63 +const menuOptions = ref<any[]>([]) // 树形结构
  64 +const getTree = async () => {
  65 + menuOptions.value = []
  66 + const res = await MenuApi.listSimpleMenusApi()
  67 + let menu: Tree = { id: 0, name: '主类目', children: [] }
  68 + menu.children = handleTree(res)
  69 + menuOptions.value.push(menu)
  70 +}
36 // ========== 查询 ========== 71 // ========== 查询 ==========
37 const queryParams = reactive({ 72 const queryParams = reactive({
38 - name: undefined,  
39 - status: undefined 73 + name: null,
  74 + status: null
40 }) 75 })
41 const getList = async () => { 76 const getList = async () => {
  77 + statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS)
42 tableLoading.value = true 78 tableLoading.value = true
43 const res = await MenuApi.getMenuListApi(queryParams) 79 const res = await MenuApi.getMenuListApi(queryParams)
44 tableData.value = res 80 tableData.value = res
@@ -50,6 +86,10 @@ const setDialogTile = (type: string) =&gt; { @@ -50,6 +86,10 @@ const setDialogTile = (type: string) =&gt; {
50 actionType.value = type 86 actionType.value = type
51 dialogVisible.value = true 87 dialogVisible.value = true
52 } 88 }
  89 +// 新建操作
  90 +const handleCreate = () => {
  91 + setDialogTile('create')
  92 +}
53 // 修改操作 93 // 修改操作
54 const handleUpdate = async (row: MenuVO) => { 94 const handleUpdate = async (row: MenuVO) => {
55 // 设置数据 95 // 设置数据
@@ -73,6 +113,16 @@ const rules = reactive({ @@ -73,6 +113,16 @@ const rules = reactive({
73 path: [required], 113 path: [required],
74 status: [required] 114 status: [required]
75 }) 115 })
  116 +// 查询操作
  117 +const handleQuery = async () => {
  118 + await getList()
  119 +}
  120 +// 重置操作
  121 +const resetQuery = async () => {
  122 + queryParams.name = null
  123 + queryParams.status = null
  124 + await getList()
  125 +}
76 // 保存操作 126 // 保存操作
77 const isExternal = (path: string) => { 127 const isExternal = (path: string) => {
78 return /^(https?:|mailto:|tel:)/.test(path) 128 return /^(https?:|mailto:|tel:)/.test(path)
@@ -111,12 +161,41 @@ const submitForm = async () =&gt; { @@ -111,12 +161,41 @@ const submitForm = async () =&gt; {
111 } 161 }
112 onMounted(async () => { 162 onMounted(async () => {
113 await getList() 163 await getList()
  164 + getTree()
114 }) 165 })
115 </script> 166 </script>
116 <template> 167 <template>
117 <ContentWrap> 168 <ContentWrap>
  169 + <el-form :model="queryParams" ref="queryForm" :inline="true">
  170 + <el-form-item label="菜单名称" prop="name">
  171 + <el-input v-model="queryParams.name" placeholder="请输入菜单名称" />
  172 + </el-form-item>
  173 + <el-form-item label="状态" prop="status">
  174 + <el-select v-model="queryParams.status" placeholder="请选择菜单状态">
  175 + <el-option
  176 + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
  177 + :key="dict.value"
  178 + :label="dict.label"
  179 + :value="dict.value"
  180 + />
  181 + </el-select>
  182 + </el-form-item>
  183 + <el-form-item>
  184 + <el-button type="primary" @click="handleQuery">
  185 + <Icon icon="ep:search" class="mr-5px" />
  186 + {{ t('common.query') }}
  187 + </el-button>
  188 + <el-button @click="resetQuery">
  189 + <Icon icon="ep:refresh-right" class="mr-5px" />
  190 + {{ t('common.reset') }}
  191 + </el-button>
  192 + </el-form-item>
  193 + </el-form>
118 <vxe-toolbar> 194 <vxe-toolbar>
119 <template #buttons> 195 <template #buttons>
  196 + <vxe-button status="primary" v-hasPermi="['system:post:create']" @click="handleCreate">
  197 + <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  198 + </vxe-button>
120 <vxe-button @click="xTable?.setAllTreeExpand(true)">展开所有</vxe-button> 199 <vxe-button @click="xTable?.setAllTreeExpand(true)">展开所有</vxe-button>
121 <vxe-button @click="xTable?.clearTreeExpand()">关闭所有</vxe-button> 200 <vxe-button @click="xTable?.clearTreeExpand()">关闭所有</vxe-button>
122 </template> 201 </template>
@@ -181,31 +260,160 @@ onMounted(async () =&gt; { @@ -181,31 +260,160 @@ onMounted(async () =&gt; {
181 id="menuModel" 260 id="menuModel"
182 :title="dialogTitle" 261 :title="dialogTitle"
183 width="800" 262 width="800"
184 - height="400" 263 + height="6f00"
185 min-width="460" 264 min-width="460"
186 min-height="320" 265 min-height="320"
187 show-zoom 266 show-zoom
188 resize 267 resize
189 - remember  
190 - storage  
191 transfer 268 transfer
192 show-footer 269 show-footer
193 > 270 >
194 <template #default> 271 <template #default>
195 <!-- 对话框(添加 / 修改) --> 272 <!-- 对话框(添加 / 修改) -->
196 - <vxe-form  
197 - ref="xForm"  
198 - v-if="['create', 'update'].includes(actionType)"  
199 - :data="menuForm" 273 + <el-form
  274 + :model="menuForm"
200 :rules="rules" 275 :rules="rules"
201 - @submit="submitForm" 276 + :inline="true"
  277 + label-width="120px"
  278 + label-position="right"
202 > 279 >
203 - <vxe-form-item title="菜单名称" field="name" :item-render="{}">  
204 - <template #default="{ data }">  
205 - <vxe-input v-model="data.name" placeholder="请输入菜单名称" clearable /> 280 + <el-row :gutter="24">
  281 + <el-col :span="24">
  282 + <el-form-item label="上级菜单">
  283 + <el-tree-select
  284 + node-key="id"
  285 + v-model="menuForm.parentId"
  286 + :props="menuProps"
  287 + :data="menuOptions"
  288 + check-strictly
  289 + />
  290 + </el-form-item>
  291 + </el-col>
  292 + <el-col :span="12">
  293 + <el-form-item label="菜单类型" prop="type">
  294 + <el-radio-group v-model="menuForm.type">
  295 + <el-radio-button
  296 + v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"
  297 + :key="dict.value"
  298 + :label="dict.value"
  299 + >
  300 + {{ dict.label }}
  301 + </el-radio-button>
  302 + </el-radio-group>
  303 + </el-form-item>
  304 + </el-col>
  305 + <el-col :span="12">
  306 + <el-form-item label="菜单名称" prop="name">
  307 + <el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />
  308 + </el-form-item>
  309 + </el-col>
  310 + <template v-if="menuForm.type !== 3">
  311 + <el-col :span="12">
  312 + <el-form-item label="菜单图标">
  313 + <IconSelect v-model="menuForm.icon" clearable />
  314 + </el-form-item>
  315 + </el-col>
  316 + <el-col :span="12">
  317 + <el-form-item label="路由地址" prop="path">
  318 + <template #label>
  319 + <Tooltip
  320 + titel="路由地址"
  321 + message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"
  322 + />
  323 + </template>
  324 + <el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />
  325 + </el-form-item>
  326 + </el-col>
  327 + </template>
  328 + <template v-if="menuForm.type === 2">
  329 + <el-col :span="12">
  330 + <el-form-item label="路由地址" prop="component">
  331 + <el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />
  332 + </el-form-item>
  333 + </el-col>
  334 + </template>
  335 + <template v-if="menuForm.type !== 1">
  336 + <el-col :span="12">
  337 + <el-form-item label="权限标识" prop="permission">
  338 + <template #label>
  339 + <Tooltip
  340 + titel="权限标识"
  341 + message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
  342 + />
  343 + </template>
  344 + <el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />
  345 + </el-form-item>
  346 + </el-col>
  347 + </template>
  348 + <el-col :span="12">
  349 + <el-form-item label="显示排序" prop="sort">
  350 + <el-input-number
  351 + v-model="menuForm.sort"
  352 + controls-position="right"
  353 + :min="0"
  354 + clearable
  355 + />
  356 + </el-form-item>
  357 + </el-col>
  358 + <el-col :span="12">
  359 + <el-form-item label="菜单状态" prop="status">
  360 + <el-radio-group v-model="menuForm.status">
  361 + <el-radio-button
  362 + v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"
  363 + :key="dict.value"
  364 + :label="dict.value"
  365 + >
  366 + {{ dict.label }}
  367 + </el-radio-button>
  368 + </el-radio-group>
  369 + </el-form-item>
  370 + </el-col>
  371 + <template v-if="menuForm.type !== 3">
  372 + <el-col :span="12">
  373 + <el-form-item label="显示状态" prop="status">
  374 + <template #label>
  375 + <Tooltip
  376 + titel="显示状态"
  377 + message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"
  378 + />
  379 + </template>
  380 + <el-radio-group v-model="menuForm.visible">
  381 + <el-radio-button key="true" :label="true">显示</el-radio-button>
  382 + <el-radio-button key="false" :label="false">隐藏</el-radio-button>
  383 + </el-radio-group>
  384 + </el-form-item>
  385 + </el-col>
206 </template> 386 </template>
207 - </vxe-form-item>  
208 - </vxe-form> 387 + <template v-if="menuForm.type === 2">
  388 + <el-col :span="12">
  389 + <el-form-item label="缓存状态" prop="keepAlive">
  390 + <template #label>
  391 + <Tooltip
  392 + titel="缓存状态"
  393 + message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"
  394 + />
  395 + </template>
  396 + <el-radio-group v-model="menuForm.keepAlive">
  397 + <el-radio-button key="true" :label="true">缓存</el-radio-button>
  398 + <el-radio-button key="false" :label="false">不缓存</el-radio-button>
  399 + </el-radio-group>
  400 + </el-form-item>
  401 + </el-col>
  402 + </template>
  403 + </el-row>
  404 + </el-form>
  405 + </template>
  406 + <template #footer>
  407 + <!-- 操作按钮 -->
  408 + <el-button
  409 + v-if="['create', 'update'].includes(actionType)"
  410 + type="primary"
  411 + :loading="actionLoading"
  412 + @click="submitForm"
  413 + >
  414 + {{ t('action.save') }}
  415 + </el-button>
  416 + <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>
209 </template> 417 </template>
210 </vxe-modal> 418 </vxe-modal>
211 </template> 419 </template>
yudao-ui-admin-vue3/src/views/system/menu/indexd.vue deleted
1 -<script setup lang="ts">  
2 -import { ref, reactive, onMounted } from 'vue'  
3 -import { handleTree } from '@/utils/tree'  
4 -import dayjs from 'dayjs'  
5 -import { IconSelect } from '@/components/Icon'  
6 -import { Tooltip } from '@/components/Tooltip'  
7 -import * as MenuApi from '@/api/system/menu'  
8 -import { useI18n } from '@/hooks/web/useI18n'  
9 -import {  
10 - ElRow,  
11 - ElCol,  
12 - ElTable,  
13 - ElTableColumn,  
14 - ElForm,  
15 - ElFormItem,  
16 - ElInput,  
17 - ElInputNumber,  
18 - ElSelect,  
19 - ElTreeSelect,  
20 - ElOption,  
21 - ElRadioGroup,  
22 - ElRadioButton  
23 -} from 'element-plus'  
24 -import { MenuVO } from '@/api/system/menu/types'  
25 -import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants'  
26 -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'  
27 -import { useMessage } from '@/hooks/web/useMessage'  
28 -import { required } from '@/utils/formRules.js'  
29 -const message = useMessage()  
30 -const { t } = useI18n() // 国际化  
31 -// ========== 创建菜单树结构 ==========  
32 -const loading = ref(true)  
33 -const menuData = ref<any[]>([]) // 树形结构  
34 -const getList = async () => {  
35 - const res = await MenuApi.getMenuListApi(queryParams)  
36 - menuData.value = handleTree(res)  
37 - loading.value = false  
38 -}  
39 -const menuProps = {  
40 - checkStrictly: true,  
41 - children: 'children',  
42 - label: 'name',  
43 - value: 'id'  
44 -}  
45 -interface Tree {  
46 - id: number  
47 - name: string  
48 - children?: Tree[] | any[]  
49 -}  
50 -const menuOptions = ref<any[]>([]) // 树形结构  
51 -const getTree = async () => {  
52 - menuOptions.value = []  
53 - const res = await MenuApi.listSimpleMenusApi()  
54 - let menu: Tree = { id: 0, name: '主类目', children: [] }  
55 - menu.children = handleTree(res)  
56 - menuOptions.value.push(menu)  
57 -}  
58 -// ========== 查询 ==========  
59 -const queryParams = reactive({  
60 - name: undefined,  
61 - status: undefined  
62 -})  
63 -// 查询操作  
64 -const handleQuery = async () => {  
65 - await getList()  
66 -}  
67 -// 重置操作  
68 -const resetQuery = async () => {  
69 - queryParams.name = undefined  
70 - queryParams.status = undefined  
71 - await getList()  
72 -}  
73 -// ========== CRUD 相关 ==========  
74 -const actionLoading = ref(false) // 遮罩层  
75 -const actionType = ref('') // 操作按钮的类型  
76 -const dialogVisible = ref(false) // 是否显示弹出层  
77 -const dialogTitle = ref('edit') // 弹出层标题  
78 -const menuForm = ref<MenuVO>({  
79 - id: 0,  
80 - name: '',  
81 - permission: '',  
82 - type: SystemMenuTypeEnum.DIR,  
83 - sort: 1,  
84 - parentId: 0,  
85 - path: '',  
86 - icon: '',  
87 - component: '',  
88 - status: CommonStatusEnum.ENABLE,  
89 - visible: true,  
90 - keepAlive: true,  
91 - createTime: ''  
92 -})  
93 -// 表单校验  
94 -const rules = reactive({  
95 - name: [required],  
96 - sort: [required],  
97 - path: [required],  
98 - status: [required]  
99 -})  
100 -// 设置标题  
101 -const setDialogTile = (type: string) => {  
102 - dialogTitle.value = t('action.' + type)  
103 - actionType.value = type  
104 - dialogVisible.value = true  
105 -}  
106 -// 新建操作  
107 -const handleCreate = () => {  
108 - // 重置表单  
109 - setDialogTile('create')  
110 -}  
111 -// 修改操作  
112 -const handleUpdate = async (row: MenuVO) => {  
113 - // 设置数据  
114 - const res = await MenuApi.getMenuApi(row.id)  
115 - menuForm.value = res  
116 - setDialogTile('update')  
117 -}  
118 -// 删除操作  
119 -const handleDelete = async (row: MenuVO) => {  
120 - message  
121 - .confirm(t('common.delDataMessage'), t('common.confirmTitle'))  
122 - .then(async () => {  
123 - await MenuApi.deleteMenuApi(row.id)  
124 - message.success(t('common.delSuccess'))  
125 - })  
126 - .catch(() => {})  
127 - await getList()  
128 -}  
129 -// 保存操作  
130 -const isExternal = (path: string) => {  
131 - return /^(https?:|mailto:|tel:)/.test(path)  
132 -}  
133 -const submitForm = async () => {  
134 - actionLoading.value = true  
135 - // 提交请求  
136 - try {  
137 - if (  
138 - menuForm.value.type === SystemMenuTypeEnum.DIR ||  
139 - menuForm.value.type === SystemMenuTypeEnum.MENU  
140 - ) {  
141 - if (!isExternal(menuForm.value.path)) {  
142 - if (menuForm.value.parentId === 0 && menuForm.value.path.charAt(0) !== '/') {  
143 - message.error('路径必须以 / 开头')  
144 - return  
145 - } else if (menuForm.value.parentId !== 0 && menuForm.value.path.charAt(0) === '/') {  
146 - message.error('路径不能以 / 开头')  
147 - return  
148 - }  
149 - }  
150 - }  
151 - if (actionType.value === 'create') {  
152 - await MenuApi.createMenuApi(menuForm.value)  
153 - message.success(t('common.createSuccess'))  
154 - } else {  
155 - await MenuApi.updateMenuApi(menuForm.value)  
156 - message.success(t('common.updateSuccess'))  
157 - }  
158 - // 操作成功,重新加载列表  
159 - dialogVisible.value = false  
160 - await getList()  
161 - } finally {  
162 - actionLoading.value = false  
163 - }  
164 -}  
165 -// ========== 初始化 ==========  
166 -onMounted(async () => {  
167 - await getList()  
168 - getTree()  
169 -})  
170 -</script>  
171 -<template>  
172 - <!-- 搜索工作区 -->  
173 - <ContentWrap>  
174 - <el-form :model="queryParams" ref="queryForm" :inline="true">  
175 - <el-form-item label="菜单名称" prop="name">  
176 - <el-input v-model="queryParams.name" placeholder="请输入菜单名称" />  
177 - </el-form-item>  
178 - <el-form-item label="状态" prop="status">  
179 - <el-select v-model="queryParams.status" placeholder="请选择菜单状态">  
180 - <el-option  
181 - v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"  
182 - :key="dict.value"  
183 - :label="dict.label"  
184 - :value="dict.value"  
185 - />  
186 - </el-select>  
187 - </el-form-item>  
188 - <el-form-item>  
189 - <el-button type="primary" @click="handleQuery">  
190 - <Icon icon="ep:search" class="mr-5px" />  
191 - {{ t('common.query') }}  
192 - </el-button>  
193 - <el-button @click="resetQuery">  
194 - <Icon icon="ep:refresh-right" class="mr-5px" />  
195 - {{ t('common.reset') }}  
196 - </el-button>  
197 - </el-form-item>  
198 - </el-form>  
199 - </ContentWrap>  
200 - <!-- 列表 -->  
201 - <ContentWrap>  
202 - <div class="mb-10px">  
203 - <el-button type="primary" v-hasPermi="['system:notice:create']" @click="handleCreate">  
204 - <Icon icon="ep:zoom-in" class="mr-1px" /> {{ t('action.add') }}  
205 - </el-button>  
206 - </div>  
207 - <el-table  
208 - v-loading="loading"  
209 - table-layout="auto"  
210 - row-key="id"  
211 - :data="menuData"  
212 - :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"  
213 - >  
214 - <el-table-column label="菜单名称" prop="name" width="240px">  
215 - <template #default="scope">  
216 - <Icon :icon="scope.row.icon" />  
217 - <span class="ml-3">{{ scope.row.name }}</span>  
218 - </template>  
219 - </el-table-column>  
220 - <el-table-column label="菜单类型" prop="type">  
221 - <template #default="scope">  
222 - <DictTag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="scope.row.type" />  
223 - </template>  
224 - </el-table-column>  
225 - <el-table-column label="路由地址" prop="path" />  
226 - <el-table-column label="组件路径" prop="component" />  
227 - <el-table-column label="权限标识" prop="permission" />  
228 - <el-table-column label="排序" prop="sort" />  
229 - <el-table-column label="状态" prop="status">  
230 - <template #default="scope">  
231 - <DictTag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />  
232 - </template>  
233 - </el-table-column>  
234 - <el-table-column label="创建时间" prop="createTime">  
235 - <template #default="scope">  
236 - <span>{{ dayjs(scope.row.createTime).format('YYYY-MM-DD HH:mm:ss') }}</span>  
237 - </template>  
238 - </el-table-column>  
239 - <el-table-column label="操作">  
240 - <template #default="scope">  
241 - <el-button  
242 - link  
243 - type="primary"  
244 - v-hasPermi="['system:menu:update']"  
245 - @click="handleUpdate(scope.row)"  
246 - >  
247 - <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}  
248 - </el-button>  
249 - <el-button  
250 - link  
251 - type="primary"  
252 - v-hasPermi="['system:menu:delete']"  
253 - @click="handleDelete(scope.row)"  
254 - >  
255 - <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}  
256 - </el-button>  
257 - </template>  
258 - </el-table-column>  
259 - </el-table>  
260 - </ContentWrap>  
261 - <!-- 添加或修改菜单对话框 -->  
262 - <Dialog v-model="dialogVisible" :title="dialogTitle" maxHeight="400px" width="45%">  
263 - <el-form  
264 - :model="menuForm"  
265 - :rules="rules"  
266 - :inline="true"  
267 - label-width="120px"  
268 - label-position="right"  
269 - >  
270 - <el-row :gutter="24">  
271 - <el-col :span="24">  
272 - <el-form-item label="上级菜单">  
273 - <el-tree-select  
274 - node-key="id"  
275 - v-model="menuForm.parentId"  
276 - :props="menuProps"  
277 - :data="menuOptions"  
278 - check-strictly  
279 - />  
280 - </el-form-item>  
281 - </el-col>  
282 - <el-col :span="12">  
283 - <el-form-item label="菜单类型" prop="type">  
284 - <el-radio-group v-model="menuForm.type">  
285 - <el-radio-button  
286 - v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_MENU_TYPE)"  
287 - :key="dict.value"  
288 - :label="dict.value"  
289 - >  
290 - {{ dict.label }}  
291 - </el-radio-button>  
292 - </el-radio-group>  
293 - </el-form-item>  
294 - </el-col>  
295 - <el-col :span="12">  
296 - <el-form-item label="菜单名称" prop="name">  
297 - <el-input v-model="menuForm.name" placeholder="请输入菜单名称" clearable />  
298 - </el-form-item>  
299 - </el-col>  
300 - <template v-if="menuForm.type !== 3">  
301 - <el-col :span="12">  
302 - <el-form-item label="菜单图标">  
303 - <IconSelect v-model="menuForm.icon" clearable />  
304 - </el-form-item>  
305 - </el-col>  
306 - <el-col :span="12">  
307 - <el-form-item label="路由地址" prop="path">  
308 - <template #label>  
309 - <Tooltip  
310 - titel="路由地址"  
311 - message="访问的路由地址,如:`user`。如需外网地址时,则以 `http(s)://` 开头"  
312 - />  
313 - </template>  
314 - <el-input v-model="menuForm.path" placeholder="请输入路由地址" clearable />  
315 - </el-form-item>  
316 - </el-col>  
317 - </template>  
318 - <template v-if="menuForm.type === 2">  
319 - <el-col :span="12">  
320 - <el-form-item label="路由地址" prop="component">  
321 - <el-input v-model="menuForm.component" placeholder="请输入组件地址" clearable />  
322 - </el-form-item>  
323 - </el-col>  
324 - </template>  
325 - <template v-if="menuForm.type !== 1">  
326 - <el-col :span="12">  
327 - <el-form-item label="权限标识" prop="permission">  
328 - <template #label>  
329 - <Tooltip  
330 - titel="权限标识"  
331 - message="Controller 方法上的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"  
332 - />  
333 - </template>  
334 - <el-input v-model="menuForm.permission" placeholder="请输入权限标识" clearable />  
335 - </el-form-item>  
336 - </el-col>  
337 - </template>  
338 - <el-col :span="12">  
339 - <el-form-item label="显示排序" prop="sort">  
340 - <el-input-number v-model="menuForm.sort" controls-position="right" :min="0" clearable />  
341 - </el-form-item>  
342 - </el-col>  
343 - <el-col :span="12">  
344 - <el-form-item label="菜单状态" prop="status">  
345 - <el-radio-group v-model="menuForm.status">  
346 - <el-radio-button  
347 - v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)"  
348 - :key="dict.value"  
349 - :label="dict.value"  
350 - >  
351 - {{ dict.label }}  
352 - </el-radio-button>  
353 - </el-radio-group>  
354 - </el-form-item>  
355 - </el-col>  
356 - <template v-if="menuForm.type !== 3">  
357 - <el-col :span="12">  
358 - <el-form-item label="显示状态" prop="status">  
359 - <template #label>  
360 - <Tooltip  
361 - titel="显示状态"  
362 - message="选择隐藏时,路由将不会出现在侧边栏,但仍然可以访问"  
363 - />  
364 - </template>  
365 - <el-radio-group v-model="menuForm.visible">  
366 - <el-radio-button key="true" :label="true">显示</el-radio-button>  
367 - <el-radio-button key="false" :label="false">隐藏</el-radio-button>  
368 - </el-radio-group>  
369 - </el-form-item>  
370 - </el-col>  
371 - </template>  
372 - <template v-if="menuForm.type === 2">  
373 - <el-col :span="12">  
374 - <el-form-item label="缓存状态" prop="keepAlive">  
375 - <template #label>  
376 - <Tooltip  
377 - titel="缓存状态"  
378 - message="选择缓存时,则会被 `keep-alive` 缓存,需要匹配组件的 `name` 和路由地址保持一致"  
379 - />  
380 - </template>  
381 - <el-radio-group v-model="menuForm.keepAlive">  
382 - <el-radio-button key="true" :label="true">缓存</el-radio-button>  
383 - <el-radio-button key="false" :label="false">不缓存</el-radio-button>  
384 - </el-radio-group>  
385 - </el-form-item>  
386 - </el-col>  
387 - </template>  
388 - </el-row>  
389 - </el-form>  
390 - <!-- 操作按钮 -->  
391 - <template #footer>  
392 - <el-button  
393 - v-if="['create', 'update'].includes(actionType)"  
394 - type="primary"  
395 - :loading="actionLoading"  
396 - @click="submitForm"  
397 - >  
398 - {{ t('action.save') }}  
399 - </el-button>  
400 - <el-button @click="dialogVisible = false">{{ t('dialog.close') }}</el-button>  
401 - </template>  
402 - </Dialog>  
403 -</template>  
404 -<style lang="less" scoped>  
405 -:deep(.el-button.is-text) {  
406 - margin-left: 0;  
407 - padding: 8px 10px;  
408 -}  
409 -</style>  
yudao-ui-admin-vue3/src/views/system/post/index.vue
@@ -131,8 +131,6 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; { @@ -131,8 +131,6 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; {
131 min-height="320" 131 min-height="320"
132 show-zoom 132 show-zoom
133 resize 133 resize
134 - remember  
135 - storage  
136 transfer 134 transfer
137 show-footer 135 show-footer
138 > 136 >
@@ -144,7 +142,6 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; { @@ -144,7 +142,6 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; {
144 :data="formData" 142 :data="formData"
145 :items="formItems" 143 :items="formItems"
146 :rules="rules" 144 :rules="rules"
147 - @submit="submitForm"  
148 /> 145 />
149 <Descriptions 146 <Descriptions
150 v-if="actionType === 'detail'" 147 v-if="actionType === 'detail'"
@@ -159,5 +156,15 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; { @@ -159,5 +156,15 @@ const submitForm: VxeFormEvents.Submit = async () =&gt; {
159 </template> 156 </template>
160 </Descriptions> 157 </Descriptions>
161 </template> 158 </template>
  159 + <template #footer>
  160 + <vxe-button
  161 + v-if="['create', 'update'].includes(actionType)"
  162 + status="primary"
  163 + @click="submitForm"
  164 + >
  165 + {{ t('action.save') }}
  166 + </vxe-button>
  167 + <vxe-button @click="dialogVisible = false">{{ t('dialog.close') }}</vxe-button>
  168 + </template>
162 </vxe-modal> 169 </vxe-modal>
163 </template> 170 </template>