Selaa lähdekoodia

可视化布局样式

wxy 3 kuukautta sitten
vanhempi
sitoutus
05dbba6de3

+ 0 - 12
src/components/Form/index.vue

@@ -1,12 +0,0 @@
-<template>
-  <div>
-      
-  </div>
-</template>
-
-<script setup>
-import { ref, onMounted, toRefs } from 'vue';
-</script>
-<style scoped>
-
-</style>

+ 2 - 2
src/components/Layout/components/index.js

@@ -1,6 +1,6 @@
-import blendLeft from './layout/blendLeft.vue'
+import main from './layout/main.vue'
 
 
 export default {
-  blendLeft
+  main
 }

+ 117 - 129
src/components/Layout/components/layout/blendLeft.vue

@@ -1,87 +1,59 @@
 <template>
   <a-layout style="min-height: 100vh">
-    <a-layout-sider theme="dark" :width="160" :collapsed-width="48" collapsible :trigger="null"
-      @collapse="evMenuSecondLongShow" v-model:collapsed="menuSecondLongShow">
-      <div class="logo-layout">
+    <a-layout-sider theme="dark" :width="260" :collapsed-width="48" collapsible :trigger="null"
+      @collapse="evMenuSecondLongShow" v-model:collapsed="menuSecondLongShow" class="menus">
+      <div class="logo-layout" v-if="LayoutStore.setting.SidebarLogo">
         <div class="logo">
-          <img src="@/assets/images/logo.png" style="height: 26px; width: 26px;" />
-          <h3 v-if="!menuSecondLongShow" style="margin-left: 10px;"> Easy life</h3>
+          <img src="@/assets/images/logo.png" />
         </div>
+        <h3>Esay life</h3>
       </div>
 
-      <a-menu v-model:selectedKeys="menuTabSate" theme="dark" mode="inline" @mouseleave="evMouseleaveMenu">
-        <a-menu-item v-for="routeItem in routesData" :key="routeItem.name"
-          @mouseenter="evMenuGetFn(routeItem, 'mouseenter')" @click="changeRoutesItems(routeItem)">
+      <a-menu v-model:selectedKeys="selectedKeys" @subMenuClick="sublevel" :open-keys="openKeys">
+        <a-sub-menu v-for="routeItem in routesData" :key="routeItem.name">
           <template #icon>
             <svg-icon :icon="routeItem.meta.icon"></svg-icon>
           </template>
-          <span>{{ routeItem.meta.title }}</span>
-        </a-menu-item>
+          <template #title>
+            <div @click="evGoPage(routeItem)">{{ routeItem.meta.title }}</div>
+          </template>
+          <!-- 二级路由 -->
+          <template v-if="routeItem?.children && routeItem?.children.length > 0">
+            <a-menu-item v-for="(item) in routeItem?.children" :key="item.name" @click.stop="evGoPage(item)">
+              {{ item.meta.title }}
+              <!-- 三级菜单 -->
+              <template v-if="item.children && item.children.length > 0">
+                <sub-menu :key="routeItem.name" :menu-info="item" @go="evGoPage" />
+              </template>
+            </a-menu-item>
+          </template>
+          <!-- 折叠图标 -->
+          <template #expand-icon-down>
+            <icon-down v-if="routeItem?.children && routeItem?.children.length > 0"></icon-down>
+          </template>
+        </a-sub-menu>
       </a-menu>
     </a-layout-sider>
-
-    <a-layout>
-      <a-layout-header class="layout-header">
-        <LayoutHeader />
-      </a-layout-header>
-      <a-layout>
-        <a-layout-sider id="layout-sider" :collapsed="false" :width="menuSecondData &&
-          menuSecondData.children &&
-          menuSecondData.children.length > 0
-          ? 160
-          : 0
-          " @mouseleave="evMouseLeavesSubMenu">
-          <a-menu :selectedKeys="routeItemSelectedKeys" id="layout-sider" theme="light" mode="vertical"
-            :collapsed="false" :auto-open="true">
-            <template v-for="routeItem in menuSecondData?.children || []">
-              <a-menu-item v-if="!routeItem.children || routeItem.children.length === 0"
-                :key="routeItem.name?.toString()" @click="evGoPage(routeItem)">
-                <span class="menu-level-font">{{ routeItem.meta.title }}</span>
-              </a-menu-item>
-
-              <template v-else-if="routeItem.children && routeItem.children.length > 0">
-                <sub-menu :key="routeItem.name" :menu-info="routeItem" @go="evGoPage" />
-              </template>
-            </template>
-          </a-menu>
-        </a-layout-sider>
-
-        <main class="layout-content-main">
-          <router-view v-slot="{ Component }" class="layout-content-router">
-            <component :is="Component" key="Layout" />
-          </router-view>
-        </main>
-      </a-layout>
-    </a-layout>
   </a-layout>
 </template>
 <script setup>
-import { ref, computed, h, reactive, onMounted, watch, nextTick } from "vue";
+import { onMounted, ref, watch } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { useSystemStore } from "@/store/modules/systemStore";
-import LayoutHeader from "@/components/Layout/components/layoutHeader/index.vue";
 import subMenu from "@/components/Layout/components/subMenu/index.vue";
-import {RouterTagData} from '@/store/modules/routerTag.js'
-
-// 标签页仓库
-const settingStore = RouterTagData()
-
+import { RouterTagData } from '@/store/modules/routerTag.js'
+import { layoutSetting } from '@/store/modules/layoutSetting'
+const LayoutStore = layoutSetting()
+const routerTagData = RouterTagData()
 const systemStore = useSystemStore();
-
 const route = useRoute();
 const router = useRouter();
-
+const selectedKeys = ref()
+const openKeys = ref([])
 const menuSecondLongShow = ref(systemStore.menuSecondLongShow);
 // 主菜单
 const routesData = router.options.routes[0]?.children || [];
 
