Explorar el Código

feat(layout): 顶栏统一模板库/下载管理/设置入口

- 智能工作台、清洗流程顶栏补齐模板库、下载管理、设置,与传统壳保持一致
- 案件管理页顶栏同步三个入口,模板库取消禁用,无打开案件时也可查看
- 全屏路由下自带图标按钮样式,设置侧边栏与模板库按需加载
cc hace 4 días
padre
commit
200e008b6e

+ 51 - 14
ai-electron/frontend/src/ai/views/aiWorkbench/index.vue

@@ -1,14 +1,25 @@
 <template>
   <div class="awb-page">
     <!-- 工作台顶栏:与布局壳共用 AppTopBar;账号操作只留在主窗口,这里不渲染用户菜单 -->
-    <AppTopBar
-      :case-name="caseName"
-      :logo-title="'清鉴 · 智能工作台'"
-      :show-user-dropdown="false"
-    >
-      <!-- 下载管理:AI 会话的工具结果导出(CSV 等下载产物)都经它落到本机,入口与传统壳顶栏一致 -->
+    <AppTopBar :case-name="caseName" :logo-title="'清鉴 · 智能工作台'" :show-user-dropdown="false">
+      <!-- 模板库 / 下载管理 / 设置:与主壳顶栏保持同一套入口 -->
       <template #actions>
+        <Tooltip title="模板库" placement="bottom" :mouseEnterDelay="0.5">
+          <Button
+            type="text"
+            class="jeesite-header-action-button"
+            aria-label="模板库"
+            @click="templateLibraryVisible = true"
+          >
+            <Icon icon="i-ant-design:book-outlined" />
+          </Button>
+        </Tooltip>
+
+        <!-- 下载管理:AI 会话的工具结果导出(CSV 等下载产物)都经它落到本机 -->
         <AppDownloadManager />
+
+        <!-- 设置:与主壳同一套侧边栏(地图 / 服务地址 / 系统服务 / 授权 / 关于产品) -->
+        <SettingDrawer v-if="getShowSettingButton" />
       </template>
     </AppTopBar>
 
@@ -95,6 +106,9 @@
 
     <!-- 产物预览弹窗:全局唯一实例,消息卡片与产物栏共用 -->
     <ArtifactPreviewModal />
+
+    <!-- 模板库:挂在顶栏上就地打开,不打断当前会话 -->
+    <CaseTemplateLibraryModal v-model:open="templateLibraryVisible" />
   </div>
 </template>
 
@@ -112,8 +126,11 @@
    */
   import { computed, nextTick, onMounted, ref, watch } from 'vue';
   import { useRoute } from 'vue-router';
+  import { Button, Tooltip } from 'ant-design-vue';
   import Icon from '@/core/components/Icon/src/Icon.vue';
   import { AppDownloadManager, AppTopBar } from '@/core/components/Application';
+  import { createAsyncComponent } from '@/core/utils/factory/createAsyncComponent';
+  import { useRootSetting } from '@/core/hooks/setting/useRootSetting';
   import { getLastWorkbenchView, setLastWorkbenchView } from '@/core/logics/workbenchMode';
   import SessionList from '../../components/SessionList.vue';
   import ChatPanel from '../../components/ChatPanel.vue';
@@ -130,6 +147,17 @@
   type WorkbenchView = 'chat' | 'model' | 'agent' | 'knowledge' | 'plugin';
   const WORKBENCH_VIEWS: WorkbenchView[] = ['chat', 'model', 'agent', 'knowledge', 'plugin'];
 
