MenuForm.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. <template>
  2. <el-form
  3. :model="formParams"
  4. :rules="rules"
  5. ref="formRef"
  6. label-placement="left"
  7. :label-width="100"
  8. require-mark-placement="left"
  9. >
  10. <el-divider border-style="dashed" class="mt-0 mb-10">基本设置</el-divider>
  11. <el-form-item label="类型" prop="menuType">
  12. <el-radio-group v-model="formParams.menuType" name="menuTypeGroup">
  13. <el-radio-button :label="0">目录</el-radio-button>
  14. <el-radio-button :label="1">菜单</el-radio-button>
  15. <el-radio-button :label="2">按钮</el-radio-button>
  16. </el-radio-group>
  17. </el-form-item>
  18. <el-form-item :label="`上级${getTypeLable}`" prop="parentId">
  19. <el-tree-select
  20. rowKey="key"
  21. :data="getPermissionList"
  22. clearable
  23. v-model="formParams.parentId"
  24. />
  25. </el-form-item>
  26. <el-row :gutter="24">
  27. <el-col :span="12">
  28. <el-form-item :label="`${getTypeLable}名称`" prop="menuName">
  29. <el-input :placeholder="`${getTypeLable}名称`" v-model="formParams.menuName" />
  30. </el-form-item>
  31. </el-col>
  32. <el-col :span="12">
  33. <el-form-item :label="`${getTypeLable}图标`" prop="icon">
  34. <template #label>
  35. <div class="flex items-center">
  36. <el-tooltip trigger="hover">
  37. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  38. <QuestionCircleOutlined />
  39. </el-icon>
  40. <template #content>
  41. {{ getTypeLable }}图标,填写图标组件名称,需在 `src\router\router-icons.ts`
  42. 中导入并映射
  43. </template>
  44. </el-tooltip>
  45. <span>{{ getTypeLable }}图标</span>
  46. </div>
  47. </template>
  48. <el-input :placeholder="`${getTypeLable}图标映射名称`" v-model="formParams.icon" />
  49. </el-form-item>
  50. </el-col>
  51. </el-row>
  52. <el-row :gutter="24">
  53. <el-col :span="12">
  54. <el-form-item prop="routeUrl" v-if="formParams.menuType != 2">
  55. <template #label>
  56. <div class="flex items-center">
  57. <el-tooltip trigger="hover">
  58. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  59. <QuestionCircleOutlined />
  60. </el-icon>
  61. <template #content> 路由地址,如:user </template>
  62. </el-tooltip>
  63. <span>路由地址</span>
  64. </div>
  65. </template>
  66. <el-input placeholder="路由地址" v-model="formParams.routeUrl" />
  67. </el-form-item>
  68. </el-col>
  69. <el-col :span="12">
  70. <el-form-item prop="routeName" v-if="formParams.menuType != 2">
  71. <template #label>
  72. <div class="flex items-center">
  73. <el-tooltip trigger="hover">
  74. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  75. <QuestionCircleOutlined />
  76. </el-icon>
  77. <template #content>
  78. 对应路由配置文件中 `name` 只能是唯一性,配置 `http(s)://` 开头地址 则会新窗口打开
  79. </template>
  80. </el-tooltip>
  81. <span>路由名称</span>
  82. </div>
  83. </template>
  84. <el-input placeholder="路由名称" v-model="formParams.routeName" />
  85. </el-form-item>
  86. </el-col>
  87. </el-row>
  88. <el-row :gutter="24" v-if="formParams.menuType > 0">
  89. <el-col :span="12">
  90. <el-form-item prop="permissionCode">
  91. <template #label>
  92. <div class="flex items-center">
  93. <el-tooltip trigger="hover">
  94. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  95. <QuestionCircleOutlined />
  96. </el-icon>
  97. <template #content> 权限标识,也是权限字符,比如 `system:menu:list` </template>
  98. </el-tooltip>
  99. <span>权限标识</span>
  100. </div>
  101. </template>
  102. <el-input placeholder="权限标识 system:user:add" v-model="formParams.permissionCode" />
  103. </el-form-item>
  104. </el-col>
  105. <el-col :span="12">
  106. <el-form-item prop="permissionName">
  107. <template #label>
  108. <div class="flex items-center">
  109. <el-tooltip trigger="hover">
  110. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  111. <QuestionCircleOutlined />
  112. </el-icon>
  113. <template #content>权限名称 对应 权限标识 `中文名称`</template>
  114. </el-tooltip>
  115. <span>权限名称</span>
  116. </div>
  117. </template>
  118. <el-input placeholder="权限名称" v-model="formParams.permissionName" />
  119. </el-form-item>
  120. </el-col>
  121. </el-row>
  122. <el-row :gutter="24">
  123. <el-col v-if="formParams.menuType != 2" :span="12">
  124. <el-form-item prop="redirect">
  125. <template #label>
  126. <div class="flex items-center">
  127. <el-tooltip trigger="hover">
  128. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  129. <QuestionCircleOutlined />
  130. </el-icon>
  131. <template #content
  132. >默认跳转路由地址,如:`/system/menu/menu` 多级路由情况下适用</template
  133. >
  134. </el-tooltip>
  135. <span>默认路由</span>
  136. </div>
  137. </template>
  138. <el-input placeholder="默认跳转路由地址" v-model="formParams.redirect" />
  139. </el-form-item>
  140. </el-col>
  141. <el-col :span="12">
  142. <el-form-item label="显示排序" prop="orderNum">
  143. <el-input-number placeholder="显示排序" v-model="formParams.orderNum" class="w-full" />
  144. </el-form-item>
  145. </el-col>
  146. </el-row>
  147. <el-row :gutter="24" v-if="formParams.menuType != 2">
  148. <el-col>
  149. <el-form-item prop="component">
  150. <template #label>
  151. <div class="flex items-center">
  152. <el-tooltip trigger="hover">
  153. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  154. <QuestionCircleOutlined />
  155. </el-icon>
  156. <template #content>
  157. 访问的组件路径,如:`/system/menu/menu`,默认在`views`目录下,默认 `LAYOUT`
  158. 如果是多级菜单 `ParentLayout`
  159. </template>
  160. </el-tooltip>
  161. <span>组件路径</span>
  162. </div>
  163. </template>
  164. <el-input placeholder="组件路径" v-model="formParams.component" />
  165. </el-form-item>
  166. </el-col>
  167. </el-row>
  168. <el-divider border-style="dashed" class="mb-10">功能设置</el-divider>
  169. <el-row :gutter="24">
  170. <el-col :span="12">
  171. <el-form-item prop="status">
  172. <template #label>
  173. <div class="flex items-center">
  174. <el-tooltip trigger="hover">
  175. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  176. <QuestionCircleOutlined />
  177. </el-icon>
  178. <template #content> 选择停用则路由将不会出现在侧边栏,也不能被访问 </template>
  179. </el-tooltip>
  180. <span>{{ getTypeLable }}状态</span>
  181. </div>
  182. </template>
  183. <el-radio-group v-model="formParams.status" name="statusGroup">
  184. <el-radio-button :label="0">正常</el-radio-button>
  185. <el-radio-button :label="1">停用</el-radio-button>
  186. </el-radio-group>
  187. </el-form-item>
  188. </el-col>
  189. <el-col :span="12" v-if="formParams.menuType != 2">
  190. <el-form-item label="根路由" prop="isRoot">
  191. <template #label>
  192. <div class="flex items-center">
  193. <el-tooltip trigger="hover">
  194. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  195. <QuestionCircleOutlined />
  196. </el-icon>
  197. <template #content
  198. >如果使用 `顶部混合菜单`,必须传 true,否则左侧会显示异常</template
  199. >
  200. </el-tooltip>
  201. <span>根路由</span>
  202. </div>
  203. </template>
  204. <el-switch v-model="formParams.isRoot" />
  205. </el-form-item>
  206. </el-col>
  207. </el-row>
  208. <el-row :gutter="24" v-if="formParams.menuType != 2">
  209. <el-col :span="12">
  210. <el-form-item prop="isVisible">
  211. <template #label>
  212. <div class="flex items-center">
  213. <el-tooltip trigger="hover">
  214. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  215. <QuestionCircleOutlined />
  216. </el-icon>
  217. <template #content>选择隐藏则路由将不会出现在侧边栏,但仍然可以访问</template>
  218. </el-tooltip>
  219. <span>显示状态</span>
  220. </div>
  221. </template>
  222. <el-radio-group v-model="formParams.isVisible" name="isVisibleGroup">
  223. <el-radio-button :label="0">显示</el-radio-button>
  224. <el-radio-button :label="1">隐藏</el-radio-button>
  225. </el-radio-group>
  226. </el-form-item>
  227. </el-col>
  228. <el-col :span="12">
  229. <el-form-item prop="isCache">
  230. <template #label>
  231. <div class="flex items-center">
  232. <el-tooltip trigger="hover">
  233. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  234. <QuestionCircleOutlined />
  235. </el-icon>
  236. <template #content
  237. >选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致</template
  238. >
  239. </el-tooltip>
  240. <span>是否缓存</span>
  241. </div>
  242. </template>
  243. <el-radio-group v-model="formParams.isCache" name="isCacheGroup">
  244. <el-radio-button :label="0">缓存</el-radio-button>
  245. <el-radio-button :label="1">不缓存</el-radio-button>
  246. </el-radio-group>
  247. </el-form-item>
  248. </el-col>
  249. </el-row>
  250. <el-row :gutter="24" v-if="formParams.menuType != 2">
  251. <el-col :span="12">
  252. <el-form-item label="是否外链" prop="isFrame">
  253. <template #label>
  254. <div class="flex items-center">
  255. <el-tooltip trigger="hover">
  256. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  257. <QuestionCircleOutlined />
  258. </el-icon>
  259. <template #content>选择是外部地址需以`http(s)://`开头</template>
  260. </el-tooltip>
  261. <span>是否外链</span>
  262. </div>
  263. </template>
  264. <el-radio-group v-model="formParams.isFrame" name="isFrameGroup">
  265. <el-radio-button :label="0">是</el-radio-button>
  266. <el-radio-button :label="1">否</el-radio-button>
  267. </el-radio-group>
  268. </el-form-item>
  269. </el-col>
  270. <el-col :span="12">
  271. <el-form-item label="简化路由" prop="alwaysShow">
  272. <template #label>
  273. <div class="flex items-center">
  274. <el-tooltip trigger="hover">
  275. <el-icon :size="18" class="mr-1 text-gray-400 cursor-pointer">
  276. <QuestionCircleOutlined />
  277. </el-icon>
  278. <template #content
  279. >取消自动计算根路由模式
  280. 开启之后,当菜单子菜单只有1个的时候,会直接显示子菜单</template
  281. >
  282. </el-tooltip>
  283. <span>简化路由</span>
  284. </div>
  285. </template>
  286. <el-switch v-model="formParams.alwaysShow" />
  287. </el-form-item>
  288. </el-col>
  289. </el-row>
  290. <el-row :gutter="24" v-if="formParams.menuType != 2">
  291. <el-col>
  292. <el-form-item
  293. label="外部地址"
  294. prop="frameSrc"
  295. v-if="formParams.menuType != 2 && formParams.isFrame === 0"
  296. >
  297. <el-input placeholder="内联外部地址" v-model="formParams.frameSrc" />
  298. </el-form-item>
  299. </el-col>
  300. </el-row>
  301. <el-form-item v-if="isShowSubmit">
  302. <el-button type="primary" :loading="subLoading" @click="formSubmit">保存修改</el-button>
  303. </el-form-item>
  304. </el-form>
  305. </template>
  306. <script lang="ts" setup>
  307. import { ref, computed } from 'vue';
  308. import { ElMessage } from 'element-plus';
  309. import { addMenu, editMenu } from '@/api/system/menu';
  310. import { QuestionCircleOutlined } from '@vicons/antd';
  311. import { replaceParams } from '@/utils/helper/treeHelper';
  312. import { cloneDeep } from 'lodash-es';
  313. const emit = defineEmits(['change']);
  314. const props = defineProps({
  315. permissionList: {
  316. type: Array,
  317. defalut: () => [],
  318. },
  319. isShowSubmit: {
  320. type: Boolean,
  321. defalut: false,
  322. },
  323. });
  324. const message = ElMessage;
  325. const formRef: any = ref(null);
  326. const subLoading = ref(false);
  327. const defaultValueRef = () => ({
  328. id: null,
  329. parentId: null,
  330. activeMenu: '',
  331. affix: false,
  332. alwaysShow: false,
  333. icon: '',
  334. isRoot: 1,
  335. isCache: 1,
  336. isFrame: 1,
  337. isVisible: 0,
  338. menuName: '',
  339. menuType: 0,
  340. openType: 1,
  341. status: 0,
  342. orderNum: null,
  343. permissionCode: '',
  344. permissionName: '',
  345. query: '',
  346. routeUrl: '',
  347. routeName: '',
  348. redirect: '',
  349. component: '', //目录默认 LAYOUT 多级菜单目录 设置 ParentLayout
  350. });
  351. const formParams: any = ref(defaultValueRef());
  352. const getPermissionList = computed(() => {
  353. return replaceParams(cloneDeep(props.permissionList || []), 'label', 'key');
  354. });
  355. const getTypeLable = computed(() => {
  356. const typsStr = ['目录', '菜单', '按钮'];
  357. return typsStr[formParams.value.menuType];
  358. });
  359. const rules = {
  360. menuName: {
  361. required: true,
  362. message: `${getTypeLable.value}名称`,
  363. trigger: 'blur',
  364. },
  365. routeUrl: {
  366. required: true,
  367. message: '路由地址不能为空',
  368. trigger: 'blur',
  369. },
  370. component: {
  371. required: true,
  372. message: '组件路径不能为空',
  373. trigger: 'blur',
  374. },
  375. routeName: {
  376. required: true,
  377. message: '路由名称不能为空',
  378. trigger: 'blur',
  379. },
  380. frameSrc: {
  381. required: true,
  382. message: '外部地址不能为空',
  383. trigger: 'blur',
  384. },
  385. };
  386. function setData(data) {
  387. formParams.value = data;
  388. formRef.value?.resetFields();
  389. }
  390. function formSubmit() {
  391. subLoading.value = true;
  392. formRef.value.validate((errors) => {
  393. if (!errors) {
  394. if (!formParams.value.id) {
  395. addMenu(formParams.value)
  396. .then(() => {
  397. subLoading.value = false;
  398. message.success('添加成功');
  399. handleReset();
  400. emit('change');
  401. })
  402. .catch(() => {
  403. subLoading.value = false;
  404. });
  405. } else {
  406. editMenu(formParams.value)
  407. .then(() => {
  408. subLoading.value = false;
  409. message.success('修改成功');
  410. handleReset();
  411. emit('change');
  412. })
  413. .catch(() => {
  414. subLoading.value = false;
  415. });
  416. }
  417. } else {
  418. subLoading.value = false;
  419. message.error('请填写完整信息');
  420. }
  421. });
  422. }
  423. function handleReset() {
  424. formRef.value.resetFields();
  425. formParams.value = Object.assign(formParams.value, defaultValueRef());
  426. }
  427. defineExpose({
  428. formSubmit,
  429. handleReset,
  430. setData,
  431. });
  432. </script>
  433. <style lang="scss" sctep>
  434. .submit-form-item {
  435. margin-left: 100px;
  436. }
  437. </style>