Jelajahi Sumber

[Feat 0000]添加系统AI助手首页

wangkeyi 1 Minggu lalu
induk
melakukan
d694a5cdaa

+ 3 - 2
src/layouts/default/sider/bottomSideder.vue

@@ -132,7 +132,8 @@
         e.preventDefault();
         const startX = e.clientX;
         const startY = e.clientY;
-        const rect = (e.target as HTMLElement).closest('.menu-show-icon')!.getBoundingClientRect();
+        const menuEl = (e.target as HTMLElement).closest('.menu-show-icon');
+        const rect = menuEl!.getBoundingClientRect();
         const startTop = rect.top;
         let dragged = false;
 
@@ -152,7 +153,7 @@
         const onUp = () => {
           if (dragged) {
             isDragging.value = false;
-            const iconEl = document.querySelector('.menu-show-icon .icon');
+            const iconEl = menuEl?.querySelector('.icon');
             if (iconEl) {
               const preventClick = (e: MouseEvent) => {
                 e.stopPropagation();

+ 2 - 2
src/layouts/default/sider/index.vue

@@ -20,7 +20,7 @@
   <!-- <bottomSider2 /> -->
 </template>
 <script lang="ts">
-  import { defineComponent } from 'vue';
+  import { computed, defineComponent } from 'vue';
 
   import Sider from './LayoutSider.vue';
   import MixSider from './MixSider.vue';
@@ -58,7 +58,7 @@
         getIsBottomMenu,
         getIsBottomMenuH,
         getShowSidebar,
-        routePath: route.path,
+        routePath: computed(() => route.path),
         noSiderLink,
         noChatLink,
       };

+ 58 - 0
src/views/vent/home/homeAI/index.vue

@@ -0,0 +1,58 @@
+<!-- eslint-disable vue/multi-word-component-names -->
+<template>
+  <div class="home-ai-page">
+    <AiAssistantModal :visible="visible" default-fullscreen :show-controls="false" :z-index="999999" />
+    <Teleport to="body">
+      <div class="home-menu-layer" :style="menuLayerStyle">
+        <BottomSider />
+      </div>
+    </Teleport>
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { computed, onMounted, ref } from 'vue';
+  import AiAssistantModal from '/@/views/ventAI/manageAssistent/components/AiAssistantModal.vue';
+  import BottomSider from '/@/layouts/default/sider/bottomSideder.vue';
+  import { useAppStore } from '/@/store/modules/app';
+
+  const visible = ref(false);
+  const appStore = useAppStore();
+
+  onMounted(() => {
+    // 初始 false 再置 true,触发 AiAssistantModal 的 visible watcher 完成会话列表等初始化加载
+    visible.value = true;
+  });
+
+  // BottomSider 的 z-index 被 #adaptive-container 的 transform 层叠上下文锁住,
+  // 无法盖过挂在 body 层的全屏 AI 弹框;将其 teleport 到 body,
+  // 并用与容器一致的尺寸+缩放复刻坐标系,使菜单正常显示在弹框之上
+  const menuLayerStyle = computed(() => {
+    const container = document.getElementById('adaptive-container');
+    return {
+      width: `${container?.clientWidth || 1920}px`,
+      height: `${container?.clientHeight || 928}px`,
+      transform: `scale(${appStore.getWidthScale}, ${appStore.getHeightScale})`,
+    };
+  });
+</script>
+
+<style scoped lang="less">
+  .home-ai-page {
+    width: 100%;
+    height: 100%;
+  }
+
+  .home-menu-layer {
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 9999999;
+    transform-origin: left top;
+    pointer-events: none;
+
+    > * {
+      pointer-events: auto;
+    }
+  }
+</style>

+ 32 - 41
src/views/ventAI/manageAssistent/components/AiAssistantModal.vue

@@ -5,13 +5,15 @@
     :width="modalWidth"
     :footer="null"
     destroyOnClose
+    :closable="showControls"
+    :zIndex="props.zIndex"
     :class="['ai-assistant-modal', { fullscreen: isFullscreen }]"
   >
     <template #title>
       <div :class="['draggable-title', { 'no-drag': isFullscreen }]" @mousedown="onDragStart">"通安智风"大模型</div>
     </template>
     <!-- 最大化/还原按钮 -->
-    <div class="maximize-btn" @click="toggleFullscreen">
+    <div v-if="showControls" class="maximize-btn" @click="toggleFullscreen">
       <div :class="['maximize-icon', { minimized: isFullscreen }]"></div>
     </div>
     <!-- 收起/展开按钮 -->
@@ -199,13 +201,21 @@
 
   interface Props {
     visible: boolean;
+    // 初始即全屏显示(首页等嵌入场景)
+    defaultFullscreen?: boolean;
+    // 是否显示窗口控制按钮(最大化/还原、关闭)
+    showControls?: boolean;
+    zIndex?: number;
   }
 
   interface Emits {
     (e: 'close'): void;
   }
 
-  const props = defineProps<Props>();
+  const props = withDefaults(defineProps<Props>(), {
+    defaultFullscreen: false,
+    showControls: true,
+  });
   const emit = defineEmits<Emits>();
 
   const taskList = ref<Task[]>([]);
@@ -407,7 +417,7 @@
     }
   };
 
-  const isFullscreen = ref(false);
+  const isFullscreen = ref(!!props.defaultFullscreen);
 
   // 响应式视口宽度,用于计算 modal 最大宽度不超过屏幕
   const viewportWidth = ref(window.innerWidth);
@@ -460,32 +470,7 @@
         content.addEventListener('animationend', () => content.classList.remove(animClass), { once: true });
       }
 
