index.vue 9.5 KB

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