Przeglądaj źródła

Merge remote-tracking branch 'origin/dev' into dev

zhangyihao 7 miesięcy temu
rodzic
commit
a9d29bfb2a

+ 111 - 0
src/api/globalMap/layerConfig/index.ts

@@ -0,0 +1,111 @@
+import request from '@/utils/request';
+import { AxiosPromise } from 'axios';
+import { MenuQuery, MenuVO, MenuForm, MenuTreeOption, RoleMenuTree } from './types';
+
+// 查询菜单列表
+export const listMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
+  return request({
+    url: '/api/system/menu/list',
+    method: 'get',
+    params: query
+  });
+};
+
+// 查询图层 列表
+export const LayerlistMenu = (query?: MenuQuery): AxiosPromise<MenuVO[]> => {
+  return request({
+    url: '/api/layerConfiguration/layer_list',
+    method: 'get',
+    params: query
+  });
+};
+
+// 查询菜单详细
+export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
+  return request({
+    url: '/api/system/menu/' + menuId,
+    method: 'get'
+  });
+};
+
+// 查询图层详细
+export const getLayerMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
+  return request({
+    url: '/api/layerConfiguration/' + menuId,
+    method: 'get'
+  });
+};
+// 查询菜单下拉树结构
+export const treeselect = (): AxiosPromise<MenuTreeOption[]> => {
+  return request({
+    url: '/api/system/menu/treeselect',
+    method: 'get'
+  });
+};
+
+// 根据角色ID查询菜单下拉树结构
+export const roleMenuTreeselect = (roleId: string | number): AxiosPromise<RoleMenuTree> => {
+  return request({
+    url: '/api/system/menu/roleMenuTreeselect/' + roleId,
+    method: 'get'
+  });
+};
+
+// 根据角色ID查询菜单下拉树结构
+export const tenantPackageMenuTreeselect = (packageId: string | number): AxiosPromise<RoleMenuTree> => {
+  return request({
+    url: '/api/system/menu/tenantPackageMenuTreeselect/' + packageId,
+    method: 'get'
+  });
+};
+
+// 新增菜单
+export const addMenu = (data: MenuForm) => {
+  return request({
+    url: '/api/system/menu/create',
+    method: 'post',
+    data: data
+  });
+};
+
+// 新增图层
+export const addLayerMenu = (data: MenuForm) => {
+  return request({
+    url: '/api/layerConfiguration/create',
+    method: 'post',
+    data: data
+  });
+};
+
+// 修改菜单
+export const updateMenu = (data: MenuForm) => {
+  return request({
+    url: '/api/system/menu',
+    method: 'put',
+    data: data
+  });
+};
+
+// 修改图层
+export const updateLayerMenu = (data: MenuForm) => {
+  return request({
+    url: '/api/layerConfiguration',
+    method: 'put',
+    data: data
+  });
+};
+// 删除菜单
+export const delMenu = (menuId: string | number) => {
+  return request({
+    url: '/api/system/menu/' + menuId,
+    method: 'delete'
+  });
+};
+
+// 删除图层
+export const delLayerMenu = (menuId: string | number) => {
+  return request({
+    url: '/api/layerConfiguration/' + menuId,
+    method: 'delete'
+  });
+};

+ 71 - 0
src/api/globalMap/layerConfig/types.ts