+  /** 设置侧边栏与模板库按需加载:与主壳一致,避免工作台首屏把所有设置弹窗一起打包 */
+  const SettingDrawer = createAsyncComponent(() => import('@/core/layouts/default/setting/index.vue'), {
+    loading: true,
+  });
+  const CaseTemplateLibraryModal = createAsyncComponent(
+    () => import('@/case/views/data/components/CaseTemplateLibraryModal.vue'),
+  );
+
+  const { getShowSettingButton } = useRootSetting();
+  const templateLibraryVisible = ref(false);
+
   /**
    * 视图恢复,两个来源,前者优先:
    * 1. ?view=xxx —— 从上传流程等外部全屏页返回工作台时带回来的视图;
@@ -463,26 +491,35 @@
   }
 
   /*
-   * 下载管理入口的图标按钮观感:传统壳那套 `.jeesite-layout-header-action > div`
-   * 规则只存在于传统布局样式表(layouts/default/header/index.less),工作台路由下
-   * 不加载,这里自带一份(取值与 `jeesite-header-action-button` 一致:顶栏缩排后 30×28),
-   * 只作用于本页,不影响传统壳原有外观。图标尺寸(20px)由组件自身样式统一,
-   * 两个模式不再各写一份。
+   * 顶栏图标按钮(模板库 / 下载管理 / 设置):传统壳那套
+   * `.jeesite-layout-header-action > div` 规则只存在于传统布局样式表
+   * (layouts/default/header/index.less),工作台路由下不加载,这里自带一份
+   * (取值与传统壳一致:顶栏缩排后 30×28、圆角 8),只作用于本页。
    */
