menu.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. <template>
  2. <el-card class="tjm_card_style_custom">
  3. <div class="tjm_card_title">条件检索</div>
  4. <div class="tjm_card_select">
  5. <el-scrollbar>
  6. <el-form class="tjm_card_select_left" :model="params" label-width="80px" label-position="left">
  7. <el-row :gutter="15">
  8. <el-col :lg="8" :md="12" :xs="24">
  9. <el-form-item label="菜单名称">
  10. <el-input v-model="formInline.name" clearable placeholder="请输入菜单名称"></el-input>
  11. </el-form-item>
  12. </el-col>
  13. <!-- <el-col :lg="8" :md="12" :xs="24">
  14. <el-form-item label="创建时间">
  15. <el-date-picker v-model="formInline.date" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
  16. </el-form-item>
  17. </el-col> -->
  18. <el-col :lg="8" :md="12" :xs="24">
  19. <el-form-item label="菜单状态">
  20. <el-select v-model="formInline.state" clearable placeholder="请选择菜单状态">
  21. <el-option label="启用" value="0" />
  22. <el-option label="禁用" value="1" />
  23. </el-select>
  24. </el-form-item>
  25. </el-col>
  26. </el-row>
  27. </el-form>
  28. <div class="tjm_card_select_right">
  29. <el-button type="primary" icon="search" @click="searchMenuListBtn">搜索</el-button>
  30. <el-button icon="refresh-right" @click="resetParamsBtn">重置</el-button>
  31. </div>
  32. </el-scrollbar>
  33. </div>
  34. <el-divider></el-divider>
  35. <div class="tjm_card_table">
  36. <el-table
  37. header-cell-class-name="tjm_card_table_header"
  38. :data="tableData"
  39. v-loading="loading"
  40. border
  41. :tree-props="{ children: 'children' }"
  42. row-key="id"
  43. :expand-row-keys="expandKeys"
  44. >
  45. <el-table-column type="selection" width="55" />
  46. <el-table-column prop="name" label="菜单名称" width="180" />
  47. <el-table-column prop="path" label="菜单路由" />
  48. <el-table-column prop="type" label="类型" width="120">
  49. <template #default="scope">
  50. <el-button :type="scope.row.type == 1 ? 'primary' : 'success'" text>
  51. {{ scope.row.type == 1 ? "菜单" : "按钮" }}
  52. </el-button>
  53. </template>
  54. </el-table-column>
  55. <el-table-column prop="type" label="设备" width="120">
  56. <template #default="scope">
  57. <el-button :type="scope.row.menuType != 2 ? 'primary' : 'success'" text>
  58. {{ scope.row.menuType == 1 ? "Cloud端" : scope.row.menuType == 2 ? "APP端" : "Boot端" }}
  59. </el-button>
  60. </template>
  61. </el-table-column>
  62. <el-table-column prop="icon" label="图标" width="150">
  63. <!-- <template #default="scope">
  64. <el-icon size="18">
  65. <component :is="scope.row.icon" />
  66. </el-icon>
  67. </template> -->
  68. </el-table-column>
  69. <el-table-column prop="sort" label="排序" width="150" />
  70. <el-table-column prop="ope" label="操作" width="220">
  71. <template #default="scope">
  72. <el-button
  73. link
  74. type="primary"
  75. icon="Edit"
  76. @click="handleUpdate(scope.row)"
  77. >修改</el-button>
  78. <el-button
  79. link
  80. type="primary"
  81. icon="Plus"
  82. @click="handleAdd(scope.row)"
  83. >新增</el-button>
  84. <el-button
  85. link
  86. type="primary"
  87. icon="Delete"
  88. @click="handleDelete(scope.row)"
  89. >删除</el-button>
  90. </template>
  91. </el-table-column>
  92. </el-table>
  93. </div>
  94. </el-card>
  95. <!-- 编辑 -->
  96. <el-dialog :title="title" v-model="open" width="680px" append-to-body>
  97. <el-form ref="menuRef" :model="form" :rules="rules" label-width="100px">
  98. <el-row>
  99. <el-col :span="24">
  100. <el-form-item label="上级菜单" prop="parentId">
  101. <el-tree-select
  102. style="width: 100%"
  103. v-model="form.parentId"
  104. :data="selectTableData"
  105. :props="{ value: 'id', label: 'name', children: 'children' }"
  106. value-key="id"
  107. placeholder="选择上级菜单"
  108. check-strictly
  109. />
  110. </el-form-item>
  111. </el-col>
  112. <el-col :span="24">
  113. <el-form-item label="菜单名称" prop="name">
  114. <el-input
  115. v-model="form.name"
  116. placeholder="请输入菜单名称"
  117. clearable
  118. />
  119. </el-form-item>
  120. </el-col>
  121. <el-col :span="24">
  122. <el-form-item label="菜单代码" prop="code">
  123. <el-input
  124. v-model="form.code"
  125. placeholder="请输入菜单代码"
  126. clearable
  127. />
  128. </el-form-item>
  129. </el-col>
  130. <el-col :span="24">
  131. <el-form-item label="菜单图标">
  132. <el-popover
  133. placement="bottom-start"
  134. :width="540"
  135. v-model:visible="showChooseIcon"
  136. trigger="click"
  137. @show="showSelectIcon"
  138. >
  139. <template #reference>
  140. <el-input
  141. v-model="form.icon"
  142. placeholder="点击选择图标"
  143. @blur="showSelectIcon"
  144. v-click-outside="hideSelectIcon"
  145. readonly
  146. >
  147. <template #prefix v-if="form.icon">
  148. <el-icon size="16" class="el-input__icon">
  149. <component v-if="form.icon.indexOf('ep') !== -1" :is="form.icon.slice(2)" />
  150. <svg-icon v-if="form.icon.indexOf('tjm') !== -1" :icon-class="form.icon.slice(4)" />
  151. </el-icon>
  152. </template>
  153. </el-input>
  154. </template>
  155. <icon-select
  156. ref="iconSelectRef"
  157. @selected="selected"
  158. :active-icon="form.icon"
  159. />
  160. </el-popover>
  161. </el-form-item>
  162. </el-col>
  163. <el-col :span="24">
  164. <el-form-item label="菜单描述">
  165. <el-input
  166. v-model="form.description"
  167. placeholder="请输入菜单描述"
  168. clearable
  169. />
  170. </el-form-item>
  171. </el-col>
  172. <el-col :span="24">
  173. <el-form-item label="菜单状态">
  174. <el-radio-group v-model="form.state" @change="changeType">
  175. <el-radio :label="0">启用</el-radio>
  176. <el-radio :label="1">禁用</el-radio>
  177. </el-radio-group>
  178. </el-form-item>
  179. </el-col>
  180. <el-col :span="12">
  181. <el-form-item label="菜单路径" prop="path">
  182. <el-input
  183. v-model="form.path"
  184. placeholder="菜单路径(需要与资源路径一致)"
  185. clearable
  186. />
  187. </el-form-item>
  188. </el-col>
  189. <el-col :span="12">
  190. <el-form-item label="组件路径">
  191. <el-input v-model="form.path" disabled clearable />
  192. </el-form-item>
  193. </el-col>
  194. <el-col :span="12">
  195. <el-form-item label="菜单类型">
  196. <el-radio-group v-model="form.type" @change="changeType">
  197. <el-radio :label="1">菜单</el-radio>
  198. <el-radio :label="2">按钮</el-radio>
  199. </el-radio-group>
  200. </el-form-item>
  201. </el-col>
  202. <el-col :span="12">
  203. <el-form-item label="设备类型">
  204. <el-radio-group v-model="form.menuType" @change="changeType">
  205. <!-- <el-radio :label="1">Cloud端</el-radio> -->
  206. <el-radio :label="3">Boot端</el-radio>
  207. <el-radio :label="2">APP端</el-radio>
  208. </el-radio-group>
  209. </el-form-item>
  210. </el-col>
  211. <el-col :span="12">
  212. <el-form-item label="系统选择">
  213. <el-input
  214. v-model="form.systemCode"
  215. disabled
  216. placeholder="请选择系统"
  217. clearable
  218. />
  219. </el-form-item>
  220. </el-col>
  221. <el-col :span="12">
  222. <el-form-item label="菜单排序">
  223. <el-input
  224. v-model="form.sort"
  225. placeholder="请输入菜单排序"
  226. clearable
  227. />
  228. </el-form-item>
  229. </el-col>
  230. </el-row>
  231. </el-form>
  232. <template #footer>
  233. <div class="dialog-footer">
  234. <el-button type="primary" @click="submitForm">确 定</el-button>
  235. <el-button @click="cancel">取 消</el-button>
  236. </div>
  237. </template>
  238. </el-dialog>
  239. </template>
  240. <script setup>
  241. import { getMenuTree, addMenu, modMenu, delMenu } from "@/api/system/menu.js"
  242. import API from "@/api/system/log";
  243. const { proxy } = getCurrentInstance()
  244. const loading = ref(false) //加载
  245. const expandKeys = reactive(["1", "1762659463383281665"]) //展开
  246. const tableData = ref([])
  247. const selectTableData = ref([])
  248. const formInline = ref({})
  249. //弹出框
  250. const open = ref(false)
  251. const title = ref("")
  252. const data = reactive({
  253. form: {
  254. parentId: "1",
  255. type: 1,
  256. state: 0,
  257. menuType: 3,
  258. systemCode: 10001,
  259. sort: 0
  260. },
  261. rules: {
  262. id: [{ required: true, message: "上级菜单不能为空", trigger: "blur" }],
  263. name: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }],
  264. code: [{ required: true, message: "菜单代码不能为空", trigger: "blur" }],
  265. path: [{ required: true, message: "菜单路径不能为空", trigger: "blur" }]
  266. }
  267. })
  268. function reset() {
  269. form.value = {
  270. parentId: "1",
  271. type: 1,
  272. state: 0,
  273. menuType: 3,
  274. systemCode: 10001,
  275. sort: 0
  276. }
  277. if (proxy.$refs["menuRef"]) {
  278. proxy.$refs["menuRef"].resetFields()
  279. }
  280. }
  281. const { form, rules } = toRefs(data)
  282. function submitForm() {
  283. proxy.$refs["menuRef"].validate(valid => {
  284. if (valid) {
  285. if (form.value.id != undefined) {
  286. modMenu(form.value).then(res => {
  287. API.add("修改", "system_menu");
  288. open.value = false
  289. ElMessage.success(res.msg)
  290. getMenuTreeList()
  291. })
  292. } else {
  293. addMenu(form.value).then(res => {
  294. API.add("新增", "system_menu");
  295. open.value = false
  296. ElMessage.success(res.msg)
  297. getMenuTreeList()
  298. })
  299. }
  300. } else {
  301. return false
  302. }
  303. })
  304. }
  305. function cancel() {
  306. open.value = false
  307. }
  308. function searchMenuListBtn() {
  309. getMenuTreeList()
  310. }
  311. function resetParamsBtn() {
  312. formInline.value = {}
  313. getMenuTreeList()
  314. }
  315. function getMenuTreeList() {
  316. getMenuTree(10001).then(res => tableData.value = formatMenuList(res.data))
  317. }
  318. function formatMenuList(array) {
  319. let list = [];
  320. for (const arr of array) {
  321. if (!arr.children || !arr.children.length) {
  322. if (formatQuery(arr)) list.push(arr);
  323. } else {
  324. arr.children = formatMenuList(arr.children);
  325. if (formatMenuList(arr.children).length) list.push(arr);
  326. }
  327. }
  328. return list
  329. }
  330. function formatQuery(data) {
  331. let isAccord = true;
  332. for (const key in formInline.value) {
  333. if (formInline.value[key] && (data[key] + "") && !(data[key] + "").includes(formInline.value[key])) {
  334. isAccord = false;
  335. break;
  336. }
  337. }
  338. return isAccord
  339. }
  340. //处理添加菜单时选择父级 当选择父级拥有按钮子级时候不可同时作为按钮与菜单的父级
  341. watch(tableData, () => {
  342. selectTableData.value = JSON.parse(JSON.stringify(tableData.value))
  343. dealTreeRemBtn(selectTableData.value, true)
  344. form.value.type = 1
  345. })
  346. function dealTreeRemBtn(arr, disabled = false) {
  347. arr.forEach(item => {
  348. if (item.type == 2) {
  349. item.disabled = true
  350. } else if (
  351. item.children &&
  352. item.children.length > 0 &&
  353. item.children[0].type == 2
  354. ) {
  355. item.disabled = disabled
  356. dealTreeRemBtn(item.children, disabled)
  357. } else {
  358. dealTreeRemBtn(item.children, disabled)
  359. }
  360. })
  361. }
  362. //类型更改
  363. function changeType(e) {
  364. if (e == 1) {
  365. dealTreeRemBtn(selectTableData.value, true)
  366. } else {
  367. dealTreeRemBtn(selectTableData.value, false)
  368. }
  369. }
  370. //icon
  371. const showChooseIcon = ref(false)
  372. const iconSelectRef = ref(null)
  373. // 展示下拉图标
  374. function showSelectIcon() {
  375. iconSelectRef.value.reset()
  376. showChooseIcon.value = true
  377. }
  378. // 选择图标
  379. function selected(name) {
  380. form.value.icon = name
  381. showChooseIcon.value = false
  382. }
  383. // 图标外层点击隐藏下拉列表
  384. function hideSelectIcon(event) {
  385. var elem =
  386. event.relatedTarget ||
  387. event.srcElement ||
  388. event.target ||
  389. event.currentTarget
  390. var className = elem.className
  391. if (className !== "el-input__inner") {
  392. showChooseIcon.value = false
  393. }
  394. }
  395. //新增
  396. function handleAdd(row) {
  397. reset()
  398. if (row != null && row.id) {
  399. form.value.parentId = row.id
  400. } else {
  401. form.value.parentId = "1"
  402. }
  403. open.value = true
  404. title.value = "添加菜单"
  405. }
  406. //修改
  407. async function handleUpdate(row) {
  408. reset()
  409. let {
  410. code,
  411. description,
  412. icon,
  413. id,
  414. menuType,
  415. name,
  416. parentId,
  417. path,
  418. sort,
  419. state,
  420. type
  421. } = row
  422. form.value = {
  423. code,
  424. description,
  425. icon,
  426. id,
  427. menuType,
  428. name,
  429. parentId: parentId.toString(),
  430. path,
  431. sort,
  432. state,
  433. type
  434. }
  435. open.value = true
  436. title.value = "修改菜单"
  437. }
  438. //删除
  439. function handleDelete(row) {
  440. ElMessageBox.confirm(`是否确认删除"${row.name}"?`, "删除警告", {
  441. confirmButtonText: "确定",
  442. cancelButtonText: "取消",
  443. type: "warning"
  444. }).then(() => {
  445. delMenu({ id: row.id }).then(res => {
  446. API.add("删除", "system_menu");
  447. ElMessage.success(res.msg)
  448. getMenuTreeList()
  449. })
  450. })
  451. }
  452. getMenuTreeList()
  453. </script>