@@ -0,0 +1,71 @@
+import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
+
+/**
+ * 菜单树形结构类型
+ */
+export interface MenuTreeOption {
+  id: string | number;
+  label: string;
+  parentId: string | number;
+  weight: number;
+  children?: MenuTreeOption[];
+}
+
+export interface RoleMenuTree {
+  menus: MenuTreeOption[];
+  checkedKeys: string[];
+}
+
+/**
+ * 菜单查询参数类型
+ */
+export interface MenuQuery {
+  keywords?: string;
+  menuName?: string;
+  status?: string;
+  topicName?: string;
+}
+
+/**
+ * 菜单视图对象类型
+ */
+export interface MenuVO extends BaseEntity {
+  parentName: string;
+  parentId: string | number;
+  children: MenuVO[];
+  menuId: string | number;
+  menuName: string;
+  orderNum: number;
+  path: string;
+  component: string;
+  queryParam: string;
+  isFrame: string;
+  isCache: string;
+  menuType: MenuTypeEnum;
+  visible: string;
+  status: string;
+  icon: string;
+  remark: string;
+}
+
+export interface MenuForm {
+  parentName?: string;
+  parentId?: string | number;
+  children?: MenuForm[];
+  menuId?: string | number;
+  menuName: string;
+  orderNum: number;
+  path: string;
+  component?: string;
+  queryParam?: string;
+  isFrame?: string;
+  isCache?: string;
+  menuType?: MenuTypeEnum;
+  visible?: string;
+  status?: string;
+  icon?: string;
+  remark?: string;
+  query?: string;
+  perms?: string;
+  layer_template?: string;
+}

+ 7 - 0
src/api/system/menu/index.ts

@@ -27,6 +27,13 @@ export const getMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
   });
 };
 