-  .awb-page .jeesite-app-download-manager-trigger {
+  .awb-page .jeesite-header-action-button {
     display: inline-flex;
     align-items: center;
     justify-content: center;
     width: 30px;
+    min-width: 30px;
     height: 28px;
-    padding: 0 2px;
+    padding: 0;
     color: inherit;
     background: transparent;
+    border-color: transparent;
     border-radius: 8px;
+    box-shadow: none;
     cursor: pointer;
     transition: background-color 0.2s ease;
 
-    &:hover {
+    .jeesite-icon,
+    .anticon {
+      font-size: 18px;
+    }
+
+    &:hover,
+    &:focus,
+    &:focus-visible {
       background-color: @header-light-bg-hover-color;
     }
   }

+ 14 - 3
ai-electron/frontend/src/case/views/data/components/CaseFlowTopBar.vue

@@ -10,6 +10,9 @@
 
       <!-- 下载管理:下载记录、保存位置与清空 / 刷新等操作,按主界面顶栏同款入口提供 -->
       <AppDownloadManager />
+
+      <!-- 设置:与主壳同一套侧边栏(地图 / 服务地址 / 系统服务 / 授权 / 关于产品) -->
+      <SettingDrawer v-if="getShowSettingButton" />
     </template>
   </AppTopBar>
 
@@ -22,16 +25,24 @@
    *
    * 这三页是不套 LAYOUT 的全屏路由,公共顶栏只能由页面自己渲染;
    * 除品牌、案件、模式切换、账号与窗口控制外,这里补齐主界面顶栏的
-   * 「模板库」和「下载管理」两个入口,避免进入清洗流程后功能断档。
+   * 「模板库」「下载管理」「设置」三个入口,避免进入清洗流程后功能断档。
    */
   import { computed, ref } from 'vue';
   import { Button, Tooltip } from 'ant-design-vue';
   import { AppDownloadManager, AppTopBar } from '@/core/components/Application';
+  import { createAsyncComponent } from '@/core/utils/factory/createAsyncComponent';
+  import { useRootSetting } from '@/core/hooks/setting/useRootSetting';
   import { Icon } from '@/core/components/Icon';
   import { useUserStore } from '@/core/store/modules/case';
   import CaseTemplateLibraryModal from './CaseTemplateLibraryModal.vue';
 
+  /** 设置侧边栏按需加载:避免清洗流程首屏把所有设置弹窗一起打包 */
+  const SettingDrawer = createAsyncComponent(() => import('@/core/layouts/default/setting/index.vue'), {
+    loading: true,
+  });
+
   const userStore = useUserStore();
+  const { getShowSettingButton } = useRootSetting();
   const templateLibraryVisible = ref(false);
   /** 顶栏案件胶囊:全屏流程里也保留当前案件上下文 */
   const caseName = computed(() => String(userStore.getCaseInfo?.name || '').trim());
@@ -45,7 +56,7 @@
      * 这里自带一份,只作用于本组件,不影响传统壳原有外观。
      */
     .case-flow-topbar-action.ant-btn,
-    .jeesite-app-download-manager-trigger {
+    .jeesite-header-action-button {
       display: inline-flex;
       align-items: center;
       justify-content: center;
@@ -83,7 +94,7 @@
   html[data-theme='dark'] {
     .case-flow-topbar {
       .case-flow-topbar-action.ant-btn,
-      .jeesite-app-download-manager-trigger {
+      .jeesite-header-action-button {
         &:hover,
         &:focus,
         &:focus-visible {

+ 71 - 1
ai-electron/frontend/src/plat/views/case/case.vue

@@ -2,6 +2,22 @@
   <div :class="prefixCls" class="case-page">
     <AppHeader>
       <template #actions>
+        <!-- 模板库 / 下载管理 / 设置:与主界面顶栏保持同一套入口 -->
+        <Tooltip title="模板库">
+          <Button
+            type="text"
+            class="jeesite-header-action-button"
+            aria-label="模板库"
+            @click="templateLibraryVisible = true"
+          >
+            <Icon icon="i-ant-design:book-outlined" />
+          </Button>
+        </Tooltip>
+
+        <AppDownloadManager />
+
+        <SettingDrawer v-if="getShowSettingButton" />
+
         <UserDropdown />
       </template>
     </AppHeader>
@@ -138,6 +154,9 @@
 
     <CaseForm @register="registerModal" @success="handleSuccess" @openCase="handleOpenCreatedCase" />
 
+    <!-- 模板库:打开案件后可直接维护,和主界面顶栏入口一致 -->
+    <CaseTemplateLibraryModal v-model:open="templateLibraryVisible" />
+
     <Modal
       centered
       v-model:open="deleteModalVisible"
@@ -169,7 +188,10 @@
   import { useModal } from '@/core/components/Modal';
   import { useLoading } from '@/core/components/Loading';
   import { EmptyState } from '@/core/components/EmptyState';
-  import { AppHeader } from '@/core/components/Application';
+  import { AppDownloadManager, AppHeader } from '@/core/components/Application';
+  import { Icon } from '@/core/components/Icon';
+  import { createAsyncComponent } from '@/core/utils/factory/createAsyncComponent';
+  import { useRootSetting } from '@/core/hooks/setting/useRootSetting';
   import { UserDropdown } from '@/core/layouts/default/header/components';
   import { list, deleteById } from '@/plat/api/case/caseApi';
   import type { CaseInfo } from '@/plat/api/case/caseApi';
@@ -179,6 +201,17 @@
   const { prefixCls } = useDesign('case-list');
   const userStore = useUserStore();
 
+  /** 设置侧边栏与模板库按需加载:与主壳一致,避免案件列表首屏把整套设置弹窗一起打包 */
+  const SettingDrawer = createAsyncComponent(() => import('@/core/layouts/default/setting/index.vue'), {
+    loading: true,
+  });
+  const CaseTemplateLibraryModal = createAsyncComponent(
+    () => import('@/case/views/data/components/CaseTemplateLibraryModal.vue'),
+  );
+
+  const { getShowSettingButton } = useRootSetting();
+  const templateLibraryVisible = ref(false);
+
   const caseList = ref<CaseInfo[]>([]);
   const searchKeyword = ref('');
   const loading = ref(false);
@@ -914,6 +947,43 @@
 </style>
 
 <style lang="less">
+  /* 顶栏公共入口(模板库 / 下载管理 / 设置):AppHeader 不加载传统壳的动作区样式,这里补一份 */
+  .case-page .jeesite-header-action-button {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    width: 30px;
+    min-width: 30px;
+    height: 28px;
+    padding: 0;
+    color: inherit;
+    background: transparent;
+    border-color: transparent;
+    border-radius: 8px;
+    box-shadow: none;
+    cursor: pointer;
+    transition: background-color 0.2s ease;
+
+    .jeesite-icon,
+    .anticon {
+      font-size: 18px;
+    }
+
+    &:hover,
+    &:focus,
+    &:focus-visible {
+      background-color: @header-light-bg-hover-color;
+    }
+  }
+
+  html[data-theme='dark'] .case-page .jeesite-header-action-button {
+    &:hover,
+    &:focus,
+    &:focus-visible {
+      background-color: @header-dark-bg-hover-color;
+    }
+  }
+
   /* 案件卡片图标提示:白底黑字,和菜单 tooltip 保持同一套浅色卡片样式 */
   body .ant-tooltip.case-card-tooltip {
     --antd-arrow-background-color: #fff;