create.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <template>
  2. <el-form :model="formData" label-width="85px" ref="form" v-loading="loading" class="pr-4">
  3. <div class="flex flex-row justify-between">
  4. <div>
  5. <el-form-item label="菜单类型" prop="type">
  6. <el-radio-group v-model="formData.type">
  7. <el-radio-button
  8. v-for="item in [
  9. { label: '目录', value: 1 },
  10. { label: '菜单', value: 2 },
  11. { label: '按钮', value: 3 },
  12. ]"
  13. :key="item.value"
  14. :label="item.value"
  15. name="type"
  16. >{{ item.label }}
  17. </el-radio-button>
  18. </el-radio-group>
  19. </el-form-item>
  20. <el-form-item label="菜单名称" prop="permission_name" :rules="[{ required: true, message: '菜单名称必须填写' }]">
  21. <el-input v-model="formData.permission_name" name="permission_name" clearable />
  22. </el-form-item>
  23. <el-form-item label="所属模块" prop="module" :rules="[{ required: true, message: '所属模块必须填写' }]" v-if="!isAction">
  24. <Select v-model="formData.module" api="modules" @clear="clearModule" />
  25. </el-form-item>
  26. <el-form-item label="路由Path" prop="route" :rules="[{ required: true, message: '路由Path必须填写' }]" v-if="!isAction">
  27. <el-input v-model="formData.route" name="route" clearable />
  28. </el-form-item>
  29. <el-form-item label="Redirect" prop="redirect" v-if="!isAction">
  30. <el-input v-model="formData.redirect" name="redirect" clearable />
  31. </el-form-item>
  32. <el-form-item label="排序" prop="sort">
  33. <el-input-number v-model="formData.sort" name="sort" :min="1" />
  34. </el-form-item>
  35. </div>
  36. <div>
  37. <el-form-item label="父级菜单" prop="parent_id">
  38. <el-cascader :options="permissions" name="parent_id" v-model="formData.parent_id" clearable :props="{ value: 'id', label: 'permission_name', checkStrictly: true }" class="w-full" />
  39. </el-form-item>
  40. <el-form-item label="权限标识" prop="permission_mark" :rules="[{ required: true, message: '权限标识必须填写' }]" v-if="!isTop">
  41. <el-input v-model="formData.permission_mark" name="permission_mark" clearable v-if="isAction" />
  42. <Select v-model="formData.permission_mark" placeholder="请选择" api="controllers" :query="{ module: formData.module }" v-else />
  43. </el-form-item>
  44. <el-form-item label="菜单Icon" prop="icon" v-if="!isAction">
  45. <el-input v-model="formData.icon" name="icon" clearable @click="open" />
  46. </el-form-item>
  47. <el-form-item label="所属组件" prop="component" :rules="[{ required: true, message: '所属组件必须填写' }]" v-if="!isAction">
  48. <Select v-model="formData.component" placeholder="请选择" allow-create api="components" :query="{ module: formData.module }" />
  49. </el-form-item>
  50. <el-form-item label="Hidden" prop="hidden" v-if="!isAction">
  51. <el-radio-group v-model="formData.hidden">
  52. <el-radio
  53. v-for="item in [
  54. { label: '显示', value: 1 },
  55. { label: '隐藏', value: 2 },
  56. ]"
  57. :key="item.value"
  58. :label="item.value"
  59. name="hidden"
  60. >{{ item.label }}</el-radio
  61. >
  62. </el-radio-group>
  63. </el-form-item>
  64. <el-form-item label="Keepalive" prop="keepalive" v-if="!isAction">
  65. <el-radio-group v-model="formData.keepalive">
  66. <el-radio
  67. v-for="item in [
  68. { label: '启用', value: 1 },
  69. { label: '禁用', value: 2 },
  70. ]"
  71. :key="item.value"
  72. :label="item.value"
  73. name="keepalive"
  74. >{{ item.label }}
  75. </el-radio>
  76. </el-radio-group>
  77. </el-form-item>
  78. </div>
  79. </div>
  80. <div>
  81. <el-form-item label="激活菜单" prop="active_menu" v-if="isMenu">
  82. <div class="w-full flex flex-row">
  83. <el-input v-model="formData.active_menu" name="active_menu" clearable class="w-3/4" />
  84. <el-tooltip effect="dark" :content="activeMenuIntro" raw-content placement="top">
  85. <div class="text-red-500 cursor-pointer w-1/4 ml-2 justify-center flex">说明</div>
  86. </el-tooltip>
  87. </div>
  88. </el-form-item>
  89. </div>
  90. <div class="flex justify-end">
  91. <el-button type="primary" @click="submitForm(form)">{{ $t('system.confirm') }}</el-button>
  92. </div>
  93. </el-form>
  94. <Dialog v-model="visible" title="选择 Icon" width="1000px" destroy-on-close>
  95. <Icons v-model="formData.icon" @close="closeSelectIcon" />
  96. </Dialog>
  97. </template>
  98. <script lang="ts" setup>
  99. import {useCreate} from '/admin/composables/curd/useCreate'
  100. import {useShow} from '/admin/composables/curd/useShow'
  101. import {useOpen} from '/admin/composables/curd/useOpen'
  102. import {onMounted, ref, watch} from 'vue'
  103. import http from '/admin/support/http'
  104. import {MenuType} from '/admin/enum/app'
  105. const props = defineProps({
  106. primary: String | Number,
  107. api: String,
  108. })
  109. const activeMenuIntro =
  110. '<div>如果是访问内页的菜单路由,例如创建文章 create/post, 虽然它隶属于文章列表,但实际上并不会嵌套在文章列表路由里</div><div>而是单独的一个路由,并且是不显示在左侧菜单的。所以在访问它的时候,需要左侧菜单高亮,则需要设置该参数</div>'
  111. const { formData, form, loading, submitForm, close, beforeCreate, beforeUpdate } = useCreate(props.api, props.primary)
  112. // 选择 icon
  113. const { open, visible } = useOpen()
  114. // 关闭选择 icon
  115. const closeSelectIcon = () => {
  116. visible.value = false
  117. }
  118. // 默认值
  119. const defaultSort = 1
  120. const defaultKeepalive = 1
  121. const defaultHidden = 1
  122. // 初始化
  123. formData.value.sort = defaultSort
  124. formData.value.keepalive = defaultKeepalive
  125. formData.value.type = MenuType.TOP_TYPE
  126. formData.value.hidden = defaultHidden
  127. // 默认目录
  128. const isTop = ref<boolean>(true)
  129. const isMenu = ref<boolean>(false)
  130. const isAction = ref<boolean>(false)
  131. // 回显示表单
  132. if (props.primary) {
  133. const { afterShow } = useShow(props.api, props.primary, formData)
  134. afterShow.value = formData => {
  135. if (formData.value.permission_mark.indexOf('@') !== -1) {
  136. formData.value.permission_mark = formData.value.permission_mark.split('@')[1]
  137. }
  138. }
  139. }
  140. const emit = defineEmits(['close'])
  141. const permissions = ref()
  142. onMounted(() => {
  143. http.get(props.api).then(r => {
  144. permissions.value = r.data.data
  145. })
  146. close(() => emit('close'))
  147. // 监听 form data
  148. watch(
  149. formData,
  150. () => {
  151. const type: number = formData.value.type
  152. if (type === MenuType.TOP_TYPE) {
  153. isTop.value = true
  154. isMenu.value = isAction.value = false
  155. } else if (type === MenuType.PAGE_TYPE) {
  156. isMenu.value = true
  157. isTop.value = isAction.value = false
  158. } else {
  159. isAction.value = true
  160. isTop.value = isMenu.value = false
  161. }
  162. },
  163. { deep: true },
  164. )
  165. })
  166. // 菜单是菜单类型的时,清除模块,那么权限标识&组件也需要清除
  167. const clearModule = () => {
  168. if (formData.value.type === MenuType.TOP_TYPE || formData.value.type === MenuType.PAGE_TYPE) {
  169. formData.value.component = null
  170. }
  171. if (formData.value.type === MenuType.PAGE_TYPE) {
  172. formData.value.permission_mark = null
  173. }
  174. }
  175. // 创建前的钩子
  176. beforeCreate.value = () => {
  177. formData.value.parent_id = getParent(formData.value.parent_id)
  178. }
  179. // 更新前的钩子
  180. beforeUpdate.value = () => {
  181. formData.value.parent_id = getParent(formData.value.parent_id)
  182. }
  183. const getParent = (parentId: any) => {
  184. if (typeof parentId === 'number') {
  185. return parentId
  186. }
  187. return typeof parentId === 'undefined' ? 0 : parentId[parentId.length - 1]
  188. }
  189. </script>