index.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557
  1. <template>
  2. <div>
  3. <Card :bordered="false" dis-hover class="ivu-mt">
  4. <Form
  5. ref="roleData"
  6. :model="roleData"
  7. :label-width="labelWidth"
  8. :label-position="labelPosition"
  9. @submit.native.prevent
  10. >
  11. <Row type="flex" :gutter="24">
  12. <!-- <Col v-bind="grid">
  13. <FormItem label="规则状态:">
  14. <Select v-model="roleData.is_show" placeholder="请选择" clearable @on-change="getData">
  15. <Option value="1">显示</Option>
  16. <Option value="0">不显示</Option>
  17. </Select>
  18. </FormItem>
  19. </Col> -->
  20. <Col v-bind="grid">
  21. <FormItem label="按钮名称:" prop="status2" label-for="status2">
  22. <Input v-model="roleData.keyword" search enter-button placeholder="请输入按钮名称" @on-search="getData" />
  23. </FormItem>
  24. </Col>
  25. </Row>
  26. <!-- <Row type="flex">
  27. <Col v-bind="grid">
  28. <Button type="primary" @click="menusAdd('添加规则')" icon="md-add">添加规则 </Button>
  29. </Col>
  30. </Row> -->
  31. </Form>
  32. <vxe-table
  33. :border="false"
  34. class="vxeTable mt25"
  35. highlight-hover-row
  36. highlight-current-row
  37. :loading="loading"
  38. ref="xTable"
  39. header-row-class-name="false"
  40. :tree-config="tabconfig"
  41. :data="tableData"
  42. row-id="id"
  43. >
  44. <vxe-table-column field="menu_name" tree-node title="按钮名称" min-width="100"></vxe-table-column>
  45. <vxe-table-column field="menu_path" title="路径" min-width="240" tooltip="true">
  46. <template v-slot="{ row }">
  47. <span v-if="row.auth_type == 1">页面路径:{{ row.menu_path }}</span>
  48. <span v-if="row.auth_type == 2">按钮:[{{ row.methods }}]{{ row.api_url }}</span>
  49. </template>
  50. </vxe-table-column>
  51. <vxe-table-column field="sort" title="排序" width="150"></vxe-table-column>
  52. <vxe-table-column field="flag" title="是否显示" width="150">
  53. <template v-slot="{ row }">
  54. <i-switch
  55. v-model="row.is_show_path"
  56. :value="row.is_show_path"
  57. :true-value="1"
  58. :false-value="0"
  59. @on-change="onchangeIsShow(row)"
  60. size="large"
  61. >
  62. <span slot="open">显示</span>
  63. <span slot="close">隐藏</span>
  64. </i-switch>
  65. </template>
  66. </vxe-table-column>
  67. <vxe-table-column field="date" title="操作" align="center" width="150" fixed="right">
  68. <template v-slot="{ row }">
  69. <a @click="edit(row, '编辑')">编辑</a>
  70. </template>
  71. </vxe-table-column>
  72. </vxe-table>
  73. </Card>
  74. <menus-from
  75. :formValidate="formValidate"
  76. :titleFrom="titleFrom"
  77. @getList="getList"
  78. @changeMenu="changeMenu"
  79. ref="menusFrom"
  80. @clearFrom="clearFrom"
  81. ></menus-from>
  82. <Modal
  83. v-model="ruleModal"
  84. scrollable
  85. width="1100"
  86. title="权限列表"
  87. @on-ok="addRouters"
  88. @on-cancel="ruleModal = false"
  89. @on-visible-change="modalchange"
  90. >
  91. <div class="search-rule">
  92. <Alert
  93. >基础接口,可多选,并且添加后不会再展示出现;删除权限后才会出现;公共接口,可多选,并且添加后会继续展示;</Alert
  94. >
  95. <Input
  96. class="mr10"
  97. v-model="searchRule"
  98. placeholder="输入关键词搜索"
  99. clearable
  100. style="width: 300px"
  101. ref="search"
  102. @on-enter="searchRules"
  103. @on-clear="searchRules"
  104. />
  105. <Button class="mr10" type="primary" @click="searchRules">搜索</Button>
  106. <Button @click="init">重置</Button>
  107. </div>
  108. <div class="route-list">
  109. <div class="tree">
  110. <el-tree
  111. ref="treeBox"
  112. :data="ruleCateList"
  113. :highlight-current="true"
  114. :props="defaultProps"
  115. node-key="id"
  116. :default-expanded-keys="expandedKeys"
  117. :current-node-key="nodeKey"
  118. @node-click="handleNodeClick"
  119. ></el-tree>
  120. </div>
  121. <div class="rule">
  122. <div
  123. class="rule-list"
  124. v-show="!arrs.length || arrs.includes(item.id)"
  125. :class="{ 'select-rule': seletRouteIds.includes(item.id) }"
  126. v-for="(item, index) in children"
  127. :key="index"
  128. @click="selectRule(item)"
  129. >
  130. <div>接口名称:{{ item.name }}</div>
  131. <div>请求方式:{{ item.method }}</div>
  132. <div>接口地址:{{ item.path }}</div>
  133. </div>
  134. </div>
  135. </div>
  136. <!-- <Tabs v-model="routeType" @on-click="changTab">
  137. <TabPane :label="item.name" :name="'' + index" v-for="(item, index) in foundationList" :key="item"></TabPane>
  138. </Tabs> -->
  139. </Modal>
  140. </div>
  141. </template>
  142. <script>
  143. import { mapState } from 'vuex';
  144. import {
  145. getTable,
  146. menusDetailsApi,
  147. isShowApi,
  148. editMenus,
  149. getRuleList,
  150. menusBatch,
  151. getMenusUnique,
  152. menusRuleCate,
  153. } from '@/api/systemMenus';
  154. import formCreate from '@form-create/iview';
  155. import menusFrom from './components/menusFrom';
  156. import { formatFlatteningRoutes, findFirstNonNullChildren, findFirstNonNullChildrenKeys } from '@/libs/system';
  157. export default {
  158. name: 'systemMenus',
  159. data() {
  160. return {
  161. children: [],
  162. expandedKeys: [],
  163. tabconfig: { children: 'children', reserve: true, accordion: true },
  164. spinShow: false,
  165. ruleModal: false,
  166. searchRule: '',
  167. grid: {
  168. xl: 7,
  169. lg: 7,
  170. md: 12,
  171. sm: 24,
  172. xs: 24,
  173. },
  174. roleData: {
  175. is_show: 1,
  176. keyword: '',
  177. auth_type: 1,
  178. },
  179. defaultProps: {
  180. children: 'children',
  181. label: 'name',
  182. },
  183. ruleCateList: [], //权限树
  184. loading: false,
  185. tableData: [],
  186. FromData: null,
  187. icons: '',
  188. formValidate: {},
  189. titleFrom: '',
  190. modalTitleSs: '',
  191. routeType: '0',
  192. arrs: [],
  193. foundationList: [], // 基础接口列表
  194. openList: [], // 公开接口列表
  195. seletRoute: [], // 选中路由
  196. seletRouteIds: [], // 选中id
  197. menusId: 0, // 选中分类id
  198. nodeKey: 0, // 选中节点
  199. };
  200. },
  201. components: { menusFrom, formCreate: formCreate.$form() },
  202. computed: {
  203. ...mapState('admin/layout', ['isMobile']),
  204. labelWidth() {
  205. return this.isMobile ? undefined : 75;
  206. },
  207. labelPosition() {
  208. return this.isMobile ? 'top' : 'right';
  209. },
  210. },
  211. mounted() {
  212. this.getData();
  213. },
  214. methods: {
  215. init() {
  216. this.searchRule = '';
  217. this.searchRules();
  218. },
  219. addRouters() {
  220. let data = {
  221. menus: this.seletRoute,
  222. };
  223. menusBatch(data)
  224. .then((res) => {
  225. this.getData();
  226. })
  227. .catch((res) => {
  228. this.$Message.error(res.msg);
  229. });
  230. },
  231. selectRule(data) {
  232. if (this.seletRouteIds.includes(data.id)) {
  233. let i = this.seletRouteIds.findIndex((e) => e == data.id);
  234. this.seletRouteIds.splice(i, 1);
  235. this.seletRoute.splice(i, 1);
  236. } else {
  237. this.seletRouteIds.push(data.id);
  238. this.seletRoute.push({
  239. menu_name: data.name,
  240. unique_auth: '',
  241. api_url: data.path,
  242. path: this.menusId,
  243. method: data.method,
  244. });
  245. }
  246. },
  247. changTab(name) {
  248. this.routeType = name;
  249. let index = parseInt(name);
  250. this.children = this.foundationList[index] ? this.foundationList[index].children : [];
  251. this.searchRules();
  252. },
  253. // 搜索规则
  254. searchRules() {
  255. if (this.searchRule.trim()) {
  256. this.arrs = [];
  257. let arr = this.foundationList;
  258. for (var i = 0; i < arr.length; i++) {
  259. if (arr[i].name.indexOf(this.searchRule) !== -1) {
  260. this.arrs.push(arr[i].id);
  261. }
  262. }
  263. } else {
  264. this.arrs = [];
  265. }
  266. },
  267. addRoute(row) {
  268. this.menusId = row.id;
  269. this.routeType = '0';
  270. // this.getRuleList();
  271. menusRuleCate().then((res) => {
  272. this.ruleCateList = res.data;
  273. this.ruleModal = true;
  274. if (res.data.length) {
  275. this.$nextTick((e) => {
  276. this.expandedKeys = findFirstNonNullChildrenKeys(res.data[0], []);
  277. this.nodeKey = findFirstNonNullChildren(res.data).id;
  278. this.$refs.treeBox.setCurrentKey(this.nodeKey);
  279. this.getRuleList(this.nodeKey);
  280. });
  281. }
  282. });
  283. },
  284. handleNodeClick(data) {
  285. this.getRuleList(data.id);
  286. },
  287. modalchange() {
  288. this.seletRouteIds = [];
  289. this.seletRoute = [];
  290. },
  291. // 获取权限列表
  292. getRuleList(cate_id) {
  293. getRuleList(cate_id).then((res) => {
  294. this.foundationList = res.data;
  295. this.children = res.data;
  296. this.searchRules();
  297. // this.openList = [];
  298. // this.seletRouteIds = [];
  299. // this.seletRoute = [];
  300. });
  301. },
  302. // 修改规则状态
  303. onchangeIsShow(row) {
  304. let data = {
  305. id: row.id,
  306. is_show_path: row.is_show_path,
  307. is_show: -1,
  308. };
  309. isShowApi(data)
  310. .then(async (res) => {
  311. this.$Message.success(res.msg);
  312. this.$store.dispatch('menus/getMenusNavList');
  313. })
  314. .catch((res) => {
  315. this.$Message.error(res.msg);
  316. });
  317. },
  318. // 请求列表
  319. getList() {
  320. this.formValidate = Object.assign({}, this.$options.data().formValidate);
  321. this.getData();
  322. },
  323. // 清除表单数据
  324. clearFrom() {
  325. this.formValidate = Object.assign({}, this.$options.data().formValidate);
  326. },
  327. // 添加子菜单
  328. addE(row, title) {
  329. this.formValidate = {};
  330. let pid = row.id.toString();
  331. if (pid) {
  332. menusDetailsApi(row.id)
  333. .then(async (res) => {
  334. this.formValidate.path = res.data.path;
  335. this.formValidate.path.push(row.id);
  336. this.formValidate.pid = pid;
  337. this.$refs.menusFrom.modals = true;
  338. this.$refs.menusFrom.valids = false;
  339. this.titleFrom = title;
  340. this.formValidate.auth_type = 1;
  341. this.formValidate.is_show = 0;
  342. this.formValidate.is_show_path = 0;
  343. })
  344. .catch((res) => {
  345. this.$Message.error(res.msg);
  346. });
  347. } else {
  348. this.formValidate.pid = pid;
  349. this.$refs.menusFrom.modals = true;
  350. this.$refs.menusFrom.valids = false;
  351. this.titleFrom = title;
  352. this.formValidate.auth_type = 1;
  353. this.formValidate.is_show = 0;
  354. this.formValidate.is_show_path = 0;
  355. }
  356. // this.formValidate.pid = row.id.toString();
  357. // this.$refs.menusFrom.modals = true;
  358. // this.$refs.menusFrom.valids = false;
  359. // this.titleFrom = title;
  360. // this.formValidate.auth_type = 1;
  361. // this.formValidate.is_show = '0';
  362. },
  363. // 删除
  364. del(row, tit) {
  365. let delfromData = {
  366. title: tit,
  367. url: `/setting/menus/${row.id}`,
  368. method: 'DELETE',
  369. ids: '',
  370. };
  371. this.$modalSure(delfromData)
  372. .then((res) => {
  373. this.$Message.success(res.msg);
  374. this.getData();
  375. this.getMenusUnique();
  376. // this.$store.dispatch('menus/getMenusNavList');
  377. })
  378. .catch((res) => {
  379. this.$Message.error(res.msg);
  380. });
  381. },
  382. // 规则详情
  383. menusDetails(id) {
  384. menusDetailsApi(id)
  385. .then(async (res) => {
  386. this.formValidate = res.data;
  387. this.$refs.menusFrom.modals = true;
  388. })
  389. .catch((res) => {
  390. this.$Message.error(res.msg);
  391. });
  392. },
  393. // 编辑
  394. edit(row, title, index) {
  395. this.formValidate = {};
  396. this.menusDetails(row.id);
  397. this.titleFrom = title;
  398. this.$refs.menusFrom.valids = false;
  399. this.$refs.menusFrom.getAddFrom(row.id);
  400. },
  401. // 添加
  402. menusAdd(title) {
  403. this.formValidate = {};
  404. this.$refs.menusFrom.modals = true;
  405. this.$refs.menusFrom.valids = false;
  406. // this.formValidate = Object.assign(this.$data, this.$options.formValidate());
  407. this.titleFrom = title;
  408. this.formValidate.auth_type = 1;
  409. this.formValidate.is_show = 0;
  410. this.formValidate.is_show_path = 0;
  411. },
  412. // 新增页面表单
  413. // getAddFrom () {
  414. // this.spinShow = true;
  415. // addMenus(this.roleData).then(async res => {
  416. // this.FromData = res.data;
  417. // this.$refs.menusFrom.modals = true;
  418. // this.spinShow = false;
  419. // }).catch(res => {
  420. // this.spinShow = false;
  421. // this.$Message.error(res.msg);
  422. // })
  423. // },
  424. // 列表
  425. getData() {
  426. this.loading = true;
  427. getTable(this.roleData)
  428. .then(async (res) => {
  429. this.tableData = res.data;
  430. this.loading = false;
  431. })
  432. .catch((res) => {
  433. this.loading = false;
  434. this.$Message.error(res.msg);
  435. });
  436. },
  437. changeMenu() {
  438. // this.getData(1);
  439. this.getMenusUnique();
  440. },
  441. getMenusUnique() {
  442. getMenusUnique().then((res) => {
  443. let data = res.data;
  444. this.$store.commit('userInfo/uniqueAuth', data.uniqueAuth);
  445. this.$store.commit('menus/getmenusNav', data.menus);
  446. this.$store.dispatch('routesList/setRoutesList', data.menus);
  447. let arr = formatFlatteningRoutes(this.$router.options.routes);
  448. this.formatTwoStageRoutes(arr);
  449. let routes = formatFlatteningRoutes(data.menus);
  450. this.$store.commit('menus/setOneLvRoute', routes);
  451. this.bus.$emit('routesListChange');
  452. });
  453. },
  454. formatTwoStageRoutes(arr) {
  455. if (arr.length <= 0) return false;
  456. const newArr = [];
  457. const cacheList = [];
  458. arr.forEach((v) => {
  459. if (v && v.meta && v.meta.keepAlive) {
  460. newArr.push({ ...v });
  461. cacheList.push(v.name);
  462. this.$store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList);
  463. }
  464. });
  465. return newArr;
  466. },
  467. // 关闭按钮
  468. cancel() {
  469. this.$emit('onCancel');
  470. },
  471. },
  472. };
  473. </script>
  474. <style scoped lang="scss">
  475. .vxeTable {
  476. > .vxe-table--header-wrapper {
  477. background: #fff !important;
  478. }
  479. .icon {
  480. font-size: 20px;
  481. }
  482. }
  483. .rule {
  484. display: flex;
  485. flex-wrap: wrap;
  486. overflow-y: scroll;
  487. height: max-content;
  488. max-height: 600px;
  489. flex: 1;
  490. }
  491. .tree::-webkit-scrollbar {
  492. width: 2px;
  493. background-color: #f5f5f5;
  494. }
  495. /*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
  496. .rule::-webkit-scrollbar {
  497. width: 10px;
  498. height: 10px;
  499. background-color: #f5f5f5;
  500. }
  501. /*定义滚动条轨道 内阴影+圆角*/
  502. .rule::-webkit-scrollbar-track {
  503. border-radius: 4px;
  504. background-color: #f5f5f5;
  505. }
  506. /*定义滑块 内阴影+圆角*/
  507. .rule::-webkit-scrollbar-thumb {
  508. border-radius: 4px;
  509. background-color: #ccc;
  510. }
  511. .rule-list {
  512. background-color: #f2f2f2;
  513. width: 48.5%;
  514. height: max-content;
  515. margin: 5px;
  516. border-radius: 3px;
  517. padding: 10px;
  518. color: #333;
  519. cursor: pointer;
  520. transition: all 0.1s;
  521. }
  522. .rule-list:hover {
  523. background-color: #badbfb;
  524. }
  525. .rule-list div {
  526. white-space: nowrap;
  527. }
  528. .select-rule {
  529. background-color: #badbfb;
  530. }
  531. .route-list {
  532. display: flex;
  533. margin-top: 10px;
  534. .tree {
  535. width: 200px;
  536. overflow-y: scroll;
  537. max-height: 600px;
  538. /deep/ .el-tree-node__children .el-tree-node .el-tree-node__content {
  539. padding-left: 14px !important;
  540. }
  541. }
  542. }
  543. </style>