-const menuTabSate = ref([systemStore.getMenuTabSate || routesData[0].name]);
-//子菜单
-const menuSecondData = ref(systemStore.getRouteItem);
-// 选中的状态路由列表
-const menuSecondSelectedStatusData = ref(systemStore.getRouteItem);
-const routeItemSelectedKeys = ref();
-
 const evMenuSecondLongShow = () => {
   systemStore.setStateValue({
     key: "menuSecondLongShow",
@@ -89,23 +61,6 @@ const evMenuSecondLongShow = () => {
     localStorage: true,
   });
 };
-const evMouseleaveMenu = (e) => {
-  if (
-    e.relatedTarget?.offsetParent?.id &&
-    e.relatedTarget?.offsetParent?.id == "layout-sider"
-  )
-    return;
-  menuSecondData.value = menuSecondSelectedStatusData.value;
-};
-// 子菜单
-const evMouseLeavesSubMenu = () => {
-  menuSecondData.value = menuSecondSelectedStatusData.value;
-};
-// 鼠标滑动上去更新菜单栏 目前先屏蔽
-const evMenuGetFn = (routeItem, type) => {
-  // menuSecondData.value = routeItem;
-  // console.log(routeItem);
-};
 
 // 查找最下级
 const findPath = (data) => {
@@ -116,55 +71,96 @@ const findPath = (data) => {
   }
 };
 
-// 主路由 状态
-const changeRoutesItems = (e) => {
-  const item = findPath(e);
-  evGoPage(item);
-};
-
 // 跳转路由
 const evGoPage = async (routeItem) => {
-  // settingStore.tagsPushData(routeItem);
-  await router.push({
-    name: routeItem.name,
-  });
-  menuTabSate.value = [route.matched[1].name];
+  selectedKeys.value = [routeItem.name]
+  openKeys.value = [routeItem.name]
+  if (!routeItem.children) {
+    await router.push({
+      path: routeItem.path,
+    });
+    if (LayoutStore.setting.HeadNavigationBar) {
+      routerTagData.tagsPushData(routeItem)
+      routerTagData.getDefault(routeItem.name)
+    }
+  }
+
   systemStore.setStateValue({
     key: "menuTabSate",
-    value: menuTabSate.value[0] || "",
+    value: routeItem.name || "",
     localStorage: true,
   });
 
-  menuSecondSelectedStatusData.value = routesData.find(
-    (item) => item.name == menuTabSate.value
-  );
-  menuSecondData.value = menuSecondSelectedStatusData.value;
   systemStore.setStateValue({
     key: "routeItem",
-    value: JSON.stringify(menuSecondData.value),
+    value: JSON.stringify(routeItem),
     localStorage: true,
   });
 };
 
-watch(
-  route,
-  (val) => {
-    if (val.name) {
-      routeItemSelectedKeys.value = [val.name];
-    }
-  },
-  { immediate: true }
-);
+watch(route, val => {
+  if (val) {
+    selectedKeys.value = [val.name]
+    routesData.forEach(res => {
+      if (res.children && res.children.length > 0) {
+        res.children.forEach(item => {
+          if (item.name == val.name) {
+            openKeys.value = [res.name];
+          }
+        })
+      }
+    })
+  }
+}, { immediate: true })
+
+
 </script>
 <style scoped lang="less">
-.layout-header {
-  height: 48px;
-  background: @bg_color_2;
-  padding-inline: 10px;
-  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
-  z-index: 9;
+.menus {
+  background-color: #0b3d7f !important;
+
+  ::v-deep .arco-menu-inner {
+    background-color: #0b3d7f;
+
+    .arco-menu-has-icon {
+      background: #0b3d7f;
+      color: #fff;
+
+      .arco-menu-icon {
+        color: #fff;
+      }
+
+      .arco-menu-icon-suffix {
+        svg {
+          color: #fff;
+        }
+      }
+    }
+
+    .arco-menu-item {
+      background-color: #0b3d7f;
+      color: #fff;
+    }
+
+    .arco-menu-inline-content {
+      .arco-menu-item-inner {
+        padding-left: 10px;
+      }
+
+      .arco-menu-selected {
+        background-color: #FFF;
+        color: #000;
+
+        .arco-icon {
+          color: #fff !important;
+        }
+      }
+
+    }
+  }
 }
 
+
 .ant-layout-content {
   height: auto;
   min-height: auto;
@@ -172,16 +168,7 @@ watch(
   padding: 0 20px;
 }
 
-.layout-content-main {
-  flex: 1;
-  padding: 0 1.2rem 1.6rem 1.2rem;
-  margin: 1.1rem 0 0rem 0;
-  overflow-y: auto;
-  overflow: overlay;
-  height: 95%;
-  display: flex;
-  flex-wrap: wrap;
-}
+
 
 .logo-layout {
   height: 64px;
@@ -191,16 +178,23 @@ watch(
   justify-content: center;
 
   .logo {
-    width: 100%;
-    height: 32px;
+    width: 36px;
+    height: 36px;
     display: flex;
     justify-content: center;
     align-items: center;
 
-    h3 {
-      white-space: nowrap;
+    img {
+      width: 100%;
+      height: 100%;
     }
   }
+
+  h3 {
+    color: #fff;
+    margin-left: 10px;
+  }
+
 }
 
 .arco-layout {
@@ -227,12 +221,6 @@ watch(
   background-color: @bg_color_3;
 }
 
-:deep(.layout-content-router) {
-  padding: 0 1rem 1rem 1rem;
-  background-color: @bg_color_4;
-  box-sizing: border-box;
-  width: 100%;
-}
 
 :deep(.arco-menu-inline-content) {
   .arco-menu-selected {

+ 73 - 0
src/components/Layout/components/layout/main.vue

@@ -0,0 +1,73 @@
+<template>
+  <a-layout style="height: 100vh;">
+    <a-layout>
+      <a-layout-sider :width="LayoutStore.setting.laoutStyle==0?260:340" >
+        <!-- 侧边栏 -->
+        <blendMenu v-if="LayoutStore.setting.laoutStyle==0"/>
+        <Menu v-if="LayoutStore.setting.laoutStyle==1"></Menu>
+      </a-layout-sider>
+      <a-layout-content>
+        <!-- 主体内容 -->
+        <a-layout style="height: 100vh;">
+          <!-- 头部 -->
+          <a-layout-header class="layout-header">
+            <LayoutHeader />
+          </a-layout-header>
+          <a-layout>
+            <a-layout-content class="layout-content">
+              <!-- 主题内容 -->
+              <main class="layout-content-main">
+                <router-view v-slot="{ Component }" class="layout-content-router">
+                  <component :is="Component" key="Layout" />
+                </router-view>
+              </main>
+            </a-layout-content>
+          </a-layout>
+        </a-layout>
+      </a-layout-content>
+    </a-layout>
+  </a-layout>
+</template>
+
+<script setup>
+import { ref, onMounted, toRefs } from 'vue';
+import LayoutHeader from "@/components/Layout/components/layoutHeader/index.vue";
+import blendMenu from './blendLeft.vue'
+import Menu from './menu.vue'
+import { layoutSetting } from '@/store/modules/layoutSetting'
+const LayoutStore = layoutSetting()
+</script>
+<style lang="less" scoped>
+::v-deep .arco-layout-sider-trigger{
+  display: none;
+}
+.layout-header {
+  height: auto;
+  background: @bg_color_2;
+  padding-inline: 10px;
+  z-index: 9;
+  background-color: #fff;
+}
+
+.layout-content{
+  background-color: #f2f3f5;
+}
+
+.layout-content-main {
+  flex: 1;
+  padding: 0 1.2rem 1.6rem 1.2rem;
+  margin: 1.1rem 0 0rem 0;
+  overflow-y: auto;
+  overflow: overlay;
+  height: 95%;
+  display: flex;
+  flex-wrap: wrap;
+}
+
+:deep(.layout-content-router) {
+  padding: 0 1rem 1rem 1rem;
+  background-color: @bg_color_4;
+  box-sizing: border-box;
+  width: 100%;
+}
+</style>

+ 302 - 0
src/components/Layout/components/layout/menu.vue

@@ -0,0 +1,302 @@
+<template>
+  <a-layout style="min-height: 100vh">
+    
+    <a-layout-sider theme="dark" :width="160" :collapsed-width="48" collapsible :trigger="null"
+      @collapse="evMenuSecondLongShow" v-model:collapsed="menuSecondLongShow">
+      <div class="logo-layout" v-if="LayoutStore.setting.SidebarLogo">
+        <div class="logo">
+          <img src="@/assets/images/logo.png" />
+        </div>
+        <h3>Easy life</h3>
+      </div>
+      <a-menu v-model:selectedKeys="menuTabSate" theme="dark" mode="inline" @mouseleave="evMouseleaveMenu">
+        <a-menu-item v-for="routeItem in routesData" :key="routeItem.name"
+          @mouseenter="evMenuGetFn(routeItem, 'mouseenter')" @click="changeRoutesItems(routeItem)">
+          <template #icon>
+            <svg-icon :icon="routeItem.meta.icon"></svg-icon>
+          </template>
+          <span>{{ routeItem.meta.title }}</span>
+        </a-menu-item>
+      </a-menu>
+    </a-layout-sider>
+    <a-layout-sider :width="180" :collapsed-width="48" collapsible :trigger="null" @collapse="evMenuSecondLongShow"
+      v-model:collapsed="menuSecondLongShow">
+      <a-menu :selectedKeys="routeItemSelectedKeys" id="layout-sider" theme="light" mode="vertical" :collapsed="false"
+        :auto-open="true">
+        <template v-for="routeItem in menuSecondData?.children || []">
+          <a-menu-item v-if="!routeItem.children || routeItem.children.length === 0" :key="routeItem.name?.toString()"
+            @click="evGoPage(routeItem)">
+            <span class="menu-level-font">{{ routeItem.meta.title }}</span>
+          </a-menu-item>
+
+          <template v-else-if="routeItem.children && routeItem.children.length > 0">
+            <sub-menu :key="routeItem.name" :menu-info="routeItem" @go="evGoPage" />
+          </template>
+        </template>
+      </a-menu>
+    </a-layout-sider>
+  </a-layout>
+</template>
+<script setup>
+import { ref, computed, h, reactive, onMounted, watch, nextTick } from "vue";
+import { useRoute, useRouter } from "vue-router";
+import { useSystemStore } from "@/store/modules/systemStore";
+import LayoutHeader from "@/components/Layout/components/layoutHeader/index.vue";
+import subMenu from "@/components/Layout/components/subMenu/index.vue";
+import { RouterTagData } from '@/store/modules/routerTag.js'
+import { layoutSetting } from '@/store/modules/layoutSetting'
+const LayoutStore = layoutSetting()
+// 标签页仓库
+const routerTagData = RouterTagData()
+
+const systemStore = useSystemStore();
+
+const route = useRoute();
+const router = useRouter();
+
+const menuSecondLongShow = ref(systemStore.menuSecondLongShow);
+// 主菜单
+const routesData = router.options.routes[0]?.children || [];
+
+const menuTabSate = ref([systemStore.getMenuTabSate || routesData[0].name]);
+//子菜单
+const menuSecondData = ref(systemStore.getRouteItem);
+// 选中的状态路由列表
+const menuSecondSelectedStatusData = ref(systemStore.getRouteItem);
+const routeItemSelectedKeys = ref();
+
+const evMenuSecondLongShow = () => {
+  systemStore.setStateValue({
+    key: "menuSecondLongShow",
+    value: menuSecondLongShow.value ? 1 : 0,
+    localStorage: true,
+  });
+};
+const evMouseleaveMenu = (e) => {
+  if (
+    e.relatedTarget?.offsetParent?.id &&
+    e.relatedTarget?.offsetParent?.id == "layout-sider"
+  )
+    return;
+  menuSecondData.value = menuSecondSelectedStatusData.value;
+};
+// 子菜单
+const evMouseLeavesSubMenu = () => {
+  menuSecondData.value = menuSecondSelectedStatusData.value;
+};
+// 鼠标滑动上去更新菜单栏 目前先屏蔽
+const evMenuGetFn = (routeItem, type) => {
+  // menuSecondData.value = routeItem;
+  // console.log(routeItem);
+};
+
+// 查找最下级
+const findPath = (data) => {
+  if (data.children && data.children.length > 0) {
+    return findPath(data.children[0]);
+  } else {
+    return data;
+  }
+};
+
+// 主路由 状态
+const changeRoutesItems = (e) => {
+  const item = findPath(e);
+  evGoPage(item);
+};
+
+// 跳转路由
+const evGoPage = async (routeItem) => {
+  await router.push({
+    name: routeItem.name,
+  });
+
+  if (LayoutStore.setting.HeadNavigationBar) {
+    routerTagData.tagsPushData(routeItem)
+    routerTagData.getDefault(routeItem.name)
+  }
+  menuTabSate.value = [route.matched[1].name];
+  systemStore.setStateValue({
+    key: "menuTabSate",
+    value: routeItem.name || "",
+    localStorage: true,
+  });
+
+  menuSecondSelectedStatusData.value = routesData.find(
+    (item) => item.name == menuTabSate.value
+  );
+  menuSecondData.value = menuSecondSelectedStatusData.value;
+  systemStore.setStateValue({
+    key: "routeItem",
+    value: JSON.stringify(menuSecondData.value),
+    localStorage: true,
+  });
+};
+
+watch(
+  route,
+  (val) => {
+    if (val.name) {
+      routeItemSelectedKeys.value = [val.name];
+    }
+  },
+  { immediate: true }
+);
+
+watch(() => routerTagData.getMenuTabSate, val => {
+  routesData.forEach(res=>{
+    if(res.children && res.children.length > 0){
+      res.children.forEach(item=>{
+        if(item.name == val){
+          menuTabSate.value =  [res.name]
+          routeItemSelectedKeys.value = [item.name]
+          const items = findPath(res);
+          evGoPage(items);
+        }
+      })
+    }
+  })
+  console.log(val);
+}, { immediate: true })
+</script>
+<style scoped lang="less">
+.layout-header {
+  height: 48px;
+  background: @bg_color_2;
+  padding-inline: 10px;
+  box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
+  z-index: 9;
+}
+
+.ant-layout-content {
+  height: auto;
+  min-height: auto;
+  overflow-y: auto;
+  padding: 0 20px;
+}
+
+.layout-content-main {
+  flex: 1;
+  padding: 0 1.2rem 1.6rem 1.2rem;
+  margin: 1.1rem 0 0rem 0;
+  overflow-y: auto;
+  overflow: overlay;
+  height: 95%;
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.logo-layout {
+  height: 64px;
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  .logo {
+    width: 36px;
+    height: 36px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+  h3{
+    color: #fff;
+    margin-left: 10px;
+  }
+}
+
+.arco-layout {
+  color: @text_color_1;
+  background: @layout-split-color !important;
+  overflow: hidden;
+}
+
+:deep(.arco-menu-light) {
+  background-color: @bg_color_3;
+
+  .arco-menu-item {
+    background-color: @bg_color_3;
+  }
+}
+
+:deep(.arco-layout-sider-light) {
+  background-color: @bg_color_3;
+}
+
+:deep(.arco-menu-inline-header) {
+  font-weight: 600;
+  color: @text_color_1;
+  background-color: @bg_color_3;
+}
+
+:deep(.layout-content-router) {
+  padding: 0 1rem 1rem 1rem;
+  background-color: @bg_color_4;
+  box-sizing: border-box;
+  width: 100%;
+}
+
+:deep(.arco-menu-inline-content) {
+  .arco-menu-selected {
+    color: @text_color_1;
+    background-color: @black_3;
+
+    &:after {
+      content: none;
+    }
+  }
+}
+
+#layout-sider {
+  :deep(.arco-menu-selected) {
+    color: @text_color_1 !important;
+    font-weight: bold;
+    background-color: @black_3;
+
+    .arco-icon {
+      color: @text_color_1 !important;
+    }
+
+    &:hover {
+      background-color: rgba(235, 19, 19, 0.1) !important;
+    }
+  }
+
+  :deep(.arco-menu-inline) {
+    .arco-menu-inline-header {
+      background-color: @bg_color_3;
+      padding-left: 20px;
+      left: 5px;
+
+      svg {
+        color: @text_color_1 !important;
+        font-size: 14px;
+      }
+
+      .arco-menu-icon-suffix {
+        color: @text_color_1 !important;
+        left: 0px !important;
+        right: auto;
+      }
+
+      &:hover {
+        background-color: rgba(255, 255, 255, 0) !important;
+      }
+    }
+
+    .arco-menu-item {
+      padding: 0 5px;
+    }
+  }
+
+  :deep(.arco-menu-item) {
+    padding-left: 24px;
+  }
+}
+</style>

+ 89 - 54
src/components/Layout/components/layoutHeader/index.vue

@@ -1,51 +1,54 @@
 <template>
   <div class="header">
-    <!--    标签-->
-    <div class="flex-left-tag">
-      <!--      <a-space wrap>-->
-      <!--        <a-tag-->
-      <!--            v-for="(tag, index) in tags"-->
-      <!--            :key="tag.name"-->
-      <!--            :closable="index !== 0"-->
-      <!--            @close="settingStore.handleRemove(tag)"-->
-      <!--        >-->
-      <!--          {{ tag.meta.title }}-->
-      <!--        </a-tag>-->
-      <!--      </a-space>-->
-    </div>
-    <!--    个人信息和操作-->
-    <div class="flex-right-user">
-      <a-select :style="{ width: '100px' }" v-model="langStore.lang" :bordered="false" @change="toggleLang">
-        <a-option v-for="item in langList" :key="item.key" :value="item.key">{{ item.label }}</a-option>
-      </a-select>
-
-      <div class="toggle-theme-box">
-        <icon-sun-fill v-if="designStore.getDarkTheme" @click="toggleTheme(false)" :size="18" />
-        <icon-moon-fill v-else @click="toggleTheme(true)" :size="18" />
+    <div class="layout_header">
+      <!-- 面包屑 -->
+      <div class="flex_item_bread" >
+        <a-breadcrumb v-if="LayoutStore.setting.crumbs">
+          <a-breadcrumb-item v-for="(item, index) in itemBreabucm" :key="index">{{ item }}</a-breadcrumb-item>
+        </a-breadcrumb>
+      </div>
+      <!--    个人信息和操作-->
+      <div class="flex-right-user">
+        <a-select :style="{ width: '100px' }" v-model="langStore.lang" :bordered="false" @change="toggleLang">
+          <a-option v-for="item in langList" :key="item.key" :value="item.key">{{ item.label }}</a-option>
+        </a-select>
+
+        <div class="toggle-theme-box">
+          <icon-sun-fill v-if="designStore.getDarkTheme" @click="toggleTheme(false)" :size="18" />
+          <icon-moon-fill v-else @click="toggleTheme(true)" :size="18" />
+        </div>
+
+        <a-dropdown trigger="click">
+          <a-avatar :size="38" class="avatar-body">
+            <img class="avatar-image" alt="avatar" src="https://avatars.githubusercontent.com/u/39849555?v=4" />
+          </a-avatar>
+          <template #content>
+            <a-doption>
+              <a-space @click="evHandleLogout">
+                <icon-export />
+                <span>{{ $t('global.logOut') }}</span>
+              </a-space>
+            </a-doption>
+          </template>
+        </a-dropdown>
+
+        <div class="userName">{{ userInfor?.username }}</div>
+
+        <a-tooltip content="设置中心">
+          <icon-settings size="22" style="margin-left: 10px;" @click="SettingVisible = true" />
+        </a-tooltip>
+
+        <Setting v-model:visible="SettingVisible" />
       </div>
-
-      <a-dropdown trigger="click">
-        <a-avatar :size="38" class="avatar-body">
-          <img class="avatar-image" alt="avatar" src="https://avatars.githubusercontent.com/u/39849555?v=4" />
-        </a-avatar>
-        <template #content>
-          <a-doption>
-            <a-space @click="evHandleLogout">
-              <icon-export />
-              <span>{{ $t('global.logOut') }}</span>
-            </a-space>
-          </a-doption>
-        </template>
-      </a-dropdown>
-
-      <div class="userName">{{ userInfor?.username }}</div>
     </div>
+
+    <Tags v-if="LayoutStore.setting.HeadNavigationBar"></Tags>
   </div>
 </template>
 
 <script setup>
-import { ref, inject, onMounted, watch, reactive } from 'vue'
-import { useRouter } from 'vue-router'
+import { ref, inject, onMounted, watch, reactive, defineEmits } from 'vue'
+import { useRouter, useRoute } from 'vue-router'
 import { useSystemStore } from '@/store/modules/systemStore'
 import { Notification } from '@arco-design/web-vue'
 import { useDesignStore } from '@/store/modules/designStore'
@@ -53,46 +56,73 @@ import { useLangStore } from '@/store/modules/langStore'
 import { useI18n } from 'vue-i18n'
 import { langList } from '@/i18n'
 import { fn_logout } from '@/utils'
-import { RouterTagData } from '@/store/modules/routerTag.js'
-import { setLocalStorage, getLocalStorage } from "@/utils";
-// 标签页仓库
-const settingStore = RouterTagData()
 import { updateRouteByMenu } from "@/router/router.update.js"
-
-
+import Setting from '../setting/index.vue'
+import Tags from '../layoutTags/index.vue'
+import { layoutSetting } from '@/store/modules/layoutSetting'
+const LayoutStore = layoutSetting()
 const router = useRouter()
+const route = useRoute()
 const { locale } = useI18n()
 const systemStore = useSystemStore()
 const designStore = useDesignStore()
 const langStore = useLangStore()
 const userInfor = ref(null)
-
-// const reload  = inject('reloadRoutePage')
+const SettingVisible = ref(false)
+const routesData = router.options.routes[0]?.children || [];
+const itemBreabucm = ref([])
 
 const toggleTheme = (e) => {
   designStore.changeTheme(e)
 }
-
 const toggleLang = async (e) => {
   langStore.changeLang(e)
   locale.value = langStore.lang
   await updateRouteByMenu(router, systemStore)
   router.go(0)
 }
-
 const evHandleLogout = () => {
-
   Notification.success({
     content: '退出成功!',
     duration: 1000,
   })
   // 清除缓存数据
   fn_logout()
-
   router.push({
     name: "login",
   })
 }
+
+// 获取当前路由菜单
+const AcquireRouterItem = (item) => {
+    itemBreabucm.value = [];
+    function recursiveFindRoute(routes, parentTitles = []) {
+        for (const route of routes) {
+            const currentTitles = [...parentTitles];
+            currentTitles.push(route.meta.title);
+            if (route.path === item) {
+                return currentTitles;
+            }
+            if (route.children && route.children.length > 0) {
+                const result = recursiveFindRoute(route.children, currentTitles);
+                if (result.length > 0) {
+                    return result;
+                }
+            }
+        }
+        return [];
+    }
+
+    const result = recursiveFindRoute(routesData);
+    if (result.length > 0) {
+        itemBreabucm.value = result;
+    }
+};
+
+watch(route, val => {
+    AcquireRouterItem(val.path)
+}, { immediate: true })
+
 onMounted(() => {
   userInfor.value = JSON.parse(localStorage.getItem('user_login_information'))
 })
@@ -100,14 +130,19 @@ onMounted(() => {
 </script>
 
 <style scoped lang="less">
-.header {
-  height: 100%;
+.layout_header {
   display: flex;
   align-items: center;
   justify-content: space-between;
   padding: 0 10px;
+  height: 50px;
 }
 
+.header {
+  background-color: #ffffff;
+}
+
+
 .flex-right-user {
   width: auto;
   display: flex;

+ 138 - 0
src/components/Layout/components/layoutTags/index.vue

@@ -0,0 +1,138 @@
+<template>
+    <div class="flex-left-tag" v-if="LayoutStore.setting.HeadNavigationBar">
+        <div class="tabs">
+            <a-tabs type="card" :editable="true" v-model:active-key="activeKey" auto-switch hide-content
+                @tab-click="handelClickTags" @delete="handelDelete">
+                <a-tab-pane v-for="(tag, index) in tags" :key="tag.name" :title="tag.meta.title">
+                </a-tab-pane>
+            </a-tabs>
+        </div>
+        <div class="aryuso">
+            <a-tooltip background-color="#FFF">
+                <icon-apps size="18" style="color: #0256ff;" />
+                <template #content>
+                    <a-list :bordered="false" :split="false" size="small">
+                        <a-list-item class="list_hover">
+                            <a-space @click="handelItemSetting(0)">
+                                <icon-refresh />
+                                刷新
+                            </a-space>
+                        </a-list-item>
+                        <a-list-item class="list_hover">
+                            <a-space @click="handelItemSetting(1)">
+                                <icon-close />
+                                关闭
+                            </a-space>
+                        </a-list-item>
+                        <a-list-item class="list_hover">
+                            <a-space @click="handelItemSetting(2)">
+                                <icon-delete />
+                                全部关闭
+                            </a-space>
+                        </a-list-item>
+                    </a-list>
+                </template>
+            </a-tooltip>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, onMounted, toRefs, watch } from 'vue';
+import { RouterTagData } from '@/store/modules/routerTag.js'
+import { useRouter, useRoute } from 'vue-router'
+
+import { layoutSetting } from '@/store/modules/layoutSetting'
+const LayoutStore = layoutSetting()
+
+const router = useRouter()
+const route = useRoute()
+// 标签页仓库
+const routerTagData = RouterTagData()
+
+const tags = ref([])
+const activeKey = ref()
+
+watch(
+    () => routerTagData.tagData,
+    (val) => {
+        // 更新 tags
+        tags.value = val;
+    },
+    { deep: true }
+);
+
+watch(
+    () => routerTagData.getMenuTabSate,
+    (val) => {
+        activeKey.value = val
+    }, { immediate: true })
+
+
+
+const handelClickTags = (val) => {
+    router.push(val)
+    activeKey.value = val
+    localStorage.setItem('menuTabSate', val)
+    routerTagData.getDefault(val)
+}
+
+const handelItemSetting = (val) => {
+    switch (val) {
+        case 0:
+            router.go(0)
+            break;
+        case 1:
+            routerTagData.handleRemove(routerTagData.getMenuTabSate)
+            router.push({
+                path: '/' + routerTagData.getMenuTabSate
+            })
+            break;
+        case 2:
+            routerTagData.tagData = []
+            localStorage.removeItem('RouterTagData')
+            routerTagData.getDefault('home')
+            router.push({
+                path: '/'
+            })
+            break;
+    }
+}
+
+const handelDelete = (item) => {
+    routerTagData.handleRemove(item)
+    router.push({
+        path: '/' + routerTagData.getMenuTabSate
+    })
+}
+
+onMounted(() => {
+    const tagList = localStorage.getItem('RouterTagData') || []
+    routerTagData.tagsPushFilter(tagList)
+});
+</script>
+<style lang="less" scoped>
+.flex-left-tag {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    padding-top: 8px;
+    border-top: 1px solid #f0f2f5;
+
+    ::v-deep .arco-tabs-tab-active {
+        color: #0256FF;
+        background-color: #e5eeff;
+    }
+}
+
+.list_hover {
+    cursor: pointer;
+    padding: 3px 6px !important;
+    width: 100px;
+}
+
+.list_hover:hover {
+    background-color: #e5eeff;
+    color: #0256FF;
+}
+</style>

+ 254 - 0
src/components/Layout/components/setting/index.vue

@@ -0,0 +1,254 @@
+<template>
+    <a-drawer :width="320" :visible="visible" unmountOnClose @cancel="close" :footer="false">
+        <div>
+            <a-divider>布局样式</a-divider>
+            <div class="menu_item">
+                <div class="item_floter">
+                    <a-row :gutter="6" justify="space-around">
+                        <a-col :span="12">
+                            <div class="layout-drawer-content-item" @click="LayoutStore.handelLayoutStyle(0)" :style="{border:LayoutStore.setting.laoutStyle==0?'1px solid #0256FF':''}">
+                                <div class="menu"></div>
+                                <div class="layout">
+                                    <div class="header"></div>
+                                    <div class="main"></div>
+                                </div>
+                            </div>
+                        </a-col>
+                        <a-col :span="12">
+                            <div class="layout-drawer-content-item" @click="LayoutStore.handelLayoutStyle(1)" :style="{border:LayoutStore.setting.laoutStyle==1?'1px solid #0256FF':''}">
+                                <div class="menu mr5"></div>
+                                <div class="menu aside"></div>
+                                <div class="layout mr8">
+                                    <div class="header"></div>
+                                    <div class="main"></div>
+                                </div>
+                            </div>
+                        </a-col>
+                    </a-row>
+                </div>
+            </div>
+
+            <div class="Interface_setting">
+                <a-divider>界面设置</a-divider>
+
+                <a-row class="grid-demo" justify="space-between">
+                    <a-col :span="12">
+                        <div>主题</div>
+                    </a-col>
+                    <a-col :span="12">
+                        <div class="area">
+                            <a-select :style="{ width: 'auto' }" @change="SwitchTopic" v-model="theme">
+                                <a-option :value="false">暗黑</a-option>
+                                <a-option :value="true">白色</a-option>
+                            </a-select>
+                        </div>
+                    </a-col>
+                </a-row>
+                <a-row class="grid-demo" justify="space-between" align="">
+                    <a-col :span="12">
+                        <div>语言</div>
+                    </a-col>
+                    <a-col :span="12">
+                        <div class="area">
+                            <a-select :style="{ width: 'auto' }" v-model="langStore.lang" @change="toggleLang">
+                                <a-option v-for="item in langList" :key="item.key" :value="item.key">{{
+                                    item.label}}</a-option>
+                            </a-select>
+                        </div>
+                    </a-col>
+                </a-row>
+                <a-row class="grid-demo" justify="space-between" align="">
+                    <a-col :span="12">
+                        <div>面包屑</div>
+                    </a-col>
+                    <a-col :span="12">
+                        <div class="area">
+                            <a-switch v-model="crumbs" @change="LayoutStore.handelCrumbs" />
+                        </div>
+                    </a-col>
+                </a-row>
+                <!-- <a-row class="grid-demo" justify="space-between" align="">
+                    <a-col :span="12">
+                        <div>面包屑图标</div>
+                    </a-col>
+                    <a-col :span="12">
+                        <div class="area">
+                            <a-switch v-model="BreadcrumbIcon" @change="LayoutStore.handelBreadcrumbIcon"/>
+                        </div>
+                    </a-col>
+                </a-row> -->
+                <a-row class="grid-demo" justify="space-between" align="">
+                    <a-col :span="12">
+                        <div>侧边栏Logo</div>
+                    </a-col>
+                    <a-col :span="12">
+                        <div class="area">
+                            <a-switch v-model="SidebarLogo" @change="LayoutStore.handelSidebarLogo"/>
+                        </div>
+                    </a-col>
+                </a-row>
+                <a-row class="grid-demo" justify="space-between" align="">
+                    <a-col :span="12">
+                        <div>头部导航栏</div>
+                    </a-col>
+                    <a-col :span="12">
+                        <div class="area">
+                            <a-switch v-model="HeadNavigationBar" @change="LayoutStore.handelHeadNavigationBar"/>
+                        </div>
+                    </a-col>
+                </a-row>
+            </div>
+        </div>
+    </a-drawer>
+</template>
+
+<script setup>
+import { ref, onMounted, toRefs, defineProps, defineEmits } from 'vue';
+import { langList } from '@/i18n'
+import { useI18n } from 'vue-i18n'
+import { useRouter } from 'vue-router'
+import { useLangStore } from '@/store/modules/langStore'
+import { useDesignStore } from '@/store/modules/designStore'
+import { updateRouteByMenu } from "@/router/router.update.js"
+import { useSystemStore } from '@/store/modules/systemStore'
+import { layoutSetting } from '@/store/modules/layoutSetting'
+const LayoutStore = layoutSetting()
+const systemStore = useSystemStore()
+const { locale } = useI18n()
+const langStore = useLangStore()
+const designStore = useDesignStore()
+const router = useRouter()
+const props = defineProps({
+    visible: {
+        type: Boolean,
+        default: false,
+    },
+});
+
+const { visible } = toRefs(props)
+
+const emits = defineEmits(['update:visible']);
+
+const state = ref({
+    theme: designStore.getDarkTheme,// 主题色
+    crumbs: LayoutStore.getStore.crumbs,     //面包屑
+    BreadcrumbIcon: LayoutStore.getStore.BreadcrumbIcon, // 面包屑图标
+    SidebarLogo: LayoutStore.getStore.SidebarLogo, // 侧边栏Logo
+    HeadNavigationBar: LayoutStore.getStore.HeadNavigationBar, // 头部导航栏
+})
+
+const { theme, crumbs, BreadcrumbIcon, SidebarLogo, HeadNavigationBar } = toRefs(state.value)
+
+// 多语言切换
+const toggleLang = async (e) => {
+    langStore.changeLang(e)
+    locale.value = langStore.lang
+    await updateRouteByMenu(router, systemStore)
+    router.go(0)
+}
+
+// 切换主题
+const SwitchTopic = (e) => {
+    designStore.changeTheme(e)
+}
+
+
+//面包屑
+const changeCrumbs = (e) => {
+    LayoutStore.handelCrumbs(e)
+}
+
+const close = ()=>{
+    LayoutStore.saveLayoutDisposition()
+    emits('update:visible',false)
+}
+
+onMounted(() => {
+})
+
+</script>
+<style lang="less" scoped>
+h3 {
+    text-align: center;
+}
+
+.menu_item {
+
+    .layout-drawer-content-item {
+        box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .08);
+        display: flex;
+        padding: 6px;
+        width: auto;
+        border-radius: 6px;
+        cursor: pointer;
+        border: 1px solid transparent;
+
+
+        &:hover {
+            border: 1px solid #0256FF;
+            transition: all 0.5s linear;
+        }
+
+        .menu {
+            background: #0256ff;
+            width: 17px;
+            height: auto;
+            border-radius: 2px;
+        }
+
+        .mr5 {
+            width: 10px;
+        }
+
+        .aside {
+            margin-left: 5px;
+            width: 17px;
+            background: #80aaff;
+        }
+
+        .layout {
+            margin-left: 5px;
+            width: 100%;
+
+            .header {
+                background: #b3c0d1;
+                width: 100%;
+                height: 14px;
+                border-radius: 2px;
+            }
+
+            .main {
+                width: 100%;
+                height: 39px;
+                background-color: #e9eef3;
+                border: 1px dashed #0256FF;
+                border-radius: 2px;
+                margin-top: 10px;
+            }
+        }
+
+        .mr8 {
+            .header {
+                width: auto;
+            }
+
+            .main {
+                width: auto;
+            }
+        }
+    }
+}
+
+.Interface_setting {
+    margin-top: 40px;
+
+    .area {
+        display: flex;
+        justify-content: flex-end;
+    }
+
+    .grid-demo {
+        margin-bottom: 20px;
+    }
+}
+</style>

+ 43 - 99
src/components/Search/index.vue

@@ -1,35 +1,29 @@
 <template>
-    <div class="search">
+    <div ref="search">
         <!-- 动态渲染表单项 -->
-        <div class="Form">
-            <a-form :model="formState" layout="inline">
-                <a-form-item v-for="(item, index) in showIcon ? data : InitialData" :key="index" :label="item.label"
-                    :field="item.field" :wrapper-col-style="{ marginBottom: '20px' }">
-                    <component :is="'a-' + item.type" v-model="formState[item.field]" item.Custom
-                        :placeholder="item.type == 'input' ? '请输入' : '请选择' + item.label" allow-clear
-                        :style="{ width: item.width ? item.width + 'px' : '' }">
-                        <template v-if="item.type == 'select'">
-                            <a-option v-for="option in item.options" :key="option.value" :value="option.value">
-                                {{ option.label }}
-                            </a-option>
-                        </template>
-                    </component>
-                </a-form-item>
-                <a-form-item>
-                    <a-button type="primary" @click="handleQuery">查询</a-button>
-                    <a-button @click="handleReset" style="margin-left: 10px;">重置</a-button>
-                    <div v-if="show" @click="showIcon = !showIcon" class="icon">
-                        {{ showIcon ? '折叠' : '展开' }} <icon-down :rotate="showIcon ? 180 : 0" />
-                    </div>
-                </a-form-item>
-            </a-form>
-        </div>
+        <a-form :model="formState" layout="inline">
+            <a-form-item v-for="(item, index) in SearchForm" :key="index" :label="item.label" :field="item.field">
+                <component :is="'a-' + item.type" v-model="formState[item.field].value" :placeholder="item.placeholder"
+                    allow-clear :style="{ width: item.width ? item.width + 'px' : '' }">
+                    <template v-if="item.type == 'select'">
+                        <a-option v-for="option in item.options" :key="option.value" :value="option.value">
+                            {{ option.label }}
+                        </a-option>
+                    </template>
+                </component>
+            </a-form-item>
+            <a-form-item>
+                <a-button type="primary" @click="handleQuery">查询</a-button>
+                <a-button @click="handleReset" style="margin-left: 10px;">重置</a-button>
+            </a-form-item>
+        </a-form>
     </div>
 </template>
 
 <script setup>
-import { ref, watch, defineProps, defineEmits, toRefs } from 'vue';
-import { Getdictionary } from '@/mixins/index.js'
+import { ref, defineProps, toRefs, onMounted, watch, watchEffect } from 'vue';
+import { Getdictionary } from "@/mixins/index.js";
+
 // 接收 props
 const props = defineProps({
     SearchForm: {
@@ -39,21 +33,24 @@ const props = defineProps({
                 type: 'input',
                 label: '字典名称',
                 field: 'source',
-                value: '',
+                placeholder: '请输入字典名称',
+                value: '', // 双向绑定的值
             },
             {
                 type: 'input',
                 label: '资费ID',
                 field: 'trafficId',
-                value: '',
+                placeholder: '请输入资费ID',
+                value: '', // 双向绑定的值
             },
             {
                 type: 'select',
                 label: '卡类型',
                 field: 'simType',
-                options: [],
-                dict: 'CardType',
-                value: '',
+                placeholder: '选择卡类型',
+                options: [], // 默认空,后面会通过字典加载
+                dict:'CardType',
+                value: '', // 双向绑定的值
                 width: '200'
             },
         ],
@@ -62,93 +59,40 @@ const props = defineProps({
 
 const { SearchForm } = toRefs(props);
 
-// 保存原始的SearchForm副本
-const originalSearchForm = ref([]);
-
-// 分割完成的数据
-const InitialData = ref();
-const show = ref(false);
-const showIcon = ref(false);
 const formState = ref({});
-
-const data = ref();
-
-const emit = defineEmits(['query', 'reset']);
+SearchForm.value.forEach(item => {
+    formState.value[item.field] = {
+        value: item.value, 
+    };
+});
 
 // 字典加载
-const loadedDicts = ref({});
+const loadedDicts = ref({}); 
 const loadDictOptions = async (index, dict) => {
     if (loadedDicts.value[dict]) {
         SearchForm.value[index].options = loadedDicts.value[dict];
         return;
     }
     const dictionary = await Getdictionary(dict);
-    loadedDicts.value[dict] = dictionary || [];
-    SearchForm.value[index].options = loadedDicts.value[dict];
+    loadedDicts.value[dict] = dictionary || []; 
+    SearchForm.value[index].options = loadedDicts.value[dict]; 
 };
 
-// 在watch首次执行或者组件挂载完成时保存原始副本
-watch(() => SearchForm.value, () => {
-    if (originalSearchForm.value.length === 0) {
-        originalSearchForm.value = [...SearchForm.value];
-    }
+watch(
+  () => SearchForm.value, 
+  async () => {
     for (let index = 0; index < SearchForm.value.length; index++) {
         const item = SearchForm.value[index];
         if (item.dict && !loadedDicts.value[item.dict]) {
-            loadDictOptions(index, item.dict);
+            await loadDictOptions(index, item.dict); 
         }
     }
-    // 如果当前长度超过5条就需要折叠展开
-    if (originalSearchForm.value.length >= 6) {
-        show.value = true;
-    }
-    // 基于原始副本进行数组分割操作
-    InitialData.value = originalSearchForm.value.splice(0, 6);
-    data.value = [...InitialData.value, ...originalSearchForm.value];
-}, { immediate: true, deep: true });
+}, { immediate: true });
 
 // 查询操作
 const handleQuery = () => {
+    // 将表单数据通过事件传递给父组件
     emit('query', formState.value);
 };
 
-// 重置操作,正确更新formState并触发reset事件
-const handleReset = () => {
-    const newFormState = {};
-    SearchForm.value.forEach(item => {
-        if (typeof item.value === 'string') {
-            newFormState[item.field] = '';
-        } else if (Array.isArray(item.value)) {
-            newFormState[item.field] = [];
-        } else if (typeof item.value === 'object') {
-            newFormState[item.field] = {};
-        }
-    });
-    formState.value = newFormState;
-    emit('reset', formState.value);
-};
-
-
-
-</script>
-
-<style lang="less" scoped>
-.search {
-    display: flex;
-    justify-content: space-between;
-}
-
-.Form {
-    width: 95%;
-}
-
-.icon {
-    display: flex;
-    // align-items: center;
-    color: #3491fa;
-    font-size: 15px;
-    cursor: pointer;
-    font-weight: 600;
-    margin-left: 15px;
-}
-</style>
+</script>

+ 6 - 6
src/components/upload/index.vue

@@ -1,7 +1,7 @@
 <template>
   <a-upload :show-file-list="true" :custom-request="handelUpload == null ? customRequest : handelUpload"
-    :list-type="listType" :file-list="fileList" :limit="minx" @before-remove="beforeRemove"
-     :image-preview="imagePreview" :accept="accept" @before-upload="beforeUpload"/>
+    :list-type="listType" :file-list="fileList" :limit="minx" @before-remove="beforeRemove" :show-remove-button="showRemoveButton" :show-cancel-button="false"
+    :image-preview="imagePreview" :accept="accept" @before-upload="beforeUpload" />
 </template>
 
 <script setup>
@@ -20,12 +20,14 @@ const customRequest = async (option) => {
   const fileName = `thumbnail_${file.name}`;
   const key = `test/${fileName}`;
 
+  // 获取上传客户端(假设 systemStore 已经定义了上传接口)
   const client = await systemStore.getSTSClient();
   const resClient = await client.putObject({
     key: key,
     body: file
   });
 
+
   // 上传成功后,更新 fileList 和 modelValue
   if (resClient.statusCode === 200) {
     const uploadedFileUrl = resClient.url;
@@ -64,7 +66,7 @@ const props = defineProps({
     type: Function,
     default: null  // 外部传入的自定义上传方法
   },
-  showRemoveButton: {
+  showRemoveButton:{
     type: Boolean,
     default: true  // 是否显示删除按钮
   },
@@ -80,7 +82,6 @@ const modelValue = toRef(props, 'modelValue');
 const listType = toRef(props, 'listType');
 const imagePreview = toRef(props, 'imagePreview');
 const handelUpload = toRef(props, 'handelUpload');
-const showRemoveButton = toRef(props, 'showRemoveButton');
 // 定义 emit,用于向外抛出事件
 const emit = defineEmits(["update:modelValue"]);
 
@@ -135,7 +136,7 @@ onMounted(() => {
 
 // 上传前校验
 const beforeUpload = (rawFile) => {
-  if (!props.beforeUpload) return true
+  if(!props.beforeUpload) return true
   const isAllowType =
     rawFile.type === "image/png" ||
     rawFile.type === "image/jpg" ||
@@ -150,7 +151,6 @@ const beforeUpload = (rawFile) => {
   return true;
 };
 
-
 </script>
 
 <style scoped>

+ 49 - 0
src/store/modules/layoutSetting.js

@@ -0,0 +1,49 @@
+import { defineStore } from "pinia";
+import { setLocalStorage, getLocalStorage } from "@/utils";
+
+const LayoutDisposition = getLocalStorage("LayoutDisposition") || false
+
+// 语言
+export const layoutSetting = defineStore({
+  id: "layoutSetting",
+  state: () => ({
+    setting: {
+      crumbs: LayoutDisposition.crumbs , //面包屑
+      BreadcrumbIcon: LayoutDisposition.BreadcrumbIcon , // 面包屑图标
+      SidebarLogo: LayoutDisposition.SidebarLogo , // 侧边栏Logo
+      HeadNavigationBar: LayoutDisposition.HeadNavigationBar , // 头部导航栏
+      laoutStyle: LayoutDisposition.laoutStyle 
+    },
+  }),
+  getters: {
+    getStore() {
+      return this.setting;
+    },
+  },
+  actions: {
+    // 面包屑
+    handelCrumbs(e) {
+      this.setting.crumbs = e;
+    },
+    // 面包屑图标
+    handelBreadcrumbIcon(e){
+        this.setting.BreadcrumbIcon = e;
+    },
+    // 侧边栏Logo
+    handelSidebarLogo(e){
+        this.setting.SidebarLogo = e;
+    },
+    // 头部导航栏
+    handelHeadNavigationBar(e){
+        this.setting.HeadNavigationBar = e;
+    },
+    // 切换布局样式
+    handelLayoutStyle(e){
+        this.setting.laoutStyle = e;
+    },
+    // 本地存储
+    saveLayoutDisposition() {
+      setLocalStorage("LayoutDisposition", this.setting);
+    },
+  },
+});

+ 27 - 9
src/store/modules/routerTag.js

@@ -1,10 +1,10 @@
 import { defineStore } from "pinia";
-import { setLocalStorage, getLocalStorage } from "@/utils";
+
 export const RouterTagData = defineStore({
   id: "RouterTagData",
   state: () => ({
-    tagData: [],
-    getMenuTabSate:localStorage.getItem('menuTabSate') || ''
+    tagData: [] || localStorage.getItem("RouterTagData"),
+    getMenuTabSate: localStorage.getItem("menuTabSate") || "",
   }),
   getters: {},
   actions: {
@@ -14,6 +14,7 @@ export const RouterTagData = defineStore({
       // 再将新的标签加入
       this.tagData.push(data);
       this.localStorageTagsData(this.tagData);
+      this.getDefault(data.name);
     },
     // 初始化标签
     tagsPushFilter(data) {
@@ -25,17 +26,34 @@ export const RouterTagData = defineStore({
     },
     // 删除标签
     handleRemove(data) {
-      // 去重
-      this.tagData = this.tagData.filter((item) => item.name !== data.name);
-      this.localStorageTagsData(this.tagData);
+      this.tagData.forEach((item, index) => {
+        if (item.name === data) {
+          const itemIndex = index;
+          let previousItem = null;
+          // 判断是否是最后一个元素
+          if (itemIndex === this.tagData.length - 1) {
+            if (itemIndex > 0) {
+              previousItem = this.tagData[itemIndex - 1];
+            }
+          } else {
+            previousItem = this.tagData[itemIndex + 1];
+          }
+          this.tagData.splice(index, 1);
+          this.localStorageTagsData(this.tagData);
+          // 添加判断,避免previousItem为null时报错(比如删除最后一个元素且之前判断没取到前一个元素时)
+          if (previousItem) {
+            this.getDefault(previousItem.name);
+          }
+        }
+      });
     },
     localStorageTagsData(data) {
       localStorage.setItem("RouterTagData", JSON.stringify(data));
     },
     // 默认选中菜单
-    getDefault(data){
-        this.getMenuTabSate = data
+    getDefault(data) {
+      this.getMenuTabSate = data;
+      localStorage.setItem("menuTabSate", data);
     },
-
   },
 });

+ 1 - 1
src/store/modules/systemStore.js

@@ -4,7 +4,7 @@ import { setLocalStorage, getLocalStorage } from "@/utils";
 
 const INITIALIZE = {
   local_loading: false,
-  layout: "blendLeft",
+  layout: "main",
   menus: "",
   routeItem: "",
   menuTabSate: "",

+ 13 - 0
src/views/login/login-form.vue

@@ -90,6 +90,19 @@ const handleSubmit = async () => {
     localStorage: true,
   })
   await updateRouteByMenu(router, systemStore)
+
+  const settingLayout = localStorage.getItem('LayoutDisposition') || false
+  if(!settingLayout) {
+    const item = {
+      crumbs:false,
+      BreadcrumbIcon:false,
+      SidebarLogo:false,
+      HeadNavigationBar:false,
+      laoutStyle:1
+    }
+    localStorage.setItem('LayoutDisposition',item)
+  }
+
   router.push({
     path: "/",
   })