index.vue 9.4 KB

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