+// 查询菜单详细
+export const getLayerMenu = (menuId: string | number): AxiosPromise<MenuVO> => {
+  return request({
+    url: '/api/system/layer_menu/' + menuId,
+    method: 'get'
+  });
+};
 // 查询菜单下拉树结构
 export const treeselect = (): AxiosPromise<MenuTreeOption[]> => {
   return request({

+ 0 - 4
src/types/auto-imports.d.ts

@@ -312,10 +312,6 @@ declare module 'vue' {
   interface GlobalComponents {}
   interface ComponentCustomProperties {
     readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
-    readonly ElLoading: UnwrapRef<typeof import('element-plus/es')['ElLoading']>
-    readonly ElMessage: UnwrapRef<typeof import('element-plus/es')['ElMessage']>
-    readonly ElMessageBox: UnwrapRef<typeof import('element-plus/es')['ElMessageBox']>
-    readonly ElNotification: UnwrapRef<typeof import('element-plus/es')['ElNotification']>
     readonly acceptHMRUpdate: UnwrapRef<typeof import('pinia')['acceptHMRUpdate']>
     readonly asyncComputed: UnwrapRef<typeof import('@vueuse/core')['asyncComputed']>
     readonly autoResetRef: UnwrapRef<typeof import('@vueuse/core')['autoResetRef']>

+ 0 - 29
src/types/components.d.ts

@@ -19,23 +19,13 @@ declare module 'vue' {
     Dialog: typeof import('./../components/Dialog/index.vue')['default']
     DictTag: typeof import('./../components/DictTag/index.vue')['default']
     Editor: typeof import('./../components/Editor/index.vue')['default']
-    ElAnchor: typeof import('element-plus/es')['ElAnchor']
-    ElAnchorLink: typeof import('element-plus/es')['ElAnchorLink']
     ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
     ElBadge: typeof import('element-plus/es')['ElBadge']
     ElButton: typeof import('element-plus/es')['ElButton']
-    ElCard: typeof import('element-plus/es')['ElCard']
     ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
-    ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
     ElCol: typeof import('element-plus/es')['ElCol']
-    ElCollapse: typeof import('element-plus/es')['ElCollapse']
-    ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
     ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
     ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
-    ElContainer: typeof import('element-plus/es')['ElContainer']
-    ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
-    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
-    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
     ElDialog: typeof import('element-plus/es')['ElDialog']
     ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDrawer: typeof import('element-plus/es')['ElDrawer']
@@ -45,45 +35,27 @@ declare module 'vue' {
     ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
-    ElHeader: typeof import('element-plus/es')['ElHeader']
     ElIcon: typeof import('element-plus/es')['ElIcon']
-    ElImage: typeof import('element-plus/es')['ElImage']
     ElInput: typeof import('element-plus/es')['ElInput']
     ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
     ElLink: typeof import('element-plus/es')['ElLink']
-    ElMain: typeof import('element-plus/es')['ElMain']
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
-    ElPagination: typeof import('element-plus/es')['ElPagination']
     ElPopover: typeof import('element-plus/es')['ElPopover']
     ElRadio: typeof import('element-plus/es')['ElRadio']
-    ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
     ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
-    ElSegmented: typeof import('element-plus/es')['ElSegmented']
     ElSelect: typeof import('element-plus/es')['ElSelect']
-    ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
-    ElSkeletonItem: typeof import('element-plus/es')['ElSkeletonItem']
-    ElSlider: typeof import('element-plus/es')['ElSlider']
-    ElSpace: typeof import('element-plus/es')['ElSpace']
-    ElStep: typeof import('element-plus/es')['ElStep']
-    ElSteps: typeof import('element-plus/es')['ElSteps']
     ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
     ElTable: typeof import('element-plus/es')['ElTable']
     ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
-    ElTabPane: typeof import('element-plus/es')['ElTabPane']
-    ElTabs: typeof import('element-plus/es')['ElTabs']
     ElTag: typeof import('element-plus/es')['ElTag']
     ElText: typeof import('element-plus/es')['ElText']
-    ElTimeline: typeof import('element-plus/es')['ElTimeline']
-    ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
     ElTooltip: typeof import('element-plus/es')['ElTooltip']
-    ElTree: typeof import('element-plus/es')['ElTree']
     ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
-    ElUpload: typeof import('element-plus/es')['ElUpload']
     ExcelEditor: typeof import('./../components/ExcelEditor/index.vue')['default']
     FileUpload: typeof import('./../components/FileUpload/index.vue')['default']
     FooterSection: typeof import('./../components/FooterSection/index.vue')['default']
@@ -96,7 +68,6 @@ declare module 'vue' {
     IconSelect: typeof import('./../components/IconSelect/index.vue')['default']
     IEpCaretBottom: typeof import('~icons/ep/caret-bottom')['default']
     IEpCaretTop: typeof import('~icons/ep/caret-top')['default']
-    IEpUploadFilled: typeof import('~icons/ep/upload-filled')['default']
     IFrame: typeof import('./../components/iFrame/index.vue')['default']
     ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default']
     ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default']

+ 448 - 0
src/views/globalMap/layerConfiguration.vue

@@ -0,0 +1,448 @@
+<template>
+  <div class="app-container">
+    <div v-show="!dialog.visible">
+      <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
+        <div v-show="showSearch">
+          <el-form ref="queryFormRef" :model="queryParams">
+            <el-row :gutter="20">
+              <!-- 第一行 -->
+              <el-col :span="6">
+                <el-form-item label="图层名称:" prop="menuName">
+                  <el-input v-model="queryParams.menuName" placeholder="请输入图层名称" clearable @keyup.enter="handleQuery" />
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="专题:" prop="status">
+                  <el-select v-model="queryParams.topicName" placeholder="请选择" clearable>
+                    <el-option v-for="dict in topic_name" :key="dict.value" :label="dict.label" :value="dict.value" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="状态:" prop="status">
+                  <el-select v-model="queryParams.status" placeholder="状态" clearable>
+                    <el-option v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.label" :value="dict.value" />
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item>
+                  <el-button type="primary" @click="handleQuery">搜索</el-button>
+                  <el-button @click="resetQuery">重置</el-button>
+                </el-form-item>
+              </el-col>
+            </el-row>
+          </el-form>
+        </div>
+      </transition>
+
+      <el-row :gutter="10" class="mb8">
+        <el-col :span="1.5">
+          <el-button v-hasPermi="['system:menu:add']" icon="Plus" type="primary" @click="handleAdd()">新增 </el-button>
+        </el-col>
+        <el-col :span="1.5">
+          <el-button type="danger" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button>
+        </el-col>
+        <!--      <right-toolbar v-model:showSearch="showSearch" @query-table="getList"></right-toolbar>-->
+      </el-row>
+
+      <el-table
+        ref="menuTableRef"
+        v-loading="loading"
+        :data="menuList"
+        row-key="menuId"
+        :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
+        :default-expand-all="isExpandAll"
+      >
+        <!--        <el-table-column type="selection" width="55"> </el-table-column>-->
+        <el-table-column prop="menuName" align="center" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
+        <!--        <el-table-column prop="menuType" align="center" label="菜单类型" :show-overflow-tooltip="true" width="160"></el-table-column>-->
+        <el-table-column prop="menuType" label="菜单类型" align="center" width="100">
+          <template #default="scope">
+            <dict-tag :options="menu_type" :value="scope.row.menuType" />
+          </template>
+        </el-table-column>
+        <el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
+        <el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
+        <el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
+        <el-table-column prop="status" label="状态" width="80">
+          <template #default="scope">
+            <dict-tag :options="sys_normal_disable" :value="scope.row.status" />
+          </template>
+        </el-table-column>
+        <el-table-column label="创建时间" align="center" prop="createTime">
+          <template #default="scope">
+            <span>{{ scope.row.createTime }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template #default="scope">
+            <el-text v-hasPermi="['system:menu:edit']" class="common-btn-text-primary" @click="handleUpdate(scope.row)">修改</el-text>
+            <el-text v-hasPermi="['system:menu:add']" class="common-btn-text-primary" @click="handleAdd(scope.row)">新增</el-text>
+            <el-text v-hasPermi="['system:menu:remove']" class="common-btn-text-danger" @click="handleDelete(scope.row)">删除</el-text>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <div v-show="dialog.visible" class="common-dialog">
+      <div class="common-dialog-content">
+        <div class="common-dialog-title-box">
+          <i class="common-dialog-title-icon" />
+          <div>{{ dialog.title }}</div>
+        </div>
+        <div class="common-dialog-box">
+          <el-form ref="menuFormRef" :model="form" :rules="rules" label-width="100px">
+            <el-col :span="24">
+              <el-form-item style="width: 500px" label="上级菜单:">
+                <el-tree-select
+                  v-model="form.parentId"
+                  :data="menuOptions"
+                  :props="{ value: 'menuId', label: 'menuName', children: 'children' }"
+                  value-key="menuId"
+                  placeholder="选择上级菜单"
+                  check-strictly
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="24">
+              <el-form-item label="菜单类型:" prop="menuType">
+                <el-radio-group v-model="form.menuType">
+                  <el-radio value="M">目录</el-radio>
+                  <el-radio value="C">菜单</el-radio>
+                  <el-radio value="F">按钮</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType !== 'F'" :span="24">
+              <el-form-item label="菜单图标:" prop="icon">
+                <!-- 图标选择器 -->
+                <icon-select v-model="form.icon" />
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType !== 'F'" :span="24">
+              <el-form-item label="菜单模板:" prop="template">
+                <!-- 模板选择器 -->
+                <el-select v-model="form.template" placeholder="请选择模板">
+                  <el-option v-for="item in templateOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="显示排序:" prop="orderNum">
+                <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType !== 'F'" :span="12">
+              <el-form-item>
+                <template #label>
+                  <span>
+                    <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon> </el-tooltip
+                    >是否外链:
+                  </span>
+                </template>
+                <el-radio-group v-model="form.isFrame">
+                  <el-radio label="0">是</el-radio>
+                  <el-radio label="1">否</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType !== 'F'" :span="12">
+              <el-form-item prop="path" style="width: 500px">
+                <template #label>
+                  <span>
+                    <el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    路由地址:
+                  </span>
+                </template>
+                <el-input v-model="form.path" placeholder="请输入路由地址" />
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType === 'C'" :span="12">
+              <el-form-item prop="component">
+                <template #label>
+                  <span>
+                    <el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    组件路径:
+                  </span>
+                </template>
+                <el-input v-model="form.component" placeholder="请输入组件路径" />
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType !== 'M'" :span="12">
+              <el-form-item>
+                <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
+                <template #label>
+                  <span>
+                    <el-tooltip content="控制器中定义的权限字符,如:@SaCheckPermission('system:user:list')" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    权限字符:
+                  </span>
+                </template>
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType === 'C'" :span="12">
+              <el-form-item>
+                <el-input v-model="form.queryParam" placeholder="请输入路由参数" maxlength="255" />
+                <template #label>
+                  <span>
+                    <el-tooltip content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`' placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    路由参数:
+                  </span>
+                </template>
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType === 'C'" :span="12">
+              <el-form-item>
+                <template #label>
+                  <span>
+                    <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    是否缓存:
+                  </span>
+                </template>
+                <el-radio-group v-model="form.isCache">
+                  <el-radio label="0">缓存</el-radio>
+                  <el-radio label="1">不缓存</el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col v-if="form.menuType !== 'F'" :span="12">
+              <el-form-item>
+                <template #label>
+                  <span>
+                    <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    显示状态:
+                  </span>
+                </template>
+                <el-radio-group v-model="form.visible">
+                  <el-radio v-for="dict in sys_show_hide" :key="dict.value" :label="dict.value">{{ dict.label }} </el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item>
+                <template #label>
+                  <span>
+                    <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
+                      <el-icon>
+                        <question-filled />
+                      </el-icon>
+                    </el-tooltip>
+                    菜单状态:
+                  </span>
+                </template>
+                <el-radio-group v-model="form.status">
+                  <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
+                    {{ dict.label }}
+                  </el-radio>
+                </el-radio-group>
+              </el-form-item>
+            </el-col>
+          </el-form>
+        </div>
+        <div class="common-dialog-footer" style="display: flex; justify-content: center">
+          <el-button type="primary" @click="submitForm">确 定</el-button>
+          <el-button @click="cancel">取 消</el-button>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup name="Menu" lang="ts">
+import {
+  addMenu,
+  addLayerMenu,
+  delMenu,
+  delLayerMenu,
+  getMenu,
+  getLayerMenu,
+  listMenu,
+  LayerlistMenu,
+  updateMenu,
+  updateLayerMenu
+} from '@/api/globalMap/layerConfig';
+import { MenuForm, MenuQuery, MenuVO } from '@/api/globalMap/layerConfig/types';
+import { MenuTypeEnum } from '@/enums/MenuTypeEnum';
+
+interface MenuOptionsType {
+  menuId: number;
+  menuName: string;
+  children: MenuOptionsType[] | undefined;
+}
+
+import { ref, onMounted } from 'vue';
+import { ElMessage } from 'element-plus';
+import { addInformation, getTemplateList } from '@/api/informationissue/informationissue';
+
+const templateOptions = ref([
+  { value: 'template1', label: '图层分析一' },
+  { value: 'template2', label: '图层分析二' },
+  { value: 'template3', label: '图层分析三' }
+  // 更多模板选项...
+]);
+
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const { sys_show_hide, sys_normal_disable, topic_name, menu_type } = toRefs<any>(
+  proxy?.useDict('sys_show_hide', 'sys_normal_disable', 'topic_name', 'menu_type')
+);
+const menuList = ref<MenuVO[]>([]);
+const loading = ref(true);
+const showSearch = ref(true);
+const menuOptions = ref<MenuOptionsType[]>([]);
+const isExpandAll = ref(false);
+
+const dialog = reactive<DialogOption>({
+  visible: false,
+  title: ''
+});
+
+const queryFormRef = ref<ElFormInstance>();
+const menuFormRef = ref<ElFormInstance>();
+const initFormData = {
+  path: '',
+  menuId: undefined,
+  parentId: 0,
+  menuName: '',
+  icon: '',
+  menuType: MenuTypeEnum.M,
+  orderNum: 1,
+  isFrame: '1',
+  isCache: '0',
+  visible: '0',
+  status: '0'
+};
+const data = reactive<PageData<MenuForm, MenuQuery>>({
+  form: { ...initFormData },
+  queryParams: {
+    menuName: undefined,
+    status: undefined
+  },
+  rules: {
+    menuName: [{ required: true, message: '菜单名称不能为空', trigger: 'blur' }],
+    orderNum: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }],
+    path: [{ required: true, message: '路由地址不能为空', trigger: 'blur' }]
+  }
+});
+
+const menuTableRef = ref<ElTableInstance>();
+
+const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data);
+/** 查询菜单列表 */
+const getList = async () => {
+  loading.value = true;
+  const res = await LayerlistMenu(queryParams.value);
+  const data = proxy?.handleTree<MenuVO>(res.data, 'menuId');
+  if (data) {
+    menuList.value = data;
+  }
+  loading.value = false;
+};
+/** 查询菜单下拉树结构 */
+const getTreeselect = async () => {
+  menuOptions.value = [];
+  const response = await LayerlistMenu();
+  const menu: MenuOptionsType = { menuId: 0, menuName: '主类目', children: [] };
+  menu.children = proxy?.handleTree<MenuOptionsType>(response.data, 'menuId');
+  menuOptions.value.push(menu);
+};
+/** 取消按钮 */
+const cancel = () => {
+  reset();
+  dialog.visible = false;
+};
+/** 表单重置 */
+const reset = () => {
+  form.value = { ...initFormData };
+  menuFormRef.value?.resetFields();
+};
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  getList();
+};
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value?.resetFields();
+  handleQuery();
+};
+/** 新增按钮操作 */
+const handleAdd = (row?: MenuVO) => {
+  reset();
+  getTreeselect();
+  row && row.menuId ? (form.value.parentId = row.menuId) : (form.value.parentId = 0);
+  dialog.visible = true;
+  dialog.title = '添加菜单';
+  // fullscreen: false;
+};
+/** 展开/折叠操作 */
+const handleToggleExpandAll = () => {
+  isExpandAll.value = !isExpandAll.value;
+  toggleExpandAll(menuList.value, isExpandAll.value);
+};
+/** 展开/折叠所有 */
+const toggleExpandAll = (data: MenuVO[], status: boolean) => {
+  data.forEach((item: MenuVO) => {
+    menuTableRef.value?.toggleRowExpansion(item, status);
+    if (item.children && item.children.length > 0) toggleExpandAll(item.children, status);
+  });
+};
+/** 修改按钮操作 */
+const handleUpdate = async (row: MenuVO) => {
+  reset();
+  await getTreeselect();
+  if (row.menuId) {
+    const { data } = await getLayerMenu(row.menuId);
+    form.value = data;
+  }
+  dialog.visible = true;
+  dialog.title = '修改菜单';
+};
+/** 提交按钮 */
+const submitForm = () => {
+  menuFormRef.value?.validate(async (valid: boolean) => {
+    if (valid) {
+      form.value.menuId ? await updateLayerMenu(form.value) : await addLayerMenu(form.value);
+      proxy?.$modal.msgSuccess('操作成功');
+      dialog.visible = false;
+      await getList();
+    }
+  });
+};
+/** 删除按钮操作 */
+const handleDelete = async (row: MenuVO) => {
+  await proxy?.$modal.confirm('是否确认删除名称为"' + row.menuName + '"的数据项?');
+  await delLayerMenu(row.menuId);
+  await getList();
+  proxy?.$modal.msgSuccess('删除成功');
+};
+
+onMounted(() => {
+  getList();
+});
+</script>