Просмотр исходного кода

Merge branch 'v5.0.0dev' of https://gitee.com/ZhongBangKeJi/CRMEB into v5.0.0dev

liaofei 2 лет назад
Родитель
Сommit
1954c11e23
23 измененных файлов с 1600 добавлено и 537 удалено
  1. 15 3
      crmeb/app/adminapi/controller/v1/setting/SystemMenus.php
  2. 3 1
      crmeb/app/model/system/SystemMenus.php
  3. 2 2
      crmeb/app/services/system/SystemMenusServices.php
  4. 1 2
      crmeb/crmeb/utils/Arr.php
  5. 2 2
      template/admin/src/api/systemBackendRouting.js
  6. 1 4
      template/admin/src/api/systemMenus.js
  7. 0 2
      template/admin/src/layout/component/transverseAside.vue
  8. 11 13
      template/admin/src/layout/navBars/breadcrumb/setings.vue
  9. 4 4
      template/admin/src/layout/navBars/breadcrumb/user.vue
  10. 1 9
      template/admin/src/layout/navBars/tagsView/tagsView.vue
  11. 3 0
      template/admin/src/layout/navMenu/horizontal.vue
  12. 1 2
      template/admin/src/pages/division/agent/applyList.vue
  13. 2 42
      template/admin/src/pages/setting/systemMenus/components/menusFrom.vue
  14. 17 25
      template/admin/src/pages/setting/systemMenus/index.vue
  15. 1 1
      template/admin/src/pages/setting/systemOutInterface/request.js
  16. 8 3
      template/admin/src/pages/system/backendRouting/debugging.vue
  17. 410 389
      template/admin/src/pages/system/backendRouting/index.vue
  18. 1 8
      template/admin/src/pages/system/backendRouting/request.js
  19. 545 0
      template/admin/src/pages/system/systemMenus/components/menusFrom.vue
  20. 561 0
      template/admin/src/pages/system/systemMenus/index.vue
  21. 1 24
      template/admin/src/router/modules/index.js
  22. 9 0
      template/admin/src/router/modules/system.js
  23. 1 1
      template/admin/src/theme/element.scss

+ 15 - 3
crmeb/app/adminapi/controller/v1/setting/SystemMenus.php

@@ -40,12 +40,19 @@ class SystemMenus extends AuthController
     /**
      * 菜单展示列表
      * @return \think\Response
+     * @throws \think\db\exception\DataNotFoundException
+     * @throws \think\db\exception\DbException
+     * @throws \think\db\exception\ModelNotFoundException
+     * @author 吴汐
+     * @email 442384644@qq.com
+     * @date 2023/05/06
      */
     public function index()
     {
         $where = $this->request->getMore([
             ['is_show', ''],
             ['keyword', ''],
+            ['auth_type', ''],
         ]);
         return app('json')->success($this->services->getList($where));
     }
@@ -267,7 +274,7 @@ class SystemMenus extends AuthController
     }
 
     /**
-     * 显示和隐藏
+     * 权限的开启和关闭,显示和隐藏
      * @param $id
      * @return mixed
      */
@@ -277,9 +284,14 @@ class SystemMenus extends AuthController
             return app('json')->fail(100100);
         }
 
-        [$show] = $this->request->postMore([['is_show', 0]], true);
+        [$isShow, $isShowPath] = $this->request->postMore([['is_show', 0], ['is_show_path', 0]], true);
+        if ($isShow == -1) {
+            $res = $this->services->update($id, ['is_show_path' => $isShowPath]);
+        } else {
+            $res = $this->services->update($id, ['is_show' => $isShow, 'is_show_path' => $isShow]);
+        }
 
