index.vue 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. <template>
  2. <div class="table-box">
  3. <ProTable
  4. ref="proTable"
  5. title="菜单列表"
  6. row-key="menuId"
  7. :columns="columns"
  8. :pagination="false"
  9. :request-api="listMenuApi"
  10. :init-param="initParam"
  11. :data-callback="dataCallback"
  12. >
  13. <!-- 表格 header 按钮 -->
  14. <template #tableHeader>
  15. <el-button type="primary" v-auth="['system:menu:add']" @click="openDialog(1, '菜单新增')">新增</el-button>
  16. </template>
  17. <!-- 菜单图标 -->
  18. <template #icon="scope">
  19. <el-icon :size="18" v-if="scope.row.icon">
  20. <component :is="scope.row.icon"></component>
  21. </el-icon>
  22. </template>
  23. <!-- 表格操作 -->
  24. <template #operation="scope">
  25. <el-button type="primary" link icon="EditPen" v-auth="['system:menu:edit']" @click="openDialog(2, '菜单编辑', scope.row)"> 编辑 </el-button>
  26. <el-button type="primary" link icon="CirclePlus" v-auth="['system:menu:add']" @click="openDialog(1, '菜单新增')"> 新增 </el-button>
  27. <el-button type="primary" link icon="Delete" v-auth="['system:menu:remove']" @click="deleteMenu(scope.row)"> 删除 </el-button>
  28. </template>
  29. </ProTable>
  30. <FormDialog ref="formDialogRef" />
  31. </div>
  32. </template>
  33. <script setup lang="tsx" name="MenuManage">
  34. import { ref, reactive } from 'vue'
  35. import { useHandleData } from '@/hooks/useHandleData'
  36. import ProTable from '@/components/ProTable/index.vue'
  37. import FormDialog from '@/components/FormDialog/index.vue'
  38. import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
  39. import { handleTree } from '@/utils/common'
  40. import { listMenuApi, delMenuApi, addMenuApi, updateMenuApi, getMenuApi } from '@/api/modules/system/menu'
  41. import { getDictsApi } from '@/api/modules/system/dictData'
  42. // ProTable 实例
  43. const proTable = ref<ProTableInstance>()
  44. // 如果表格需要初始化请求参数,直接定义传给 ProTable (之后每次请求都会自动带上该参数,此参数更改之后也会一直带上,改变此参数会自动刷新表格数据)
  45. const initParam = reactive({})
  46. const menuOptions = ref<any[]>([])
  47. const menuTypeOptions = ref<any[]>([
  48. { value: 'M', label: '目录' },
  49. { value: 'C', label: '菜单' },
  50. { value: 'F', label: '按钮' }
  51. ])
  52. // dataCallback 是对于返回的表格数据做处理,如果你后台返回的数据不是 list && total && pageNum && pageSize 这些字段,可以在这里进行处理成这些字段
  53. const dataCallback = (res: any) => {
  54. const data = handleTree(res, 'menuId')
  55. menuOptions.value = []
  56. const menu: any = { menuId: 0, menuName: '主类目', children: [] }
  57. menu.children = data
  58. menuOptions.value.push(menu)
  59. setItemsOptions()
  60. return data
  61. }
  62. // 删除用户信息
  63. const deleteMenu = async (params: any) => {
  64. await useHandleData(delMenuApi, params.menuId, `删除【${params.menuName}】菜单`)
  65. proTable.value?.getTableList()
  66. }
  67. const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
  68. // 打开弹框的功能
  69. const openDialog = async (type: number, title: string, row?: any) => {
  70. let res = { data: {} }
  71. if (row?.menuId) {
  72. res = await getMenuApi(row?.menuId || null)
  73. }
  74. const params = {
  75. title,
  76. width: 720,
  77. isEdit: type !== 3,
  78. itemsOptions: itemsOptions,
  79. model: type == 1 ? {} : res.data,
  80. api: type == 1 ? addMenuApi : updateMenuApi,
  81. getTableList: proTable.value?.getTableList
  82. }
  83. formDialogRef.value?.openDialog(params)
  84. }
  85. // 表格配置项
  86. const columns = reactive<ColumnProps<any>[]>([
  87. {
  88. prop: 'menuName',
  89. label: '菜单名称',
  90. align: 'left',
  91. search: {
  92. el: 'input'
  93. },
  94. width: 180
  95. },
  96. {
  97. prop: 'orderNum',
  98. label: '显示排序',
  99. search: {
  100. el: 'input'
  101. },
  102. width: 120
  103. },
  104. {
  105. prop: 'path',
  106. label: '路由地址',
  107. search: {
  108. el: 'input'
  109. },
  110. width: 120
  111. },
  112. {
  113. prop: 'status',
  114. label: '状态',
  115. tag: true,
  116. enum: () => getDictsApi('sys_normal_disable'),
  117. search: {
  118. el: 'tree-select'
  119. },
  120. fieldNames: { label: 'dictLabel', value: 'dictValue' }
  121. },
  122. {
  123. prop: 'perms',
  124. label: '权限标识',
  125. search: {
  126. el: 'input'
  127. },
  128. width: 120
  129. },
  130. {
  131. prop: 'icon',
  132. label: '图标',
  133. search: {
  134. el: 'input'
  135. },
  136. width: 120
  137. },
  138. {
  139. prop: 'createTime',
  140. label: '创建时间',
  141. search: {
  142. el: 'date-picker',
  143. props: { type: 'datetimerange', valueFormat: 'YYYY-MM-DD HH:mm:ss' }
  144. },
  145. width: 120
  146. },
  147. { prop: 'operation', label: '操作', width: 230, fixed: 'right' }
  148. ])
  149. let itemsOptions: ProForm.ItemsOptions[] = []
  150. const setItemsOptions = () => {
  151. itemsOptions = [
  152. {
  153. label: '菜单类型',
  154. prop: 'menuType',
  155. span: 12,
  156. rules: [{ required: true, message: '菜单类型不能为空', trigger: 'change' }],
  157. compOptions: {
  158. elTagName: 'radio-button',
  159. enum: menuTypeOptions.value
  160. }
  161. },
  162. {
  163. label: '菜单状态',
  164. prop: 'status',
  165. rules: [{ required: true, message: '菜单状态不能为空', trigger: 'change' }],
  166. span: 12,
  167. compOptions: {
  168. elTagName: 'radio-button',
  169. value: '0',
  170. enum: () => getDictsApi('sys_normal_disable'),
  171. labelKey: 'dictLabel',
  172. valueKey: 'dictValue'
  173. }
  174. },
  175. {
  176. label: '上级菜单',
  177. rules: [{ required: true, message: '上级菜单不能为空', trigger: 'change' }],
  178. prop: 'parentId',
  179. compOptions: {
  180. elTagName: 'tree-select',
  181. placeholder: '请选择上级菜单',
  182. props: { label: 'menuName', value: 'menuId' },
  183. enum: menuOptions.value,
  184. checkStrictly: true
  185. }
  186. },
  187. {
  188. label: '菜单名称',
  189. prop: 'menuName',
  190. span: 12,
  191. rules: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
  192. compOptions: {
  193. elTagName: 'input',
  194. placeholder: '请输入菜单名称'
  195. }
  196. },
  197. {
  198. label: '显示排序',
  199. prop: 'orderNum',
  200. span: 12,
  201. compOptions: {
  202. elTagName: 'input-number',
  203. min: 0,
  204. controlsPosition: 'right'
  205. }
  206. },
  207. {
  208. label: '图标',
  209. prop: 'icon',
  210. show: val => {
  211. return val?.menuType !== 'F'
  212. },
  213. compOptions: {
  214. elTagName: 'icon'
  215. }
  216. },
  217. {
  218. label: '是否外链',
  219. prop: 'isFrame',
  220. span: 12,
  221. show: val => {
  222. return val?.menuType !== 'F'
  223. },
  224. tooltip: '选择是外链则路由地址需要以`http(s)://`开头',
  225. compOptions: {
  226. elTagName: 'radio-button',
  227. enum: () => getDictsApi('sys_yes_no'),
  228. enumKey: 'sys_yes_no',
  229. labelKey: 'dictLabel',
  230. valueKey: 'dictValue'
  231. }
  232. },
  233. {
  234. label: '显示状态',
  235. prop: 'visible',
  236. tooltip: '选择隐藏则路由将不会出现在侧边栏,但仍然可以访问',
  237. span: 12,
  238. show: val => {
  239. return val?.menuType !== 'F'
  240. },
  241. compOptions: {
  242. elTagName: 'radio-button',
  243. value: '0',
  244. enum: () => getDictsApi('sys_show_hide'),
  245. labelKey: 'dictLabel',
  246. valueKey: 'dictValue'
  247. }
  248. },
  249. {
  250. label: '路由地址',
  251. rules: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }],
  252. prop: 'path',
  253. show: val => {
  254. return val?.menuType !== 'F'
  255. },
  256. tooltip: '访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头',
  257. compOptions: {
  258. elTagName: 'input',
  259. placeholder: '请输入路由地址'
  260. }
  261. },
  262. {
  263. label: '路由参数',
  264. prop: 'query',
  265. show: val => {
  266. return val?.menuType !== 'F'
  267. },
  268. tooltip: '访问路由的默认传递参数,如:`{"id": 1, "name": "km"}`',
  269. compOptions: {
  270. elTagName: 'input',
  271. placeholder: '请输入路由地址'
  272. }
  273. },
  274. {
  275. label: '组件路径',
  276. rules: [{ required: true, message: '组件路径不能为空', trigger: 'blur' }],
  277. prop: 'component',
  278. tooltip: '访问的组件路径,如:`system/user/index`,默认在`views`目录下',
  279. span: 12,
  280. show: val => {
  281. return val?.menuType == 'C'
  282. },
  283. compOptions: {
  284. elTagName: 'input',
  285. placeholder: '请输入组件路径'
  286. }
  287. },
  288. {
  289. label: '路由名称',
  290. // rules: [{ required: true, message: '路由名称不能为空', trigger: 'blur' }],
  291. prop: 'componentName',
  292. tooltip: '路由名称,匹配路由内Name属性,如:UserManage',
  293. span: 12,
  294. show: val => {
  295. return val?.menuType == 'C'
  296. },
  297. compOptions: {
  298. elTagName: 'input',
  299. placeholder: '请输入路由名称'
  300. }
  301. },
  302. {
  303. label: '权限标识',
  304. prop: 'perms',
  305. tooltip: '控制器中定义的权限字符,如:system:user:list',
  306. span: 12,
  307. show: val => {
  308. return val?.menuType !== 'M'
  309. },
  310. compOptions: {
  311. elTagName: 'input',
  312. placeholder: '请输入权限标识'
  313. }
  314. },
  315. {
  316. label: '是否缓存',
  317. prop: 'isCache',
  318. span: 12,
  319. show: val => {
  320. return val?.menuType == 'C'
  321. },
  322. compOptions: {
  323. elTagName: 'radio-button',
  324. enum: () => getDictsApi('sys_yes_no'),
  325. enumKey: 'sys_yes_no',
  326. labelKey: 'dictLabel',
  327. valueKey: 'dictValue'
  328. }
  329. }
  330. ]
  331. }
  332. </script>