-      const wraps = document.querySelectorAll<HTMLElement>('.zxm-modal-wrap, .ant-modal-wrap, [class*="modal-wrap"]');
-      wraps.forEach((wrap) => {
-        if (isFullscreen.value) {
-          wrap.style.position = '';
-          wrap.style.left = '';
-          wrap.style.top = '0';
-          wrap.style.right = '';
-          wrap.style.bottom = '';
-          wrap.style.width = '';
-          wrap.style.height = '';
-          wrap.style.margin = '0';
-          wrap.style.padding = '0';
-          wrap.style.transform = '';
-        } else {
-          wrap.style.position = '';
-          wrap.style.left = '';
-          wrap.style.top = '';
-          wrap.style.right = '';
-          wrap.style.bottom = '';
-          wrap.style.width = '';
-          wrap.style.height = '';
-          wrap.style.margin = '';
-          wrap.style.padding = '';
-          wrap.style.transform = '';
-        }
-      });
+      applyWrapStyles(isFullscreen.value);
       draggedEl = null;
       if (!isFullscreen.value) {
         centerModal();
@@ -1898,20 +1883,26 @@
 
   const queryModalWrap = (): HTMLElement | null => document.querySelector<HTMLElement>('.zxm-modal-wrap, .ant-modal-wrap, [class*="modal-wrap"]');
 
+  // 统一处理 modal-wrap 内联样式:全屏时置顶铺满,否则恢复默认
+  const applyWrapStyles = (fullscreen: boolean) => {
+    const wraps = document.querySelectorAll<HTMLElement>('.zxm-modal-wrap, .ant-modal-wrap, [class*="modal-wrap"]');
+    wraps.forEach((wrap) => {
+      wrap.style.position = '';
+      wrap.style.left = '';
+      wrap.style.top = fullscreen ? '0' : '';
+      wrap.style.right = '';
+      wrap.style.bottom = '';
+      wrap.style.width = '';
+      wrap.style.height = '';
+      wrap.style.margin = fullscreen ? '0' : '';
+      wrap.style.padding = fullscreen ? '0' : '';
+      wrap.style.transform = '';
+    });
+  };
+
   const centerModal = () => {
     nextTick(() => {
-      const wrap = queryModalWrap();
-      if (wrap) {
-        wrap.style.position = '';
-        wrap.style.left = '';
-        wrap.style.top = '';
-        wrap.style.right = '';
-        wrap.style.bottom = '';
-        wrap.style.width = '';
-        wrap.style.height = '';
-        wrap.style.margin = '';
-        wrap.style.transform = '';
-      }
+      applyWrapStyles(isFullscreen.value);
       draggedEl = null;
     });
   };