-        if ($this->services->update($id, ['is_show' => $show])) {
+        if ($res) {
             return app('json')->success(100001);
         } else {
             return app('json')->fail(100007);

+ 3 - 1
crmeb/app/model/system/SystemMenus.php

@@ -231,6 +231,8 @@ class SystemMenus extends BaseModel
      */
     public function searchAuthTypeAttr($query, $value)
     {
-        $query->where('auth_type', $value);
+        if ($value !== '') {
+            $query->where('auth_type', $value);
+        }
     }
 }

+ 2 - 2
crmeb/app/services/system/SystemMenusServices.php

@@ -152,8 +152,8 @@ class SystemMenusServices extends BaseServices
         $field[] = Form::frameInput('icon', '图标', $this->url(config('app.admin_prefix', 'admin') . '/widget.widgets/icon', ['fodder' => 'icon']), $formData['icon'] ?? '')->icon('md-add')->height('505px')->modal(['footer-hide' => true]);
         $field[] = Form::number('sort', '排序', (int)($formData['sort'] ?? 0))->precision(0);
         $field[] = Form::radio('auth_type', '类型', $formData['auth_type'] ?? 1)->options([['value' => 2, 'label' => '接口'], ['value' => 1, 'label' => '菜单(包含页面按钮)']]);
-        $field[] = Form::radio('is_show', '状态', $formData['is_show'] ?? 1)->options([['value' => 0, 'label' => '关闭'], ['value' => 1, 'label' => '开启']]);
-        $field[] = Form::radio('is_show_path', '是否为前端隐藏菜单', $formData['is_show_path'] ?? 0)->options([['value' => 1, 'label' => '是'], ['value' => 0, 'label' => '否']]);
+        $field[] = Form::radio('is_show', '权限状态', $formData['is_show'] ?? 1)->options([['value' => 1, 'label' => '开启'], ['value' => 0, 'label' => '关闭']]);
+        $field[] = Form::radio('is_show_path', '是否显示', $formData['is_show_path'] ?? 0)->options([['value' => 1, 'label' => '显示'], ['value' => 0, 'label' => '隐藏']]);
         [$menuList, $data] = $this->getFormCascaderMenus((int)($formData['pid'] ?? 0));
         $field[] = Form::cascader('menu_list', '父级id', $data)->data($menuList)->filterable(true);
         return $field;

+ 1 - 2
crmeb/crmeb/utils/Arr.php

@@ -66,8 +66,7 @@ class Arr
             $temp['icon'] = $v['icon'];
             $temp['header'] = $v['header'];
             $temp['is_header'] = $v['is_header'];
-            $temp['is_show'] = $v['is_show'];
-            $temp['is_show_path'] = $v['is_show_path'];
+            $temp['is_show'] = $v['is_show_path'];
             if ($v['is_show_path']) {
                 $temp['auth'] = ['hidden'];
             }

+ 2 - 2
template/admin/src/api/systemBackendRouting.js

@@ -31,9 +31,9 @@ export function routeCate(appName) {
 /**
  * 路由树
  */
-export function routeList() {
+export function routeList(apiType) {
   return request({
-    url: `system/route/tree`,
+    url: `system/route/tree?app_name=${apiType}`,
     method: 'get',
   });
 }

+ 1 - 4
template/admin/src/api/systemMenus.js

@@ -86,13 +86,10 @@ export function menusDetailsApi(id) {
  * @param {Number} param data.is_show {Number} 状态值
  */
 export function isShowApi(data) {
-  let datas = {
-    is_show: data.is_show,
-  };
   return request({
     url: `/setting/menus/show/${data.id}`,
     method: 'put',
-    data: datas,
+    data,
   });
 }
 

+ 0 - 2
template/admin/src/layout/component/transverseAside.vue

@@ -255,10 +255,8 @@ export default {
     }
     .columns-round {
       background: var(--prev-color-primary);
-      // color: var(--prev-color-text-white);
       position: absolute;
       left: 0;
-      //   top: 2px;
       height: 50px;
       width: 80px;
       transform: translatey(0%);

+ 11 - 13
template/admin/src/layout/navBars/breadcrumb/setings.vue

@@ -41,7 +41,7 @@
               <aside class="el-aside mr5" style="width: 10px"></aside>
               <aside class="el-aside-dark mr5" style="width: 17px"></aside>
               <section class="el-container is-vertical">
-                <!-- <header class="el-header" style="height: 10px"></header> -->
+                <header class="el-header mb5" style="height: 10px"></header>
                 <main class="el-main"></main>
               </section>
             </section>
@@ -60,7 +60,7 @@
             <section class="el-container is-vertical el-circular">
               <header class="el-aside mb5" style="height: 10px"></header>
               <section class="el-container">
-                <aside class="el-aside mr5" style="width: 20px"></aside>
+                <aside class="el-aside-dark mr5" style="width: 17px"></aside>
                 <section class="el-container is-vertical">
                   <main class="el-main"></main>
                 </section>
@@ -140,7 +140,7 @@
               <el-option label="紫白" value="theme-6"></el-option>
               <el-option label="红黑" value="theme-7"></el-option>
               <el-option label="红白" value="theme-8"></el-option>
-              <el-option label="渐变" value="theme-9"></el-option>
+              <el-option label="渐变" value="theme-9" v-if="getThemeConfig.layout === 'columns'"></el-option>
             </el-select>
           </div>
         </div>
@@ -197,7 +197,7 @@
             <el-switch v-model="getThemeConfig.isTagsview" :width="35" @change="setLocalThemeConfig"></el-switch>
           </div>
         </div>
-        <div class="layout-breadcrumb-seting-bar-flex mt15">
+        <!-- <div class="layout-breadcrumb-seting-bar-flex mt15">
           <div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsTagsviewIcon') }}</div>
           <div class="layout-breadcrumb-seting-bar-flex-value">
             <el-switch
@@ -207,7 +207,7 @@
               @change="setLocalThemeConfig"
             ></el-switch>
           </div>
-        </div>
+        </div> -->
         <!-- <div class="layout-breadcrumb-seting-bar-flex mt15">
           <div class="layout-breadcrumb-seting-bar-flex-label">{{ $t('message.layout.fourIsCacheTagsView') }}</div>
           <div class="layout-breadcrumb-seting-bar-flex-value">
@@ -601,10 +601,7 @@ export default {
 </script>
 <style>
 body .v-modal {
-  opacity: 0.5;
-  background-color: rgba(0, 0, 0, 0.3);
-  -webkit-backdrop-filter: blur(3px);
-  backdrop-filter: blur(5px);
+  background-color: rgba(0, 0, 0, 0.1);
 }
 </style>
 <style scoped lang="scss">
@@ -640,12 +637,12 @@ body .v-modal {
     margin: 0 -5px;
     .layout-drawer-content-item.drawer-layout-active {
       border: 1px solid;
-      border-color: #2d8cf0;
+      border-color: var(--prev-color-primary);
     }
     .layout-drawer-content-item:hover {
       transition: all 0.3s ease-in-out;
       border: 1px solid;
-      border-color: #2d8cf0;
+      border-color: var(--prev-color-primary);
     }
     .layout-drawer-content-item {
       width: 107px;
@@ -663,7 +660,8 @@ body .v-modal {
       .el-container {
         height: 100%;
         .el-aside-dark {
-          background-color: var(--prev-color-seting-header);
+          opacity: .5;
+          background-color: var(--prev-tag-active-color);
           border-radius: 2px;
         }
         .el-aside {
@@ -676,7 +674,7 @@ body .v-modal {
         }
         .el-main {
           border-radius: 2px;
-          border: 1px dashed #2d8cf0;
+          border: 1px dashed var(--prev-color-primary);
           padding: 0;
           background-color: var(--prev-color-seting-main);
         }

+ 4 - 4
template/admin/src/layout/navBars/breadcrumb/user.vue

@@ -191,19 +191,19 @@ export default {
                 instance.confirmButtonLoading = true;
                 instance.confirmButtonText = this.$t('message.user.logOutExit');
                 AccountLogout().then((res) => {
-                  this.$router.replace({ name: 'login' });
+                  done();
+                  this.$Message.success('您已成功退出');
                   this.$store.commit('clearAll');
                   localStorage.clear();
                   sessionStorage.clear();
                   removeCookies('token');
                   removeCookies('expires_time');
                   removeCookies('uuid');
-                  this.$Message.success('您已成功退出');
                   // this.$router.replace({ path: `${settings.routePre}/login` });
-                  done();
                   setTimeout(() => {
+                    this.$router.replace({ name: 'login' });
                     instance.confirmButtonLoading = false;
-                  }, 300);
+                  }, 1500);
                 });
               } else {
                 done();

+ 1 - 9
template/admin/src/layout/navBars/tagsView/tagsView.vue

@@ -124,15 +124,7 @@ export default {
     onTagsClick(v, k) {
       this.tagsRoutePath = v.path;
       this.tagsRefsIndex = k;
-      try {
-        if (v.name == 'home_index') {
-          this.$router.replace(`${setting.routePre}/home_page`);
-        } else {
-          this.$router.push(v);
-        }
-      } catch (error) {
-        console.log(error);
-      }
+      this.$router.push(v);
     },
     // 获取 tagsView 的下标:用于处理 tagsView 点击时的横向滚动
     getTagsRefsIndex(path) {

+ 3 - 0
template/admin/src/layout/navMenu/horizontal.vue

@@ -130,6 +130,9 @@ export default {
 </script>
 
 <style scoped lang="scss">
+/deep/ .el-scrollbar__bar.is-horizontal {
+  height: 0;
+}
 .el-menu-horizontal-warp {
   flex: 1;
   overflow: hidden;

+ 1 - 2
template/admin/src/pages/division/agent/applyList.vue

@@ -1,6 +1,6 @@
 <template>
   <div>
-    <Card :bordered="false" dis-hover class="ivu-mt">
+    <Card :bordered="false" dis-hover class="ivu-mt mb10">
       <Form
         ref="formValidate"
         :model="formValidate"
@@ -48,7 +48,6 @@
             :columns="columns"
             :data="userLists"
             ref="table"
-            class="mt25"
             :loading="loading"
             highlight-row
             no-formValidate-text="暂无数据"

+ 2 - 42
template/admin/src/pages/setting/systemMenus/components/menusFrom.vue

@@ -39,36 +39,6 @@
               <Cascader :data="menuList" change-on-select v-model="formValidate.path" filterable></Cascader>
             </FormItem>
           </Col>
-          <!-- <Col v-bind="grid" v-if="!authType">
-            <FormItem label="请求方式:" prop="methods">
-              <Select v-model="formValidate.methods">
-                <Option value="">请求</Option>
-                <Option value="GET">GET</Option>
-                <Option value="POST">POST</Option>
-                <Option value="PUT">PUT</Option>
-                <Option value="DELETE">DELETE</Option>
-              </Select>
-            </FormItem>
-          </Col> -->
-          <!-- <Col v-bind="grid" v-if="!authType">
-            <FormItem label="接口地址:">
-              <Input v-model="formValidate.api_url" placeholder="请输入接口地址" prop="api_url"></Input>
-            </FormItem>
-          </Col> -->
-          <Col v-bind="grid" v-show="authType">
-            <FormItem label="路由地址:" prop="menu_path">
-              <Input v-model="formValidate.menu_path" placeholder="请输入路由地址" @on-change="changeUnique">
-                <template #prepend>
-                  <span>{{ $routeProStr }}</span>
-                </template>
-              </Input>
-            </FormItem>
-          </Col>
-          <Col v-bind="grid">
-            <FormItem label="权限标识:" prop="unique_auth">
-              <Input v-model="formValidate.unique_auth" placeholder="请输入权限标识"></Input>
-            </FormItem>
-          </Col>
           <Col v-bind="grid" v-if="authType">
             <FormItem label="图标:">
               <Input
@@ -85,8 +55,8 @@
               <Input type="number" v-model="formValidate.sort" placeholder="请输入排序" number></Input>
             </FormItem>
           </Col>
-          <!-- <Col v-bind="grid" v-show="authType">
-            <FormItem label="隐藏菜单:">
+          <Col v-bind="grid">
+            <FormItem label="是否显示:">
               <RadioGroup v-model="formValidate.is_show_path">
                 <Radio :label="item.value" v-for="(item, i) in isShowPathRadio" :key="i">
                   <Icon type="social-apple"></Icon>
@@ -94,16 +64,6 @@
                 </Radio>
               </RadioGroup>
             </FormItem>
-          </Col> -->
-          <Col v-bind="grid">
-            <FormItem label="状态:">
-              <RadioGroup v-model="formValidate.is_show">
-                <Radio :label="item.value" v-for="(item, i) in isShowRadio" :key="i">
-                  <Icon type="social-apple"></Icon>
-                  <span>{{ item.label }}</span>
-                </Radio>
-              </RadioGroup>
-            </FormItem>
           </Col>
         </Row>
       </Form>

+ 17 - 25
template/admin/src/pages/setting/systemMenus/index.vue

@@ -9,25 +9,25 @@
         @submit.native.prevent
       >
         <Row type="flex" :gutter="24">
-          <Col v-bind="grid">
+          <!-- <Col v-bind="grid">
             <FormItem label="规则状态:">
               <Select v-model="roleData.is_show" placeholder="请选择" clearable @on-change="getData">
                 <Option value="1">显示</Option>
                 <Option value="0">不显示</Option>
               </Select>
             </FormItem>
-          </Col>
+          </Col> -->
           <Col v-bind="grid">
             <FormItem label="按钮名称:" prop="status2" label-for="status2">
               <Input v-model="roleData.keyword" search enter-button placeholder="请输入按钮名称" @on-search="getData" />
             </FormItem>
           </Col>
         </Row>
-        <Row type="flex">
+        <!-- <Row type="flex">
           <Col v-bind="grid">
             <Button type="primary" @click="menusAdd('添加规则')" icon="md-add">添加规则 </Button>
           </Col>
-        </Row>
+        </Row> -->
       </Form>
       <vxe-table
         :border="false"
@@ -42,40 +42,31 @@
         row-id="id"
       >
         <vxe-table-column field="menu_name" tree-node title="按钮名称" min-width="100"></vxe-table-column>
-        <vxe-table-column field="unique_auth" title="前端权限" min-width="200"></vxe-table-column>
-        <vxe-table-column field="menu_path" title="路由" min-width="240" tooltip="true">
+        <vxe-table-column field="menu_path" title="路径" min-width="240" tooltip="true">
           <template v-slot="{ row }">
             <span v-if="row.auth_type == 1">页面:{{ row.menu_path }}</span>
-            <span v-if="row.auth_type == 2">接口:[{{ row.methods }}]{{ row.api_url }}</span>
+            <span v-if="row.auth_type == 2">按钮:[{{ row.methods }}]{{ row.api_url }}</span>
           </template>
         </vxe-table-column>
-        <vxe-table-column field="flag" title="规则状态" min-width="120">
+        <vxe-table-column field="sort" title="排序" width="150"></vxe-table-column>
+        <vxe-table-column field="flag" title="是否显示" width="150">
           <template v-slot="{ row }">
             <i-switch
-              v-model="row.is_show"
-              :value="row.is_show"
+              v-model="row.is_show_path"
+              :value="row.is_show_path"
               :true-value="1"
               :false-value="0"
               @on-change="onchangeIsShow(row)"
               size="large"
             >
-              <span slot="open">开启</span>
-              <span slot="close">关闭</span>
+              <span slot="open">显示</span>
+              <span slot="close">隐藏</span>
             </i-switch>
           </template>
         </vxe-table-column>
-        <vxe-table-column field="date" title="操作" align="right" width="250" fixed="right">
+        <vxe-table-column field="date" title="操作" align="center" width="150" fixed="right">
           <template v-slot="{ row }">
-            <span>
-              <a @click="addRoute(row)" v-if="row.auth_type === 1">添加权限</a>
-              <Divider type="vertical" v-if="row.auth_type === 1" />
-              <a @click="addE(row, '添加子菜单')" v-if="row.auth_type === 1">添加子菜单</a>
-              <!-- <a @click="addE(row, '添加规则')" v-else>添加规则</a> -->
-            </span>
-            <Divider type="vertical" v-if="row.auth_type === 1" />
             <a @click="edit(row, '编辑')">编辑</a>
-            <Divider type="vertical" />
-            <a @click="del(row, '删除规则')">删除</a>
           </template>
         </vxe-table-column>
       </vxe-table>
@@ -183,8 +174,9 @@ export default {
         xs: 24,
       },
       roleData: {
-        is_show: '',
+        is_show: 1,
         keyword: '',
+        auth_type: 1,
       },
       defaultProps: {
         children: 'children',
@@ -314,7 +306,8 @@ export default {
     onchangeIsShow(row) {
       let data = {
         id: row.id,
-        is_show: row.is_show,
+        is_show_path: row.is_show_path,
+        is_show: -1,
       };
       isShowApi(data)
         .then(async (res) => {
@@ -436,7 +429,6 @@ export default {
     // 列表
     getData() {
       this.loading = true;
-      this.roleData.is_show = this.roleData.is_show || '';
       getTable(this.roleData)
         .then(async (res) => {
           this.tableData = res.data;

+ 1 - 1
template/admin/src/pages/setting/systemOutInterface/request.js

@@ -15,7 +15,7 @@ service.interceptors.request.use(
       let baseUrl = Setting.apiBaseURL.replace(/adminapi/, 'kefuapi');
       config.baseURL = baseUrl;
     } else {
-      config.baseURL = 'https://v5.wuht.net/outapi' || Setting.apiBaseURL;
+      config.baseURL = Setting.apiBaseURL;
     }
     if (config.file) {
       config.headers['Content-Type'] = 'multipart/form-data';

+ 8 - 3
template/admin/src/pages/system/backendRouting/debugging.vue

@@ -279,6 +279,10 @@ export default {
         return [];
       },
     },
+    apiType: {
+      type: String,
+      default: 'adminapi',
+    },
   },
   data() {
     return {
@@ -321,7 +325,7 @@ export default {
     },
     async requestData() {
       let url, method, params, body, headers;
-      url = this.interfaceData.app_name + '/' + this.interfaceData.path;
+      url = this.apiType + '/' + this.interfaceData.path;
       method = this.interfaceData.method;
       params = this.filtersData((await this.$refs.xTable.getTableData().tableData) || []);
       body =
@@ -331,6 +335,7 @@ export default {
       let h = this.filtersData((await this.$refs.zTable.getTableData().tableData) || []);
       headers = h;
       this.codes = '';
+      console.log(url);
       requestMethod(url, method, params, body, headers)
         .then((res) => {
           if (!res) return this.$Message.error('接口异常');
@@ -519,7 +524,7 @@ export default {
   display: flex;
   justify-content: right;
 }
-/deep/ .monaco-editor{
-    min-height: 700px
+/deep/ .monaco-editor {
+  min-height: 700px;
 }
 </style>

+ 410 - 389
template/admin/src/pages/system/backendRouting/index.vue

@@ -1,403 +1,409 @@
 <template>
   <div>
-    <div class="main">
-      <!-- <Tree class="tree" :data="treeData"  @on-contextmenu="handleContextMenu">
-          <template #contextMenu>
-            <DropdownItem @click.native="handleContextCreateFolder()">新建文件夹</DropdownItem>
-            <DropdownItem @click.native="handleContextCreateFile()">新建文件</DropdownItem>
-            <DropdownItem @click.native="handleContextDelFolder()" style="color: #ed4014">删除</DropdownItem>
-          </template>
-        </Tree> -->
-      <!-- <Tree :data="treeData" :render="renderContent" class="demo-tree-render"></Tree> -->
-      <div class="ivu-mt mr20 card-tree">
-        <div class="tree">
-          <div class="main-btn">
-            <Button class="mb5 mr10" style="flex: 1" type="primary" @click="clickMenu(4)" long>新增分类</Button>
-            <Button class="mr10" type="success" @click="syncRoute()">同步</Button>
-            <Button type="info" @click="debugging()">调试</Button>
-          </div>
+    <div>
+      <Card :bordered="false" dis-hover class="mb10">
+        <Tabs v-model="apiType">
+          <TabPane label="后台接口" name="adminapi"></TabPane>
+          <TabPane label="前台接口" name="api"></TabPane>
+          <TabPane label="对外接口" name="outapi"></TabPane>
+          <TabPane label="客服接口" name="kefuapi"></TabPane>
+        </Tabs>
+      </Card>
+      <div class="main">
+        <div class="ivu-mt mr20 card-tree">
+          <div class="tree">
+            <div class="main-btn">
+              <Button class="mb5 mr10" style="flex: 1" type="primary" @click="clickMenu(4)" long>新增分类</Button>
+              <Button class="mr10" type="success" @click="syncRoute()">同步</Button>
+              <Button type="info" @click="debugging()">调试</Button>
+            </div>
 
-          <vue-tree-list
-            class="tree-list"
-            @change-name="onChangeName"
-            @delete-node="onDel"
-            :model="treeData"
-            default-tree-node-name="默认文件夹"
-            default-leaf-node-name="默认接口名"
-            v-bind:default-expanded="false"
-            :expand-only-one="true"
-          >
-            <template v-slot:leafNameDisplay="slotProps">
-              <div></div>
-              <div
-                class="tree-node"
-                :class="{
-                  node: slotProps.model.method,
-                  open: formValidate.path == slotProps.model.path && formValidate.method == slotProps.model.method,
-                }"
-                @click.stop="onClick(slotProps.model)"
-              >
-                <span
-                  class=""
+            <vue-tree-list
+              class="tree-list"
+              ref="treeList"
+              @change-name="onChangeName"
+              @delete-node="onDel"
+              :model="treeData"
+              default-tree-node-name="默认文件夹"
+              default-leaf-node-name="默认接口名"
+              v-bind:default-expanded="false"
+              :expand-only-one="true"
+            >
+              <template v-slot:leafNameDisplay="slotProps">
+                <div></div>
+                <div
+                  class="tree-node"
                   :class="{
+                    node: slotProps.model.method,
                     open: formValidate.path == slotProps.model.path && formValidate.method == slotProps.model.method,
                   }"
-                  >{{ slotProps.model.name }}</span
+                  @click.stop="onClick(slotProps.model)"
                 >
-                <Dropdown
-                  transfer
-                  @on-click="
-                    (name) => {
-                      clickMenu(name, slotProps.model);
-                    }
-                  "
-                >
-                  <a href="javascript:void(0)">
-                    <Icon class="add" type="ios-more" />
-                  </a>
-                  <template #list>
-                    <DropdownMenu>
-                      <DropdownItem name="1" v-if="!slotProps.model.method">新增接口</DropdownItem>
-                      <DropdownItem name="2" v-if="!slotProps.model.method">编辑分类名</DropdownItem>
-                      <DropdownItem name="3">删除</DropdownItem>
-                    </DropdownMenu>
-                  </template>
-                </Dropdown>
-              </div>
-            </template>
-            <!-- 新建文件夹 -->
+                  <span
+                    class=""
+                    :class="{
+                      open: formValidate.path == slotProps.model.path && formValidate.method == slotProps.model.method,
+                    }"
+                    >{{ slotProps.model.name }}</span
+                  >
+                  <Dropdown
+                    transfer
+                    @on-click="
+                      (name) => {
+                        clickMenu(name, slotProps.model);
+                      }
+                    "
+                  >
+                    <a href="javascript:void(0)">
+                      <Icon class="add" type="ios-more" />
+                    </a>
+                    <template #list>
+                      <DropdownMenu>
+                        <DropdownItem name="1" v-if="!slotProps.model.method">新增接口</DropdownItem>
+                        <DropdownItem name="2" v-if="!slotProps.model.method">编辑分类名</DropdownItem>
+                        <DropdownItem name="3">删除</DropdownItem>
+                      </DropdownMenu>
+                    </template>
+                  </Dropdown>
+                </div>
+              </template>
+              <!-- 新建文件夹 -->
 
-            <span class="icon" slot="addTreeNodeIcon"></span>
-            <span class="icon" slot="addLeafNodeIcon">
-              <!-- <Icon type="md-create" /> -->
-            </span>
-            <span class="icon" slot="editNodeIcon">
-              <!-- <Icon type="md-create" /> -->
-            </span>
-            <span class="icon" slot="delNodeIcon">
-              <!-- <Icon type="ios-cut" /> -->
-            </span>
-            <template v-slot:treeNodeIcon="slotProps">
-              <span
-                v-if="slotProps.model.method"
-                class="req-method"
-                :style="{
-                  color: methodsColor(slotProps.model.method),
-                  'font-weight': slotProps.model.pid == formValidate.pid ? '500' : '500',
-                }"
-                >{{ slotProps.model.method }}</span
-              >
+              <span class="icon" slot="addTreeNodeIcon"></span>
+              <span class="icon" slot="addLeafNodeIcon">
+                <!-- <Icon type="md-create" /> -->
+              </span>
+              <span class="icon" slot="editNodeIcon">
+                <!-- <Icon type="md-create" /> -->
+              </span>
+              <span class="icon" slot="delNodeIcon">
+                <!-- <Icon type="ios-cut" /> -->
+              </span>
+              <template v-slot:treeNodeIcon="slotProps">
+                <span
+                  v-if="slotProps.model.method"
+                  class="req-method"
+                  :style="{
+                    color: methodsColor(slotProps.model.method),
+                    'font-weight': slotProps.model.pid == formValidate.pid ? '500' : '500',
+                  }"
+                  >{{ slotProps.model.method }}</span
+                >
 
-              <!-- <span v-if="slotProps.model.method"></span> -->
-            </template>
-          </vue-tree-list>
+                <!-- <span v-if="slotProps.model.method"></span> -->
+              </template>
+            </vue-tree-list>
+          </div>
         </div>
-      </div>
-      <Card :bordered="false" dis-hover class="ivu-mt right-card">
-        <div class="data">
-          <div class="eidt-sub">
-            <div class="name">
-              {{ formValidate.name }}
-            </div>
-            <div>
-              <!-- <Button type="primary" class="submission mr20" @click="debugging()">调试</Button> -->
-              <Button v-if="formValidate.id" type="primary" class="submission mr20" @click="isEdit = !isEdit">{{
-                isEdit ? '返回' : '编辑'
-              }}</Button>
-              <Button v-if="isEdit" type="primary" class="submission" @click="handleSubmit('formValidate')"
-                >保存</Button
-              >
+        <Card :bordered="false" dis-hover class="ivu-mt right-card">
+          <div class="data">
+            <div class="eidt-sub">
+              <div class="name">
+                {{ formValidate.name }}
+              </div>
+              <div>
+                <!-- <Button type="primary" class="submission mr20" @click="debugging()">调试</Button> -->
+                <Button v-if="formValidate.id" type="primary" class="submission mr20" @click="isEdit = !isEdit">{{
+                  isEdit ? '返回' : '编辑'
+                }}</Button>
+                <Button v-if="isEdit" type="primary" class="submission" @click="handleSubmit('formValidate')"
+                  >保存</Button
+                >
+              </div>
             </div>
-          </div>
-          <Form
-            class="formValidate mt20"
-            ref="formValidate"
-            :rules="ruleValidate"
-            :model="formValidate"
-            :label-width="100"
-            :label-position="labelPosition"
-            @submit.native.prevent
-          >
-            <Row :gutter="24" type="flex">
-              <Col span="24">
-                <div class="title">接口信息</div>
-                <FormItem label="接口名称:" prop="name">
-                  <Input
-                    v-if="isEdit"
-                    class="perW20"
-                    type="text"
-                    :rows="4"
-                    v-model.trim="formValidate.name"
-                    placeholder="请输入"
-                  />
-                  <span v-else>{{ formValidate.name || '' }}</span>
-                </FormItem>
-                <FormItem label="请求类型:" prop="name">
-                  <Select v-if="isEdit" v-model="formValidate.method" style="width: 120px">
-                    <Option v-for="(item, index) in requestTypeList" :key="index" :value="item.value">{{
-                      item.label
-                    }}</Option>
-                  </Select>
-                  <span v-else class="req-method" :style="'background-color:' + methodColor">{{
-                    formValidate.method || ''
-                  }}</span>
-                </FormItem>
-                <FormItem label="功能描述:" prop="name">
-                  <Input
-                    v-if="isEdit"
-                    class="perW20"
-                    type="textarea"
-                    :rows="4"
-                    v-model.trim="formValidate.describe"
-                    placeholder="请输入"
-                  />
-                  <span v-else class="text-area">{{ formValidate.describe || '--' }}</span>
-                </FormItem>
-                <FormItem label="所属分类:" prop="name" v-if="isEdit">
-                  <el-cascader
-                    v-model="formValidate.cate_id"
-                    size="small"
-                    :options="formValidate.cate_tree"
-                    :props="{ checkStrictly: true, multiple: false, emitPath: false, value: 'id', label: 'name' }"
-                    clearable
-                  ></el-cascader>
-                </FormItem>
-                <FormItem label="是否公共:" prop="name">
-                  <Switch v-if="isEdit" v-model="formValidate.type" :true-value="1" :false-value="0">
-                    <template #open>
-                      <span>是</span>
-                    </template>
-                    <template #close>
-                      <span>否</span>
-                    </template>
-                  </Switch>
-                  <span v-else class="text-area">{{ formValidate.type ? '是' : '否' }}</span>
-                </FormItem>
-              </Col>
-            </Row>
-            <Row :gutter="24" type="flex">
-              <Col span="24">
-                <div class="title">调用方式</div>
-                <FormItem label="路由地址:" prop="path">
-                  <span>{{ formValidate.path || '' }}</span>
-                </FormItem>
-                <FormItem label="文件地址:" prop="path">
-                  <span>{{ formValidate.file_path || '' }}</span>
-                </FormItem>
-                <FormItem label="方法名:" prop="path">
-                  <span>{{ formValidate.action || '' }}</span>
-                </FormItem>
-                <FormItem label="请求参数:">
-                  <vxe-table
-                    resizable
-                    show-overflow
-                    keep-source
-                    ref="xTable"
-                    row-id="id"
-                    :print-config="{}"
-                    :export-config="{}"
-                    :loading="loading"
-                    :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
-                    :data="formValidate.request"
-                  >
-                    <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
-                    <vxe-column field="attribute" width="300" title="属性" tree-node :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.attribute" type="text"></vxe-input>
-                        <span v-else>{{ row.attribute || '' }}</span>
+            <Form
+              class="formValidate mt20"
+              ref="formValidate"
+              :rules="ruleValidate"
+              :model="formValidate"
+              :label-width="100"
+              :label-position="labelPosition"
+              @submit.native.prevent
+            >
+              <Row :gutter="24" type="flex">
+                <Col span="24">
+                  <div class="title">接口信息</div>
+                  <FormItem label="接口名称:" prop="name">
+                    <Input
+                      v-if="isEdit"
+                      class="perW20"
+                      type="text"
+                      :rows="4"
+                      v-model.trim="formValidate.name"
+                      placeholder="请输入"
+                    />
+                    <span v-else>{{ formValidate.name || '' }}</span>
+                  </FormItem>
+                  <FormItem label="请求类型:" prop="name">
+                    <Select v-if="isEdit" v-model="formValidate.method" style="width: 120px">
+                      <Option v-for="(item, index) in requestTypeList" :key="index" :value="item.value">{{
+                        item.label
+                      }}</Option>
+                    </Select>
+                    <span v-else class="req-method" :style="'background-color:' + methodColor">{{
+                      formValidate.method || ''
+                    }}</span>
+                  </FormItem>
+                  <FormItem label="功能描述:" prop="name">
+                    <Input
+                      v-if="isEdit"
+                      class="perW20"
+                      type="textarea"
+                      :rows="4"
+                      v-model.trim="formValidate.describe"
+                      placeholder="请输入"
+                    />
+                    <span v-else class="text-area">{{ formValidate.describe || '--' }}</span>
+                  </FormItem>
+                  <FormItem label="所属分类:" prop="name" v-if="isEdit">
+                    <el-cascader
+                      v-model="formValidate.cate_id"
+                      size="small"
+                      :options="formValidate.cate_tree"
+                      :props="{ checkStrictly: true, multiple: false, emitPath: false, value: 'id', label: 'name' }"
+                      clearable
+                    ></el-cascader>
+                  </FormItem>
+                  <FormItem label="是否公共:" prop="name">
+                    <Switch v-if="isEdit" v-model="formValidate.type" :true-value="1" :false-value="0">
+                      <template #open>
+                        <span>是</span>
                       </template>
-                    </vxe-column>
-                    <vxe-column field="type" title="类型" width="200" :edit-render="{}">
-                      <template #default="{ row }">
-                        <!-- <vxe-select v-if="isEdit" v-model="row.type" type="text" :optionGroups="typeList"></vxe-select> -->
-                        <vxe-select v-if="isEdit" v-model="row.type" transfer>
-                          <vxe-option
-                            v-for="item in typeList"
-                            :key="item.value"
-                            :value="item.value"
-                            :label="item.label"
-                          ></vxe-option>
-                        </vxe-select>
-                        <span v-else>{{ row.type || '' }}</span>
+                      <template #close>
+                        <span>否</span>
+                      </template>
+                    </Switch>
+                    <span v-else class="text-area">{{ formValidate.type ? '是' : '否' }}</span>
+                  </FormItem>
+                </Col>
+              </Row>
+              <Row :gutter="24" type="flex">
+                <Col span="24">
+                  <div class="title">调用方式</div>
+                  <FormItem label="路由地址:" prop="path">
+                    <span>{{ formValidate.path || '' }}</span>
+                  </FormItem>
+                  <FormItem label="文件地址:" prop="path">
+                    <span>{{ formValidate.file_path || '' }}</span>
+                  </FormItem>
+                  <FormItem label="方法名:" prop="path">
+                    <span>{{ formValidate.action || '' }}</span>
+                  </FormItem>
+                  <FormItem label="请求参数:">
+                    <vxe-table
+                      resizable
+                      show-overflow
+                      keep-source
+                      ref="xTable"
+                      row-id="id"
+                      :print-config="{}"
+                      :export-config="{}"
+                      :loading="loading"
+                      :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
+                      :data="formValidate.request"
+                    >
+                      <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
+                      <vxe-column field="attribute" width="300" title="属性" tree-node :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.attribute" type="text"></vxe-input>
+                          <span v-else>{{ row.attribute || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column field="type" title="类型" width="200" :edit-render="{}">
+                        <template #default="{ row }">
+                          <!-- <vxe-select v-if="isEdit" v-model="row.type" type="text" :optionGroups="typeList"></vxe-select> -->
+                          <vxe-select v-if="isEdit" v-model="row.type" transfer>
+                            <vxe-option
+                              v-for="item in typeList"
+                              :key="item.value"
+                              :value="item.value"
+                              :label="item.label"
+                            ></vxe-option>
+                          </vxe-select>
+                          <span v-else>{{ row.type || '' }}</span>
 
-                        <!-- <vxe-select v-model="row.type">
+                          <!-- <vxe-select v-model="row.type">
                       <vxe-option v-for="num in 12" :key="num" :value="num" :label="num"></vxe-option>
                     </vxe-select> -->
-                      </template>
-                    </vxe-column>
-                    <vxe-column field="must" title="必填" width="100" :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-checkbox
-                          v-if="isEdit"
-                          v-model="row.must"
-                          :unchecked-value="'0'"
-                          :checked-value="'1'"
-                        ></vxe-checkbox>
-                        <span v-else>{{ row.must == '1' ? '是' : '否' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column field="trip" title="说明" :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.trip" type="text"></vxe-input>
-                        <span v-else>{{ row.trip || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column title="操作" width="200" v-if="isEdit">
-                      <template #default="{ row }">
-                        <vxe-button
-                          type="text"
-                          v-if="row.type === 'array' || row.type === 'object'"
-                          status="primary"
-                          @click="insertRow(row, 'xTable')"
-                          >插入</vxe-button
-                        >
-                        <vxe-button type="text" status="primary" @click="removeRow(row, 'xTable')">删除</vxe-button>
-                      </template>
-                    </vxe-column>
-                  </vxe-table>
+                        </template>
+                      </vxe-column>
+                      <vxe-column field="must" title="必填" width="100" :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-checkbox
+                            v-if="isEdit"
+                            v-model="row.must"
+                            :unchecked-value="'0'"
+                            :checked-value="'1'"
+                          ></vxe-checkbox>
+                          <span v-else>{{ row.must == '1' ? '是' : '否' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column field="trip" title="说明" :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.trip" type="text"></vxe-input>
+                          <span v-else>{{ row.trip || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column title="操作" width="200" v-if="isEdit">
+                        <template #default="{ row }">
+                          <vxe-button
+                            type="text"
+                            v-if="row.type === 'array' || row.type === 'object'"
+                            status="primary"
+                            @click="insertRow(row, 'xTable')"
+                            >插入</vxe-button
+                          >
+                          <vxe-button type="text" status="primary" @click="removeRow(row, 'xTable')">删除</vxe-button>
+                        </template>
+                      </vxe-column>
+                    </vxe-table>
 
-                  <Button class="mt10" v-if="isEdit" type="primary" @click="insertEvent('xTable')">添加参数</Button>
-                </FormItem>
-                <FormItem label="返回参数:">
-                  <vxe-table
-                    resizable
-                    show-overflow
-                    keep-source
-                    ref="resTable"
-                    row-id="id"
-                    :print-config="{}"
-                    :export-config="{}"
-                    :loading="loading"
-                    :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
-                    :data="formValidate.response"
-                  >
-                    <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
-                    <vxe-column field="attribute" title="属性" width="300" tree-node :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.attribute" type="text"></vxe-input>
-                        <span v-else>{{ row.attribute || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column field="type" title="类型" width="200" :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-select v-if="isEdit" v-model="row.type" transfer>
-                          <vxe-option
-                            v-for="item in typeList"
-                            :key="item.value"
-                            :value="item.value"
-                            :label="item.label"
-                          ></vxe-option>
-                        </vxe-select>
-                        <span v-else>{{ row.type || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <!-- <vxe-column field="type" title="必填" :edit-render="{}">
+                    <Button class="mt10" v-if="isEdit" type="primary" @click="insertEvent('xTable')">添加参数</Button>
+                  </FormItem>
+                  <FormItem label="返回参数:">
+                    <vxe-table
+                      resizable
+                      show-overflow
+                      keep-source
+                      ref="resTable"
+                      row-id="id"
+                      :print-config="{}"
+                      :export-config="{}"
+                      :loading="loading"
+                      :tree-config="{ transform: true, rowField: 'id', parentField: 'parentId' }"
+                      :data="formValidate.response"
+                    >
+                      <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
+                      <vxe-column field="attribute" title="属性" width="300" tree-node :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.attribute" type="text"></vxe-input>
+                          <span v-else>{{ row.attribute || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column field="type" title="类型" width="200" :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-select v-if="isEdit" v-model="row.type" transfer>
+                            <vxe-option
+                              v-for="item in typeList"
+                              :key="item.value"
+                              :value="item.value"
+                              :label="item.label"
+                            ></vxe-option>
+                          </vxe-select>
+                          <span v-else>{{ row.type || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <!-- <vxe-column field="type" title="必填" :edit-render="{}">
                   <template #default="{ row }">
                     <vxe-checkbox v-model="row.must" :unchecked-value="0" :checked-value="1"></vxe-checkbox
                     >{{ row.must }}
                   </template>
                 </vxe-column> -->
-                    <vxe-column field="trip" title="说明" :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.trip" type="text"></vxe-input>
-                        <span v-else>{{ row.trip || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column title="操作" width="200" v-if="isEdit">
-                      <template #default="{ row }">
-                        <vxe-button
-                          type="text"
-                          v-if="row.type === 'array' || row.type === 'object'"
-                          status="primary"
-                          @click="insertRow(row, 'resTable')"
-                          >插入</vxe-button
-                        >
-                        <vxe-button type="text" status="primary" @click="removeRow(row, 'resTable')">删除</vxe-button>
-                      </template>
-                    </vxe-column>
-                  </vxe-table>
-                  <Button class="mt10" v-if="isEdit" type="primary" @click="insertEvent('resTable')">添加参数</Button>
-                </FormItem>
-              </Col>
-            </Row>
-            <Row :gutter="24" type="flex">
-              <Col span="24">
-                <div class="title">调用示例</div>
-                <FormItem label="请求数据示例:" prop="request_example">
-                  <Input
-                    v-if="isEdit"
-                    class="perW20"
-                    type="textarea"
-                    :rows="4"
-                    v-model.trim="formValidate.request_example"
-                    placeholder="请输入"
-                  />
-                  <span v-else class="text-area">{{ formValidate.request_example || '' }}</span>
-                </FormItem>
-                <FormItem label="返回数据示例:" prop="response_example">
-                  <Input
-                    v-if="isEdit"
-                    class="perW20"
-                    type="textarea"
-                    :rows="4"
-                    v-model.trim="formValidate.response_example"
-                    placeholder="请输入"
-                  />
-                  <span v-else class="text-area">{{ formValidate.response_example || '' }}</span>
-                </FormItem>
-                <FormItem label="错误码:">
-                  <vxe-table
-                    resizable
-                    show-overflow
-                    keep-source
-                    ref="codeTable"
-                    row-id="id"
-                    is-tree-view
-                    :print-config="{}"
-                    :export-config="{}"
-                    :loading="loading"
-                    :tree-config="{ rowField: 'id', parentField: 'parentId' }"
-                    :data="formValidate.error_code"
-                  >
-                    <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
-                    <vxe-column field="code" title="错误码" tree-node :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.code" type="text"></vxe-input>
-                        <span v-else>{{ row.code || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column field="value" title="错误码取值" :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.value" type="text"></vxe-input>
-                        <span v-else>{{ row.value || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column field="solution" title="解决方案" :edit-render="{}">
-                      <template #default="{ row }">
-                        <vxe-input v-if="isEdit" v-model="row.solution" type="text"></vxe-input>
-                        <span v-else>{{ row.solution || '' }}</span>
-                      </template>
-                    </vxe-column>
-                    <vxe-column title="操作" v-if="isEdit">
-                      <template #default="{ row }">
-                        <vxe-button type="text" status="primary" @click="removeRow(row, 'codeTable')">删除</vxe-button>
-                      </template>
-                    </vxe-column>
-                  </vxe-table>
-                  <Button class="mt10" v-if="isEdit" type="primary" @click="insertEvent('codeTable')">添加参数</Button>
-                </FormItem>
-              </Col>
-            </Row>
-            <!-- <Row :gutter="24" type="flex">
+                      <vxe-column field="trip" title="说明" :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.trip" type="text"></vxe-input>
+                          <span v-else>{{ row.trip || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column title="操作" width="200" v-if="isEdit">
+                        <template #default="{ row }">
+                          <vxe-button
+                            type="text"
+                            v-if="row.type === 'array' || row.type === 'object'"
+                            status="primary"
+                            @click="insertRow(row, 'resTable')"
+                            >插入</vxe-button
+                          >
+                          <vxe-button type="text" status="primary" @click="removeRow(row, 'resTable')">删除</vxe-button>
+                        </template>
+                      </vxe-column>
+                    </vxe-table>
+                    <Button class="mt10" v-if="isEdit" type="primary" @click="insertEvent('resTable')">添加参数</Button>
+                  </FormItem>
+                </Col>
+              </Row>
+              <Row :gutter="24" type="flex">
+                <Col span="24">
+                  <div class="title">调用示例</div>
+                  <FormItem label="请求数据示例:" prop="request_example">
+                    <Input
+                      v-if="isEdit"
+                      class="perW20"
+                      type="textarea"
+                      :rows="4"
+                      v-model.trim="formValidate.request_example"
+                      placeholder="请输入"
+                    />
+                    <span v-else class="text-area">{{ formValidate.request_example || '' }}</span>
+                  </FormItem>
+                  <FormItem label="返回数据示例:" prop="response_example">
+                    <Input
+                      v-if="isEdit"
+                      class="perW20"
+                      type="textarea"
+                      :rows="4"
+                      v-model.trim="formValidate.response_example"
+                      placeholder="请输入"
+                    />
+                    <span v-else class="text-area">{{ formValidate.response_example || '' }}</span>
+                  </FormItem>
+                  <FormItem label="错误码:">
+                    <vxe-table
+                      resizable
+                      show-overflow
+                      keep-source
+                      ref="codeTable"
+                      row-id="id"
+                      is-tree-view
+                      :print-config="{}"
+                      :export-config="{}"
+                      :loading="loading"
+                      :tree-config="{ rowField: 'id', parentField: 'parentId' }"
+                      :data="formValidate.error_code"
+                    >
+                      <!-- <vxe-column type="checkbox" width="60"></vxe-column> -->
+                      <vxe-column field="code" title="错误码" tree-node :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.code" type="text"></vxe-input>
+                          <span v-else>{{ row.code || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column field="value" title="错误码取值" :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.value" type="text"></vxe-input>
+                          <span v-else>{{ row.value || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column field="solution" title="解决方案" :edit-render="{}">
+                        <template #default="{ row }">
+                          <vxe-input v-if="isEdit" v-model="row.solution" type="text"></vxe-input>
+                          <span v-else>{{ row.solution || '' }}</span>
+                        </template>
+                      </vxe-column>
+                      <vxe-column title="操作" v-if="isEdit">
+                        <template #default="{ row }">
+                          <vxe-button type="text" status="primary" @click="removeRow(row, 'codeTable')"
+                            >删除</vxe-button
+                          >
+                        </template>
+                      </vxe-column>
+                    </vxe-table>
+                    <Button class="mt10" v-if="isEdit" type="primary" @click="insertEvent('codeTable')"
+                      >添加参数</Button
+                    >
+                  </FormItem>
+                </Col>
+              </Row>
+              <!-- <Row :gutter="24" type="flex">
               <Col span="24">
                 <FormItem>
                   <Button type="primary" class="submission" @click="handleSubmit('formValidate')">保存</Button>
                 </FormItem>
               </Col>
             </Row> -->
-          </Form>
-        </div>
-        <!-- <div v-else class="nothing">
+            </Form>
+          </div>
+          <!-- <div v-else class="nothing">
           <div class="box" @click="clickMenu(4)">
             <div class="icon">
               <Icon type="ios-folder" />
@@ -411,7 +417,8 @@
             <div class="text">新建接口</div>
           </div>
         </div> -->
-      </Card>
+        </Card>
+      </div>
     </div>
     <Modal v-model="nameModal" title="分组名称" :loading="loading" @on-ok="asyncOK">
       <label>分组名称:</label>
@@ -423,6 +430,7 @@
         :formValidate="formValidate"
         :typeList="intTypeList"
         :requestTypeList="requestTypeList"
+        :apiType="apiType"
       />
     </Drawer>
   </div>
@@ -553,7 +561,7 @@ export default {
         size: 'small',
       },
       methodColor: '#fff',
-      app_name: 'adminapi',
+      apiType: 'adminapi',
       paramsId: 0,
     };
   },
@@ -561,18 +569,26 @@ export default {
     ['formValidate.method']: {
       deep: true,
       handler(newVal, oldVal) {
-        let method = newVal.toUpperCase();
-        if (method == 'GET') {
-          this.methodColor = '#61affe';
-        } else if (method == 'POST') {
-          this.methodColor = '#49cc90';
-        } else if (method == 'PUT') {
-          this.methodColor = '#fca130';
-        } else if (method == 'DEL' || method == 'DELETE') {
-          this.methodColor = '#f93e3e';
+        console.log(newVal);
+        if (newVal) {
+          let method = newVal.toUpperCase();
+          if (method == 'GET') {
+            this.methodColor = '#61affe';
+          } else if (method == 'POST') {
+            this.methodColor = '#49cc90';
+          } else if (method == 'PUT') {
+            this.methodColor = '#fca130';
+          } else if (method == 'DEL' || method == 'DELETE') {
+            this.methodColor = '#f93e3e';
+          }
         }
       },
     },
+    apiType(newVal) {
+      if (newVal) {
+        this.getInterfaceList('one');
+      }
+    },
   },
   computed: {
     ...mapState('media', ['isMobile']),
@@ -592,7 +608,7 @@ export default {
         title: '立即同步',
         content: '同步之后,路由文件中新增的接口添加到接口列表中,路由文件中删除的路由会同步的在接口列表中删除',
         onOk: () => {
-          syncRoute(this.app_name).then((res) => {
+          syncRoute(this.apiType).then((res) => {
             this.getInterfaceList('one');
             this.$Message.success(res.msg);
             this.$Modal.remove();
@@ -625,7 +641,7 @@ export default {
     },
     getInterfaceList(disk_type) {
       try {
-        routeList()
+        routeList(this.apiType)
           .then((res) => {
             if (res.data.length) {
               res.data[0].expand = false;
@@ -643,9 +659,14 @@ export default {
               if (res.data[0].children && res.data[0].children.length) {
                 this.onClick(res.data[0]?.children[0]?.children[0]);
               }
+            } else {
+              // this.$refs.treeList.clear();
+              this.treeData = new Tree({});
+              this.formValidate = {};
             }
           })
           .catch((err) => {
+            console.log(err);
             this.$Message.error(err);
           });
       } catch (error) {
@@ -681,7 +702,7 @@ export default {
       this.formValidate.request = await this.$refs.xTable.getTableData().tableData;
       this.formValidate.response = await this.$refs.resTable.getTableData().tableData;
       this.formValidate.error_code = await this.$refs.codeTable.getTableData().tableData;
-      this.formValidate.app_name = this.app_name;
+      this.formValidate.apiType = this.apiType;
       await routeSave(this.formValidate)
         .then((res) => {
           this.isEdit = false;
@@ -777,12 +798,12 @@ export default {
         // this.formValidate.cate_id = params ? params.id : 0;
         // this.nameModal = true;
         // this.onEdit(params);
-        this.$modalForm(routeEdit(params.id, this.app_name)).then(() => this.getInterfaceList());
+        this.$modalForm(routeEdit(params.id, this.apiType)).then(() => this.getInterfaceList());
       } else if (name == 3) {
         this.onDel(params);
       } else if (name == 4) {
         // this.add();
-        this.$modalForm(routeCate(this.app_name)).then(() => this.getInterfaceList());
+        this.$modalForm(routeCate(this.apiType)).then(() => this.getInterfaceList());
       }
     },
 

+ 1 - 8
template/admin/src/pages/system/backendRouting/request.js

@@ -4,8 +4,7 @@ import { getCookies, removeCookies } from '@/libs/util';
 import { Message } from 'iview';
 
 const service = axios.create({
-  baseURL: 'https://v5.wuht.net',
-  // baseURL: location.protocol + '//'+ location.hostname,
+  baseURL: location.protocol + '//' + location.hostname,
   timeout: 10000, // 请求超时时间
 });
 axios.defaults.withCredentials = true; // 携带cookie
@@ -13,12 +12,6 @@ axios.defaults.withCredentials = true; // 携带cookie
 // 请求拦截器
 service.interceptors.request.use(
   (config) => {
-    // if (config.kefu) {
-    //   let baseUrl = Setting.apiBaseURL.replace(/adminapi/, 'kefuapi');
-    //   config.baseURL = baseUrl;
-    // } else {
-    //   config.baseURL = 'https://v5.wuht.net/outapi' || Setting.apiBaseURL;
-    // }
     if (config.file) {
       config.headers['Content-Type'] = 'multipart/form-data';
     } else {

+ 545 - 0
template/admin/src/pages/system/systemMenus/components/menusFrom.vue

@@ -0,0 +1,545 @@
+<template>
+  <div>
+    <Modal
+      v-model="modals"
+      width="700"
+      scrollable
+      closable
+      :title="titleFrom"
+      :mask-closable="false"
+      :z-index="1"
+      @on-cancel="handleReset"
+      @on-visible-change="visible"
+    >
+      <Form ref="formValidate" :model="formValidate" :label-width="110" @submit.native.prevent>
+        <!-- <Row type="flex" :gutter="24">
+          <Col span="24">
+            <FormItem label="类型:">
+              <RadioGroup v-model="formValidate.auth_type" @on-change="changeRadio">
+                <Radio :label="item.value" v-for="(item, i) in optionsRadio" :key="i">
+                  <Icon type="social-apple"></Icon>
+                  <span>{{ item.label }}</span>
+                </Radio>
+              </RadioGroup>
+            </FormItem>
+          </Col>
+        </Row> -->
+        <Row type="flex" :gutter="24">
+          <Col v-bind="grid">
+            <FormItem :label="!authType ? '接口名称:' : '按钮名称:'" prop="menu_name">
+              <div class="add">
+                <Input v-model="formValidate.menu_name" :placeholder="!authType ? '请输入接口名称' : '请输入按钮名称'">
+                </Input>
+                <!-- <Button class="ml10 df" v-show="!authType" @click="getRuleList()" icon="ios-apps"></Button> -->
+              </div>
+            </FormItem>
+          </Col>
+          <Col v-bind="grid">
+            <FormItem label="父级分类:">
+              <Cascader :data="menuList" change-on-select v-model="formValidate.path" filterable></Cascader>
+            </FormItem>
+          </Col>
+          <!-- <Col v-bind="grid" v-if="!authType">
+            <FormItem label="请求方式:" prop="methods">
+              <Select v-model="formValidate.methods">
+                <Option value="">请求</Option>
+                <Option value="GET">GET</Option>
+                <Option value="POST">POST</Option>
+                <Option value="PUT">PUT</Option>
+                <Option value="DELETE">DELETE</Option>
+              </Select>
+            </FormItem>
+          </Col> -->
+          <!-- <Col v-bind="grid" v-if="!authType">
+            <FormItem label="接口地址:">
+              <Input v-model="formValidate.api_url" placeholder="请输入接口地址" prop="api_url"></Input>
+            </FormItem>
+          </Col> -->
+          <Col v-bind="grid" v-show="authType">
+            <FormItem label="路由地址:" prop="menu_path">
+              <Input v-model="formValidate.menu_path" placeholder="请输入路由地址" @on-change="changeUnique">
+                <template #prepend>
+                  <span>{{ $routeProStr }}</span>
+                </template>
+              </Input>
+            </FormItem>
+          </Col>
+          <Col v-bind="grid">
+            <FormItem label="权限标识:" prop="unique_auth">
+              <Input v-model="formValidate.unique_auth" placeholder="请输入权限标识"></Input>
+            </FormItem>
+          </Col>
+          <Col v-bind="grid" v-if="authType">
+            <FormItem label="图标:">
+              <Input
+                v-model="formValidate.icon"
+                placeholder="请选择图标,点击右面图标"
+                icon="ios-appstore"
+                @on-click="iconClick"
+              ></Input>
+            </FormItem>
+          </Col>
+
+          <Col v-bind="grid" v-if="authType">
+            <FormItem label="排序:">
+              <Input type="number" v-model="formValidate.sort" placeholder="请输入排序" number></Input>
+            </FormItem>
+          </Col>
+          <!-- <Col v-bind="grid" v-show="authType">
+            <FormItem label="隐藏菜单:">
+              <RadioGroup v-model="formValidate.is_show_path">
+                <Radio :label="item.value" v-for="(item, i) in isShowPathRadio" :key="i">
+                  <Icon type="social-apple"></Icon>
+                  <span>{{ item.label }}</span>
+                </Radio>
+              </RadioGroup>
+            </FormItem>
+          </Col> -->
+          <Col v-bind="grid">
+            <FormItem label="状态:">
+              <RadioGroup v-model="formValidate.is_show">
+                <Radio :label="item.value" v-for="(item, i) in isShowRadio" :key="i">
+                  <Icon type="social-apple"></Icon>
+                  <span>{{ item.label }}</span>
+                </Radio>
+              </RadioGroup>
+            </FormItem>
+          </Col>
+        </Row>
+      </Form>
+      <template #footer>
+        <Button @click="modals = false">取消</Button>
+        <Button type="primary" @click="handleSubmit('formValidate')" :disabled="valids">提交</Button>
+      </template>
+    </Modal>
+    <Modal v-model="modal12" scrollable width="600" title="图标选择" footer-hide>
+      <Input
+        v-model="iconVal"
+        placeholder="输入关键词搜索,注意全是英文"
+        clearable
+        style="width: 300px"
+        @on-change="upIcon(iconVal)"
+        ref="search"
+      />
+      <div class="trees-coadd">
+        <div class="scollhide">
+          <div class="iconlist">
+            <ul class="list-inline">
+              <li class="icons-item" v-for="(item, i) in list" :key="i" :title="item.type">
+                <Icon :type="item.type" @click="iconChange(item.type)" class="ivu-icon" />
+              </li>
+            </ul>
+          </div>
+        </div>
+      </div>
+    </Modal>
+    <Modal v-model="ruleModal" scrollable width="1100" title="权限列表" footer-hide @on-visible-change="modalchange">
+      <div class="search-rule">
+        <Input
+          class="mr10"
+          v-model="searchRule"
+          placeholder="输入关键词搜索"
+          clearable
+          style="width: 300px"
+          ref="search"
+        />
+        <Button class="mr10" type="primary" @click="searchRules">搜索</Button>
+        <Button @click="init">重置</Button>
+      </div>
+      <div class="rule">
+        <div
+          class="rule-list"
+          v-show="!arrs.length || arrs.includes(index)"
+          :class="{ 'select-rule': arrs.includes(index) }"
+          v-for="(item, index) in ruleList"
+          :key="index"
+          @click="selectRule(item)"
+        >
+          <div>接口名称:{{ item.real_name }}</div>
+          <div>请求方式:{{ item.method }}</div>
+          <div>接口地址:{{ item.rule }}</div>
+        </div>
+      </div>
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { addMenusApi, addMenus, getRuleList } from '@/api/systemMenus';
+import icon from '@/utils/icon';
+
+export default {
+  name: 'menusFrom',
+  props: {
+    formValidate: {
+      type: Object,
+      default: null,
+    },
+    titleFrom: {
+      type: String,
+      default: '',
+    },
+  },
+  data() {
+    return {
+      arrs: [],
+      searchRule: '',
+      iconVal: '',
+      grid: {
+        xl: 12,
+        lg: 12,
+        md: 12,
+        sm: 24,
+        xs: 24,
+      },
+      modals: false,
+      modal12: false,
+      FromData: [],
+      valids: false,
+      list2: [],
+      list: icon,
+      authType: true,
+      search: icon,
+      ruleModal: false,
+      ruleList: [],
+    };
+  },
+  watch: {
+    'formValidate.header': function (n) {
+      this.formValidate.is_header = n ? 1 : 0;
+    },
+    'formValidate.auth_type': function (n) {
+      if (n === undefined) {
+        n = 1;
+      }
+      this.authType = n === 1;
+    },
+    'formValidate.data': function (n) {},
+  },
+  computed: {
+    /* eslint-disable */
+    optionsList() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('pid' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    headerOptionsList() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('header' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    optionsListmodule() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('module' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    optionsRadio() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('auth_type' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    isheaderRadio() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('is_header' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    isShowRadio() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('is_show' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    isShowPathRadio() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('is_show_path' === item.field) {
+          a = item.options;
+        }
+      });
+      return a;
+    },
+    menuList() {
+      let a = [];
+      this.FromData.map((item) => {
+        if ('menu_list' === item.field) {
+          a = item.props.data;
+        }
+      });
+      return a;
+    },
+  },
+  methods: {
+    // 获取权限列表
+    getRuleList() {
+      getRuleList().then((res) => {
+        this.ruleList = res.data;
+        this.ruleModal = true;
+      });
+    },
+    modalchange(type) {
+      if (!type) {
+        this.arrs = [];
+        this.ruleModal = '';
+        this.ruleModal = false;
+      }
+    },
+    changeUnique(val) {
+      let value = this.$routeProStr + val.target.value;
+      if (value.slice(0, 1) === '/') value = value.replace('/', '');
+      this.formValidate.unique_auth = value.replaceAll('/', '-');
+    },
+    visible(type) {
+      if (!type) {
+        this.authType = true;
+      }
+    },
+    selectRule(data) {
+      this.$emit('selectRule', data);
+      this.$nextTick((e) => {
+        this.ruleModal = false;
+      });
+    },
+    changeRadio(n) {
+      this.authType = n === 1 ? true : false;
+    },
+    // 搜索
+    upIcon(n) {
+      let arrs = [];
+      for (var i = 0; i < this.search.length; i++) {
+        if (this.search[i].type.indexOf(n) !== -1) {
+          arrs.push(this.search[i]);
+          this.list = arrs;
+        }
+      }
+    },
+    // 搜索规则
+    searchRules() {
+      if (this.searchRule.trim()) {
+        this.arrs = [];
+        for (var i = 0; i < this.ruleList.length; i++) {
+          if (this.ruleList[i].real_name.indexOf(this.searchRule) !== -1) {
+            this.arrs.push(i);
+          }
+        }
+      } else {
+        this.arrs = [];
+      }
+    },
+    init() {
+      this.searchRule = '';
+      this.arrs = [];
+    },
+    handleCreate1(val) {
+      this.headerOptionsList.push({
+        value: val,
+        label: val,
+      });
+    },
+    // 获取新增表单
+    getAddFrom() {
+      addMenus()
+        .then(async (res) => {
+          this.FromData = res.data.rules;
+        })
+        .catch((res) => {
+          this.$Message.error(res.msg);
+        });
+    },
+    iconClick() {
+      this.modal12 = true;
+    },
+    iconChange(n) {
+      this.formValidate.icon = n;
+      this.modal12 = false;
+    },
+    // 提交
+    handleSubmit(name) {
+      //判断是否选择父级分类
+      if (this.formValidate.path) {
+        let length = this.formValidate.path.length;
+        this.formValidate.pid = this.formValidate.path[length - 1] || 0;
+      }
+      let data = {
+        url: this.formValidate.id ? `/setting/menus/${this.formValidate.id}` : '/setting/menus',
+        method: this.formValidate.id ? 'put' : 'post',
+        datas: this.formValidate,
+      };
+      if (this.authType) {
+        if (!this.formValidate.menu_name) {
+          return this.$Message.warning('请填写按钮名称');
+        }
+        if (!this.formValidate.menu_path) {
+          return this.$Message.warning('请填写路由地址');
+        }
+      } else {
+        if (!this.formValidate.menu_name) {
+          return this.$Message.warning('请填写接口名称');
+        }
+        if (!this.formValidate.methods) {
+          return this.$Message.warning('请选择请求方式');
+        }
+        if (!this.formValidate.api_url) {
+          return this.$Message.warning('请选择接口地址');
+        }
+      }
+      this.valids = true;
+      addMenusApi(data)
+        .then(async (res) => {
+          this.$Message.success(res.msg);
+          this.modals = false;
+          this.$emit('changeMenu');
+          this.getAddFrom();
+          // this.$store.dispatch('menus/getMenusNavList');
+        })
+        .catch((res) => {
+          this.valids = false;
+          this.$Message.error(res.msg);
+        });
+    },
+    handleReset() {
+      this.modals = false;
+      this.authType = true;
+      this.$refs['formValidate'].resetFields();
+      this.$emit('clearFrom');
+    },
+  },
+  created() {
+    this.list = this.search;
+    this.getAddFrom();
+  },
+};
+</script>
+
+<style scoped>
+.trees-coadd {
+  width: 100%;
+  height: 500px;
+  border-radius: 4px;
+  overflow: hidden;
+}
+
+.scollhide {
+  width: 100%;
+  height: 100%;
+  overflow: auto;
+  margin-left: 18px;
+  padding: 10px 0 10px 0;
+  box-sizing: border-box;
+}
+
+.content {
+  font-size: 12px;
+}
+
+.time {
+  font-size: 12px;
+  color: #2d8cf0;
+}
+
+.icons-item {
+  float: left;
+  margin: 6px 6px 6px 0;
+  width: 53px;
+  text-align: center;
+  list-style: none;
+  cursor: pointer;
+  height: 50px;
+  color: #5c6b77;
+  transition: all 0.2s ease;
+  position: relative;
+  padding-top: 10px;
+}
+
+.icons-item .ivu-icon {
+  font-size: 16px;
+}
+
+.search-rule {
+  display: flex;
+  align-items: center;
+  padding: 10px;
+  background-color: #f2f2f2;
+}
+
+.rule {
+  display: flex;
+  flex-wrap: wrap;
+  max-height: 700px;
+  overflow: scroll;
+}
+
+/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
+.rule::-webkit-scrollbar {
+  width: 10px;
+  height: 10px;
+  background-color: #f5f5f5;
+}
+
+/*定义滚动条轨道 内阴影+圆角*/
+.rule::-webkit-scrollbar-track {
+  border-radius: 4px;
+  background-color: #f5f5f5;
+}
+
+/*定义滑块 内阴影+圆角*/
+.rule::-webkit-scrollbar-thumb {
+  border-radius: 4px;
+  background-color: #555;
+}
+
+.rule-list {
+  background-color: #f8f5f5;
+  width: 32%;
+  margin: 5px;
+  border-radius: 3px;
+  padding: 10px;
+  color: #333;
+  cursor: pointer;
+  transition: all 0.1s;
+}
+
+.rule-list:hover {
+  background-color: #c5d1dd;
+}
+
+.rule-list div {
+  white-space: nowrap;
+}
+
+.select-rule {
+  background-color: #c5d1dd;
+}
+
+.add {
+  display: flex;
+  align-items: center;
+}
+
+.df {
+  display: flex;
+  justify-content: center;
+}
+</style>

+ 561 - 0
template/admin/src/pages/system/systemMenus/index.vue

@@ -0,0 +1,561 @@
+<template>
+  <div>
+    <Card :bordered="false" dis-hover class="ivu-mt">
+      <Form
+        ref="roleData"
+        :model="roleData"
+        :label-width="labelWidth"
+        :label-position="labelPosition"
+        @submit.native.prevent
+      >
+        <Row type="flex" :gutter="24">
+          <Col v-bind="grid">
+            <FormItem label="规则状态:">
+              <Select v-model="roleData.is_show" placeholder="请选择" clearable @on-change="getData">
+                <Option value="1">显示</Option>
+                <Option value="0">不显示</Option>
+              </Select>
+            </FormItem>
+          </Col>
+          <Col v-bind="grid">
+            <FormItem label="按钮名称:" prop="status2" label-for="status2">
+              <Input v-model="roleData.keyword" search enter-button placeholder="请输入按钮名称" @on-search="getData" />
+            </FormItem>
+          </Col>
+        </Row>
+        <Row type="flex">
+          <Col v-bind="grid">
+            <Button type="primary" @click="menusAdd('添加规则')" icon="md-add">添加规则 </Button>
+          </Col>
+        </Row>
+      </Form>
+      <vxe-table
+        :border="false"
+        class="vxeTable mt25"
+        highlight-hover-row
+        highlight-current-row
+        :loading="loading"
+        ref="xTable"
+        header-row-class-name="false"
+        :tree-config="tabconfig"
+        :data="tableData"
+        row-id="id"
+      >
+        <vxe-table-column field="menu_name" tree-node title="按钮名称" min-width="100"></vxe-table-column>
+        <vxe-table-column field="unique_auth" title="前端权限" min-width="200"></vxe-table-column>
+        <vxe-table-column field="menu_path" title="路由" min-width="240" tooltip="true">
+          <template v-slot="{ row }">
+            <span v-if="row.auth_type == 1">页面:{{ row.menu_path }}</span>
+            <span v-if="row.auth_type == 2">接口:[{{ row.methods }}]{{ row.api_url }}</span>
+          </template>
+        </vxe-table-column>
+        <vxe-table-column field="flag" title="规则状态" min-width="120">
+          <template v-slot="{ row }">
+            <i-switch
+              v-model="row.is_show"
+              :value="row.is_show"
+              :true-value="1"
+              :false-value="0"
+              @on-change="onchangeIsShow(row)"
+              size="large"
+            >
+              <span slot="open">开启</span>
+              <span slot="close">关闭</span>
+            </i-switch>
+          </template>
+        </vxe-table-column>
+        <vxe-table-column field="date" title="操作" align="right" width="250" fixed="right">
+          <template v-slot="{ row }">
+            <span>
+              <a @click="addRoute(row)" v-if="row.auth_type === 1">添加权限</a>
+              <Divider type="vertical" v-if="row.auth_type === 1" />
+              <a @click="addE(row, '添加子菜单')" v-if="row.auth_type === 1">添加子菜单</a>
+              <!-- <a @click="addE(row, '添加规则')" v-else>添加规则</a> -->
+            </span>
+            <Divider type="vertical" v-if="row.auth_type === 1" />
+            <a @click="edit(row, '编辑')">编辑</a>
+            <Divider type="vertical" />
+            <a @click="del(row, '删除规则')">删除</a>
+          </template>
+        </vxe-table-column>
+      </vxe-table>
+    </Card>
+    <menus-from
+      :formValidate="formValidate"
+      :titleFrom="titleFrom"
+      @getList="getList"
+      @changeMenu="getMenusUnique"
+      ref="menusFrom"
+      @clearFrom="clearFrom"
+    ></menus-from>
+    <Modal
+      v-model="ruleModal"
+      scrollable
+      width="1100"
+      title="权限列表"
+      @on-ok="addRouters"
+      @on-cancel="ruleModal = false"
+      @on-visible-change="modalchange"
+    >
+      <div class="search-rule">
+        <Alert
+          >基础接口,可多选,并且添加后不会再展示出现;删除权限后才会出现;公共接口,可多选,并且添加后会继续展示;</Alert
+        >
+        <Input
+          class="mr10"
+          v-model="searchRule"
+          placeholder="输入关键词搜索"
+          clearable
+          style="width: 300px"
+          ref="search"
+          @on-enter="searchRules"
+          @on-clear="searchRules"
+        />
+        <Button class="mr10" type="primary" @click="searchRules">搜索</Button>
+        <Button @click="init">重置</Button>
+      </div>
+      <div class="route-list">
+        <div class="tree">
+          <el-tree
+            ref="treeBox"
+            :data="ruleCateList"
+            :highlight-current="true"
+            :props="defaultProps"
+            node-key="id"
+            :default-expanded-keys="expandedKeys"
+            :current-node-key="nodeKey"
+            @node-click="handleNodeClick"
+          ></el-tree>
+        </div>
+        <div class="rule">
+          <div
+            class="rule-list"
+            v-show="!arrs.length || arrs.includes(item.id)"
+            :class="{ 'select-rule': seletRouteIds.includes(item.id) }"
+            v-for="(item, index) in children"
+            :key="index"
+            @click="selectRule(item)"
+          >
+            <div>接口名称:{{ item.name }}</div>
+            <div>请求方式:{{ item.method }}</div>
+            <div>接口地址:{{ item.path }}</div>
+          </div>
+        </div>
+      </div>
+      <!-- <Tabs v-model="routeType" @on-click="changTab">
+        <TabPane :label="item.name" :name="'' + index" v-for="(item, index) in foundationList" :key="item"></TabPane>
+      </Tabs> -->
+    </Modal>
+  </div>
+</template>
+
+<script>
+import { mapState } from 'vuex';
+import {
+  getTable,
+  menusDetailsApi,
+  isShowApi,
+  editMenus,
+  getRuleList,
+  menusBatch,
+  getMenusUnique,
+  menusRuleCate,
+} from '@/api/systemMenus';
+import formCreate from '@form-create/iview';
+import menusFrom from './components/menusFrom';
+import { formatFlatteningRoutes, findFirstNonNullChildren, findFirstNonNullChildrenKeys } from '@/libs/system';
+
+export default {
+  name: 'systemMenus',
+  data() {
+    return {
+      children: [],
+      expandedKeys: [],
+      tabconfig: { children: 'children', reserve: true, accordion: true },
+      spinShow: false,
+      ruleModal: false,
+      searchRule: '',
+      grid: {
+        xl: 7,
+        lg: 7,
+        md: 12,
+        sm: 24,
+        xs: 24,
+      },
+      roleData: {
+        is_show: '',
+        keyword: '',
+      },
+      defaultProps: {
+        children: 'children',
+        label: 'name',
+      },
+      ruleCateList: [], //权限树
+      loading: false,
+      tableData: [],
+      FromData: null,
+      icons: '',
+      formValidate: {},
+      titleFrom: '',
+      modalTitleSs: '',
+      routeType: '0',
+      arrs: [],
+      foundationList: [], // 基础接口列表
+      openList: [], // 公开接口列表
+      seletRoute: [], // 选中路由
+      seletRouteIds: [], // 选中id
+      menusId: 0, // 选中分类id
+      nodeKey: 0, // 选中节点
+    };
+  },
+  components: { menusFrom, formCreate: formCreate.$form() },
+  computed: {
+    ...mapState('admin/layout', ['isMobile']),
+    labelWidth() {
+      return this.isMobile ? undefined : 75;
+    },
+    labelPosition() {
+      return this.isMobile ? 'top' : 'right';
+    },
+  },
+  mounted() {
+    this.getData();
+  },
+  methods: {
+    init() {
+      this.searchRule = '';
+      this.searchRules();
+    },
+    addRouters() {
+      let data = {
+        menus: this.seletRoute,
+      };
+      menusBatch(data)
+        .then((res) => {
+          this.getData();
+        })
+        .catch((res) => {
+          this.$Message.error(res.msg);
+        });
+    },
+    selectRule(data) {
+      if (this.seletRouteIds.includes(data.id)) {
+        let i = this.seletRouteIds.findIndex((e) => e == data.id);
+        this.seletRouteIds.splice(i, 1);
+        this.seletRoute.splice(i, 1);
+      } else {
+        this.seletRouteIds.push(data.id);
+        this.seletRoute.push({
+          menu_name: data.name,
+          unique_auth: '',
+          api_url: data.path,
+          path: this.menusId,
+          method: data.method,
+        });
+      }
+    },
+    changTab(name) {
+      this.routeType = name;
+      let index = parseInt(name);
+      this.children = this.foundationList[index] ? this.foundationList[index].children : [];
+      this.searchRules();
+    },
+    // 搜索规则
+    searchRules() {
+      if (this.searchRule.trim()) {
+        this.arrs = [];
+        let arr = this.foundationList;
+        for (var i = 0; i < arr.length; i++) {
+          if (arr[i].name.indexOf(this.searchRule) !== -1) {
+            this.arrs.push(arr[i].id);
+          }
+        }
+      } else {
+        this.arrs = [];
+      }
+    },
+    addRoute(row) {
+      this.menusId = row.id;
+      this.routeType = '0';
+      // this.getRuleList();
+      menusRuleCate().then((res) => {
+        this.ruleCateList = res.data;
+        this.ruleModal = true;
+        if (res.data.length) {
+          this.$nextTick((e) => {
+            this.expandedKeys = findFirstNonNullChildrenKeys(res.data[0], []);
+            this.nodeKey = findFirstNonNullChildren(res.data).id;
+            this.$refs.treeBox.setCurrentKey(this.nodeKey);
+            this.getRuleList(this.nodeKey);
+          });
+        }
+      });
+    },
+    handleNodeClick(data) {
+      this.getRuleList(data.id);
+    },
+    modalchange() {
+      this.seletRouteIds = [];
+      this.seletRoute = [];
+    },
+    // 获取权限列表
+    getRuleList(cate_id) {
+      getRuleList(cate_id).then((res) => {
+        this.foundationList = res.data;
+        this.children = res.data;
+        this.searchRules();
+
+        // this.openList = [];
+        // this.seletRouteIds = [];
+        // this.seletRoute = [];
+      });
+    },
+    // 修改规则状态
+    onchangeIsShow(row) {
+      let data = {
+        id: row.id,
+        is_show: row.is_show,
+      };
+      isShowApi(data)
+        .then(async (res) => {
+          this.$Message.success(res.msg);
+          this.$store.dispatch('menus/getMenusNavList');
+        })
+        .catch((res) => {
+          this.$Message.error(res.msg);
+        });
+    },
+    // 请求列表
+    getList() {
+      this.formValidate = Object.assign({}, this.$options.data().formValidate);
+      this.getData();
+    },
+
+    // 清除表单数据
+    clearFrom() {
+      this.formValidate = Object.assign({}, this.$options.data().formValidate);
+    },
+    // 添加子菜单
+    addE(row, title) {
+      this.formValidate = {};
+      let pid = row.id.toString();
+      if (pid) {
+        menusDetailsApi(row.id)
+          .then(async (res) => {
+            this.formValidate.path = res.data.path;
+            this.formValidate.path.push(row.id);
+            this.formValidate.pid = pid;
+            this.$refs.menusFrom.modals = true;
+            this.$refs.menusFrom.valids = false;
+            this.titleFrom = title;
+            this.formValidate.auth_type = 1;
+            this.formValidate.is_show = 0;
+            this.formValidate.is_show_path = 0;
+          })
+          .catch((res) => {
+            this.$Message.error(res.msg);
+          });
+      } else {
+        this.formValidate.pid = pid;
+        this.$refs.menusFrom.modals = true;
+        this.$refs.menusFrom.valids = false;
+        this.titleFrom = title;
+        this.formValidate.auth_type = 1;
+        this.formValidate.is_show = 0;
+        this.formValidate.is_show_path = 0;
+      }
+      // this.formValidate.pid = row.id.toString();
+      // this.$refs.menusFrom.modals = true;
+      // this.$refs.menusFrom.valids = false;
+      // this.titleFrom = title;
+      // this.formValidate.auth_type = 1;
+      // this.formValidate.is_show = '0';
+    },
+    // 删除
+    del(row, tit) {
+      let delfromData = {
+        title: tit,
+        url: `/setting/menus/${row.id}`,
+        method: 'DELETE',
+        ids: '',
+      };
+
+      this.$modalSure(delfromData)
+        .then((res) => {
+          this.$Message.success(res.msg);
+          this.getData();
+          this.getMenusUnique();
+          // this.$store.dispatch('menus/getMenusNavList');
+        })
+        .catch((res) => {
+          this.$Message.error(res.msg);
+        });
+    },
+    // 规则详情
+    menusDetails(id) {
+      menusDetailsApi(id)
+        .then(async (res) => {
+          this.formValidate = res.data;
+          this.$refs.menusFrom.modals = true;
+        })
+        .catch((res) => {
+          this.$Message.error(res.msg);
+        });
+    },
+    // 编辑
+    edit(row, title, index) {
+      this.formValidate = {};
+      this.menusDetails(row.id);
+      this.titleFrom = title;
+      this.$refs.menusFrom.valids = false;
+      this.$refs.menusFrom.getAddFrom(row.id);
+    },
+    // 添加
+    menusAdd(title) {
+      this.formValidate = {};
+      this.$refs.menusFrom.modals = true;
+      this.$refs.menusFrom.valids = false;
+      // this.formValidate = Object.assign(this.$data, this.$options.formValidate());
+      this.titleFrom = title;
+      this.formValidate.auth_type = 1;
+      this.formValidate.is_show = 0;
+      this.formValidate.is_show_path = 0;
+    },
+    // 新增页面表单
+    // getAddFrom () {
+    //     this.spinShow = true;
+    //     addMenus(this.roleData).then(async res => {
+    //         this.FromData = res.data;
+    //         this.$refs.menusFrom.modals = true;
+    //         this.spinShow = false;
+    //     }).catch(res => {
+    //         this.spinShow = false;
+    //         this.$Message.error(res.msg);
+    //     })
+    // },
+    // 列表
+    getData() {
+      this.loading = true;
+      this.roleData.is_show = this.roleData.is_show || '';
+      getTable(this.roleData)
+        .then(async (res) => {
+          this.tableData = res.data;
+          this.loading = false;
+        })
+        .catch((res) => {
+          this.loading = false;
+          this.$Message.error(res.msg);
+        });
+    },
+    getMenusUnique() {
+      getMenusUnique().then((res) => {
+        let data = res.data;
+        this.$store.commit('userInfo/uniqueAuth', data.uniqueAuth);
+        this.$store.commit('menus/getmenusNav', data.menus);
+        this.$store.dispatch('routesList/setRoutesList', data.menus);
+        let arr = formatFlatteningRoutes(this.$router.options.routes);
+        this.formatTwoStageRoutes(arr);
+        let routes = formatFlatteningRoutes(data.menus);
+        this.$store.commit('menus/setOneLvRoute', routes);
+        this.bus.$emit('routesListChange');
+      });
+    },
+    formatTwoStageRoutes(arr) {
+      if (arr.length <= 0) return false;
+      const newArr = [];
+      const cacheList = [];
+      arr.forEach((v) => {
+        if (v && v.meta && v.meta.keepAlive) {
+          newArr.push({ ...v });
+          cacheList.push(v.name);
+          this.$store.dispatch('keepAliveNames/setCacheKeepAlive', cacheList);
+        }
+      });
+      return newArr;
+    },
+    // 关闭按钮
+    cancel() {
+      this.$emit('onCancel');
+    },
+  },
+};
+</script>
+
+<style scoped lang="scss">
+.vxeTable {
+  > .vxe-table--header-wrapper {
+    background: #fff !important;
+  }
+  .icon {
+    font-size: 20px;
+  }
+}
+
+.rule {
+  display: flex;
+  flex-wrap: wrap;
+  overflow-y: scroll;
+  height: max-content;
+  max-height: 600px;
+  flex: 1;
+}
+.tree::-webkit-scrollbar {
+  width: 2px;
+  background-color: #f5f5f5;
+}
+/*定义滚动条高宽及背景 高宽分别对应横竖滚动条的尺寸*/
+.rule::-webkit-scrollbar {
+  width: 10px;
+  height: 10px;
+  background-color: #f5f5f5;
+}
+
+/*定义滚动条轨道 内阴影+圆角*/
+.rule::-webkit-scrollbar-track {
+  border-radius: 4px;
+  background-color: #f5f5f5;
+}
+
+/*定义滑块 内阴影+圆角*/
+.rule::-webkit-scrollbar-thumb {
+  border-radius: 4px;
+  background-color: #ccc;
+}
+
+.rule-list {
+  background-color: #f2f2f2;
+  width: 48.5%;
+  height: max-content;
+  margin: 5px;
+  border-radius: 3px;
+  padding: 10px;
+  color: #333;
+  cursor: pointer;
+  transition: all 0.1s;
+}
+
+.rule-list:hover {
+  background-color: #badbfb;
+}
+
+.rule-list div {
+  white-space: nowrap;
+}
+
+.select-rule {
+  background-color: #badbfb;
+}
+.route-list {
+  display: flex;
+  margin-top: 10px;
+
+  .tree {
+    width: 200px;
+    overflow-y: scroll;
+    max-height: 600px;
+    /deep/ .el-tree-node__children .el-tree-node .el-tree-node__content {
+      padding-left: 14px !important;
+    }
+  }
+}
+</style>

+ 1 - 24
template/admin/src/router/modules/index.js

@@ -12,29 +12,6 @@ import LayoutMain from '@/layout';
 import setting from '@/setting';
 let routePre = setting.routePre;
 
-// export default {
-//   path: '/',
-//   name: 'home',
-//   redirect: '/admin/home',
-//   component: LayoutMain,
-//   meta: {
-//     hideInMenu: true,
-//     notCache: true,
-//     auth: true
-//   },
-//   children: [
-//     {
-//       path: 'admin/home',
-//       name: 'home',
-//       meta: {
-//         title: '首页',
-//         auth: ['admin-index-index']
-//       },
-//       component: () => import('@/pages/index/index')
-//     }
-//   ]
-// }
-
 const meta = {
   auth: true,
 };
@@ -52,7 +29,7 @@ export default {
   component: LayoutMain,
   children: [
     {
-      path: routePre + '/home_page',
+      path: routePre + '/index',
       name: `${pre}index`,
       header: 'home',
       meta: {

+ 9 - 0
template/admin/src/router/modules/system.js

@@ -190,5 +190,14 @@ export default {
       },
       component: () => import('@/pages/system/crontab/index'),
     },
+    {
+      path: 'system_menus/index',
+      name: `${pre}systemMenus`,
+      meta: {
+        auth: ['system-system-menus'],
+        title: '权限规则',
+      },
+      component: () => import('@/pages/system/systemMenus/index'),
+    },
   ],
 };

+ 1 - 1
template/admin/src/theme/element.scss

@@ -190,7 +190,7 @@
 }
 .columns-round {
   .el-menu-item {
-    margin: 0 5px 5px 5px;
+    margin: 5px 5px 0px 5px;
     border-radius: 5px;
   }
   .el-submenu {