index.vue 15 KB

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