From 4d6b02dada557b4186cdcef843cd3859aeeaac01 Mon Sep 17 00:00:00 2001
From: zhou zhou <3272660260@qq.com>
Date: 星期四, 02 四月 2026 19:28:57 +0800
Subject: [PATCH] #

---
 rsf-design/src/components/core/layouts/art-page-content/index.vue |    6 
 rsf-design/src/router/guards/beforeEach.js                        |   66 ++++++++-
 rsf-design/src/components/core/layouts/art-chat-window/index.vue  |  298 ++++++++++++++++++++++-------------------
 rsf-design/src/locales/langs/en.json                              |    2 
 rsf-design/src/locales/langs/zh.json                              |    2 
 5 files changed, 223 insertions(+), 151 deletions(-)

diff --git a/rsf-design/src/components/core/layouts/art-chat-window/index.vue b/rsf-design/src/components/core/layouts/art-chat-window/index.vue
index 3d40c28..7749458 100644
--- a/rsf-design/src/components/core/layouts/art-chat-window/index.vue
+++ b/rsf-design/src/components/core/layouts/art-chat-window/index.vue
@@ -7,28 +7,163 @@
     class="ai-chat-drawer"
   >
     <div class="flex h-full min-h-0 flex-col overflow-hidden bg-[var(--art-main-bg-color)]">
-      <div class="flex items-center gap-4 border-b border-[var(--el-border-color-lighter)] bg-[var(--art-main-bg-color)] px-6 py-4">
-        <div class="flex size-11 items-center justify-center rounded-3xl bg-[var(--el-color-primary-light-9)] text-[var(--el-color-primary)]">
-          <ArtSvgIcon icon="ri:robot-2-line" class="text-[22px]" />
-        </div>
-        <div class="min-w-0 flex-1">
-          <div class="flex flex-wrap items-center gap-2">
-            <h3 class="text-base font-semibold text-[var(--art-gray-900)]">{{ $t('ai.drawer.title') }}</h3>
-            <ElTag v-if="streaming" type="success" effect="light" round>{{ $t('ai.drawer.streaming') }}</ElTag>
+      <div class="border-b border-[var(--el-border-color-lighter)] bg-[var(--art-main-bg-color)] px-5 py-3">
+        <div class="flex items-center gap-3">
+          <div class="flex size-10 items-center justify-center rounded-3xl bg-[var(--el-color-primary-light-9)] text-[var(--el-color-primary)]">
+            <ArtSvgIcon icon="ri:robot-2-line" class="text-xl" />
           </div>
-          <p class="mt-1 truncate text-xs text-[var(--art-gray-500)]">
-            {{ runtime?.promptName || runtime?.promptCode || DEFAULT_PROMPT_CODE }}
-          </p>
+          <div class="min-w-0 flex-1">
+            <div class="flex flex-wrap items-center gap-2">
+              <h3 class="text-base font-semibold text-[var(--art-gray-900)]">{{ $t('ai.drawer.title') }}</h3>
+              <ElTag v-if="streaming" type="success" effect="light" round>{{ $t('ai.drawer.streaming') }}</ElTag>
+            </div>
+            <p class="mt-1 truncate text-xs text-[var(--art-gray-500)]">
+              {{ runtime?.promptName || runtime?.promptCode || DEFAULT_PROMPT_CODE }}
+            </p>
+          </div>
+          <ElButton plain :disabled="streaming" @click="startNewSession">
+            <ArtSvgIcon icon="ri:add-line" class="mr-1 text-sm" />
+            {{ $t('ai.drawer.newSession') }}
+          </ElButton>
+          <ArtIconButton icon="ri:close-line" @click="closeChat" />
         </div>
-        <ElButton plain :disabled="streaming" @click="startNewSession">
-          <ArtSvgIcon icon="ri:add-line" class="mr-1 text-sm" />
-          {{ $t('ai.drawer.newSession') }}
-        </ElButton>
-        <ArtIconButton icon="ri:close-line" @click="closeChat" />
+
+        <ElCollapseTransition>
+          <div
+            v-if="!runtimePreviewCollapsed"
+            class="mt-3 rounded-3xl bg-g-100/35 px-4 py-3 ring-1 ring-[var(--el-border-color-lighter)]"
+          >
+            <div class="flex flex-wrap items-start justify-between gap-3">
+              <div class="min-w-0 flex-1">
+                <div class="flex flex-wrap items-center gap-2">
+                  <div class="text-sm font-semibold text-[var(--art-gray-900)]">{{ $t('ai.drawer.runtimeOverview') }}</div>
+                  <div class="text-xs text-[var(--art-gray-500)]">{{ usageSummaryText }}</div>
+                </div>
+
+                <div class="mt-3 flex flex-wrap gap-2">
+                  <div
+                    v-for="item in runtimeMetricCards"
+                    :key="item.label"
+                    class="flex min-w-[150px] flex-1 items-center gap-2 rounded-2xl bg-[var(--art-main-bg-color)] px-3 py-2 ring-1 ring-[var(--el-border-color-extra-light)]"
+                  >
+                    <ArtSvgIcon :icon="item.icon" class="text-sm text-[var(--art-gray-500)]" />
+                    <div class="min-w-0 flex-1">
+                      <div class="text-[11px] text-[var(--art-gray-500)]">{{ item.label }}</div>
+                      <div class="truncate text-sm font-medium text-[var(--art-gray-900)]">{{ item.value }}</div>
+                    </div>
+                  </div>
+                </div>
+              </div>
+
+              <div class="flex shrink-0 items-center gap-2">
+                <ElTag v-if="usage?.totalTokens != null" effect="plain" round>
+                  {{ $t('ai.drawer.tokenMetric', {
+                    prompt: usage?.promptTokens ?? 0,
+                    completion: usage?.completionTokens ?? 0,
+                    total: usage?.totalTokens ?? 0
+                  }) }}
+                </ElTag>
+                <ElButton text @click="runtimePreviewCollapsed = true">
+                  {{ $t('ai.drawer.runtimePreviewCollapse') }}
+                </ElButton>
+              </div>
+            </div>
+
+            <div class="mt-3 space-y-3 border-t border-[var(--el-border-color-extra-light)] pt-3">
+                <div class="flex flex-wrap items-center gap-3">
+                  <ElSelect
+                    v-if="selectableModelOptions.length"
+                    v-model="selectedAiParamId"
+                    :placeholder="$t('ai.drawer.modelSelectorLabel')"
+                    :disabled="streaming || loadingRuntime || selectableModelOptions.length <= 1"
+                    class="min-w-[280px]"
+                    @change="handleModelChange"
+                  >
+                    <ElOption
+                      v-for="item in selectableModelOptions"
+                      :key="String(item.aiParamId)"
+                      :label="formatModelOption(item)"
+                      :value="item.aiParamId"
+                    />
+                  </ElSelect>
+
+                  <div class="flex flex-wrap gap-2">
+                    <ElButton
+                      v-for="item in quickLinks"
+                      :key="item.path"
+                      plain
+                      size="small"
+                      @click="navigateTo(item.path)"
+                    >
+                      {{ item.label }}
+                    </ElButton>
+                    <ElButton plain size="small" :disabled="!sessionId || streaming" @click="handleRetainLatestRound">
+                      {{ $t('ai.drawer.retainLatestRound') }}
+                    </ElButton>
+                    <ElButton plain size="small" :disabled="!sessionId || streaming" @click="handleClearMemory">
+                      {{ $t('ai.drawer.clearMemory') }}
+                    </ElButton>
+                  </div>
+                </div>
+
+                <div class="flex flex-wrap gap-2">
+                  <ElTag effect="plain" round>{{ $t('ai.drawer.requestMetric', { value: runtimeSummary.requestId }) }}</ElTag>
+                  <ElTag effect="plain" round>{{ $t('ai.drawer.sessionMetric', { id: sessionId || '--' }) }}</ElTag>
+                  <ElTag effect="plain" round>{{ $t('ai.drawer.recentMetric', { value: runtimeSummary.recentMessageCount }) }}</ElTag>
+                  <ElTag :type="runtimeSummary.hasSummary ? 'success' : 'info'" effect="light" round>
+                    {{ $t(runtimeSummary.hasSummary ? 'ai.drawer.hasSummary' : 'ai.drawer.noSummary') }}
+                  </ElTag>
+                  <ElTag :type="runtimeSummary.hasFacts ? 'success' : 'info'" effect="light" round>
+                    {{ $t(runtimeSummary.hasFacts ? 'ai.drawer.hasFacts' : 'ai.drawer.noFacts') }}
+                  </ElTag>
+                </div>
+
+                <ElAlert
+                  v-if="runtime?.memorySummary"
+                  type="info"
+                  :closable="false"
+                  :title="runtime.memorySummary"
+                />
+                <ElAlert
+                  v-if="runtime?.memoryFacts"
+                  type="success"
+                  :closable="false"
+                  :title="runtime.memoryFacts"
+                />
+            </div>
+          </div>
+        </ElCollapseTransition>
+
+        <ElCollapseTransition>
+          <div
+            v-if="runtimePreviewCollapsed"
+            class="mt-3 flex flex-wrap items-center justify-between gap-2 rounded-3xl bg-g-100/35 px-3 py-2 ring-1 ring-[var(--el-border-color-lighter)]"
+          >
+            <div class="flex min-w-0 flex-1 flex-wrap items-center gap-2">
+              <div class="text-sm font-semibold text-[var(--art-gray-900)]">{{ $t('ai.drawer.runtimeOverview') }}</div>
+              <div class="inline-flex max-w-[220px] items-center gap-1 rounded-full bg-[var(--art-main-bg-color)] px-2.5 py-1 text-xs text-[var(--art-gray-500)] ring-1 ring-[var(--el-border-color-extra-light)]">
+                <ArtSvgIcon icon="ri:cpu-line" class="text-[13px]" />
+                <span class="truncate">{{ runtimeSummary.model }}</span>
+              </div>
+              <div class="inline-flex max-w-[220px] items-center gap-1 rounded-full bg-[var(--art-main-bg-color)] px-2.5 py-1 text-xs text-[var(--art-gray-500)] ring-1 ring-[var(--el-border-color-extra-light)]">
+                <ArtSvgIcon icon="ri:magic-line" class="text-[13px]" />
+                <span class="truncate">{{ runtimeSummary.promptName }}</span>
+              </div>
+              <div class="inline-flex items-center gap-1 rounded-full bg-[var(--art-main-bg-color)] px-2.5 py-1 text-xs text-[var(--art-gray-500)] ring-1 ring-[var(--el-border-color-extra-light)]">
+                <ArtSvgIcon icon="ri:plug-2-line" class="text-[13px]" />
+                <span>{{ runtimeSummary.mountedMcpCount }}</span>
+              </div>
+              <div class="truncate text-xs text-[var(--art-gray-500)]">{{ usageSummaryText }}</div>
+            </div>
+            <ElButton text @click="runtimePreviewCollapsed = false">
+              {{ $t('ai.drawer.runtimePreviewExpand') }}
+            </ElButton>
+          </div>
+        </ElCollapseTransition>
       </div>
 
       <div class="min-h-0 flex-1 bg-g-100/35 ai-chat-body">
-        <aside class="box-border flex min-h-0 flex-col gap-4 p-4 ai-chat-sidebar">
+        <aside class="box-border flex min-h-0 flex-col gap-3 p-3 ai-chat-sidebar">
           <div class="rounded-3xl bg-[var(--art-main-bg-color)] p-4 shadow-[0_12px_36px_rgba(15,23,42,0.05)] ring-1 ring-[var(--el-border-color-lighter)]">
             <div class="mb-3 flex items-center justify-between gap-3">
               <div class="text-sm font-semibold text-[var(--art-gray-900)]">{{ $t('ai.drawer.sessionList') }}</div>
@@ -104,102 +239,7 @@
           </div>
         </aside>
 
-        <section class="box-border flex min-h-0 flex-1 flex-col gap-4 p-4 pl-0">
-          <div class="rounded-3xl bg-[var(--art-main-bg-color)] px-5 py-4 shadow-[0_12px_36px_rgba(15,23,42,0.05)] ring-1 ring-[var(--el-border-color-lighter)]">
-            <div class="flex flex-wrap items-center justify-between gap-3">
-              <div>
-                <div class="text-sm font-semibold text-[var(--art-gray-900)]">{{ $t('ai.drawer.runtimeOverview') }}</div>
-                <div class="mt-1 text-xs text-[var(--art-gray-500)]">
-                  {{ $t('ai.drawer.modelSelectorHint') }}
-                </div>
-              </div>
-              <ElButton text @click="runtimePanelExpanded = !runtimePanelExpanded">
-                {{ $t(runtimePanelExpanded ? 'ai.drawer.runtimeCollapse' : 'ai.drawer.runtimeExpand') }}
-              </ElButton>
-            </div>
-
-            <ElCollapseTransition>
-              <div v-show="runtimePanelExpanded" class="mt-4 space-y-4">
-                <div class="grid gap-3 md:grid-cols-2 xl:grid-cols-4">
-                  <div
-                    v-for="item in runtimeMetricCards"
-                    :key="item.label"
-                    class="rounded-2xl bg-g-100/55 px-4 py-3 ring-1 ring-[var(--el-border-color-extra-light)]"
-                  >
-                    <div class="mb-2 flex items-center gap-2 text-xs text-[var(--art-gray-500)]">
-                      <ArtSvgIcon :icon="item.icon" class="text-sm" />
-                      <span>{{ item.label }}</span>
-                    </div>
-                    <div class="truncate text-sm font-semibold text-[var(--art-gray-900)]">
-                      {{ item.value }}
-                    </div>
-                  </div>
-                </div>
-
-                <div class="flex flex-wrap items-center gap-3">
-                  <ElSelect
-                    v-if="selectableModelOptions.length"
-                    v-model="selectedAiParamId"
-                    :placeholder="$t('ai.drawer.modelSelectorLabel')"
-                    :disabled="streaming || loadingRuntime || selectableModelOptions.length <= 1"
-                    class="min-w-[280px]"
-                    @change="handleModelChange"
-                  >
-                    <ElOption
-                      v-for="item in selectableModelOptions"
-                      :key="String(item.aiParamId)"
-                      :label="formatModelOption(item)"
-                      :value="item.aiParamId"
-                    />
-                  </ElSelect>
-
-                  <div class="flex flex-wrap gap-2">
-                    <ElButton
-                      v-for="item in quickLinks"
-                      :key="item.path"
-                      plain
-                      size="small"
-                      @click="navigateTo(item.path)"
-                    >
-                      {{ item.label }}
-                    </ElButton>
-                    <ElButton plain size="small" :disabled="!sessionId || streaming" @click="handleRetainLatestRound">
-                      {{ $t('ai.drawer.retainLatestRound') }}
-                    </ElButton>
-                    <ElButton plain size="small" :disabled="!sessionId || streaming" @click="handleClearMemory">
-                      {{ $t('ai.drawer.clearMemory') }}
-                    </ElButton>
-                  </div>
-                </div>
-
-                <div class="flex flex-wrap gap-2">
-                  <ElTag effect="plain" round>{{ $t('ai.drawer.requestMetric', { value: runtimeSummary.requestId }) }}</ElTag>
-                  <ElTag effect="plain" round>{{ $t('ai.drawer.sessionMetric', { id: sessionId || '--' }) }}</ElTag>
-                  <ElTag effect="plain" round>{{ $t('ai.drawer.recentMetric', { value: runtimeSummary.recentMessageCount }) }}</ElTag>
-                  <ElTag :type="runtimeSummary.hasSummary ? 'success' : 'info'" effect="light" round>
-                    {{ $t(runtimeSummary.hasSummary ? 'ai.drawer.hasSummary' : 'ai.drawer.noSummary') }}
-                  </ElTag>
-                  <ElTag :type="runtimeSummary.hasFacts ? 'success' : 'info'" effect="light" round>
-                    {{ $t(runtimeSummary.hasFacts ? 'ai.drawer.hasFacts' : 'ai.drawer.noFacts') }}
-                  </ElTag>
-                </div>
-
-                <ElAlert
-                  v-if="runtime?.memorySummary"
-                  type="info"
-                  :closable="false"
-                  :title="runtime.memorySummary"
-                />
-                <ElAlert
-                  v-if="runtime?.memoryFacts"
-                  type="success"
-                  :closable="false"
-                  :title="runtime.memoryFacts"
-                />
-              </div>
-            </ElCollapseTransition>
-          </div>
-
+        <section class="box-border flex min-h-0 flex-1 flex-col gap-3 p-3 pl-0">
           <ElAlert
             v-if="drawerError"
             type="warning"
@@ -304,26 +344,8 @@
               </button>
             </div>
 
-            <div class="flex min-h-0 flex-1 flex-col gap-4 ai-chat-main-column">
+            <div class="flex min-h-0 flex-1 flex-col gap-3 ai-chat-main-column">
               <div class="flex min-h-0 flex-1 flex-col overflow-hidden rounded-3xl bg-[var(--art-main-bg-color)] shadow-[0_12px_36px_rgba(15,23,42,0.05)] ring-1 ring-[var(--el-border-color-lighter)]">
-                <div class="flex flex-wrap items-center justify-between gap-3 border-b border-[var(--el-border-color-extra-light)] px-5 py-4">
-                  <div>
-                    <div class="text-sm font-semibold text-[var(--art-gray-900)]">{{ currentSessionTitle }}</div>
-                    <div class="mt-1 text-xs text-[var(--art-gray-500)]">
-                      {{ usageSummaryText }}
-                    </div>
-                  </div>
-                  <div class="flex flex-wrap gap-2">
-                    <ElTag v-if="usage?.totalTokens != null" effect="plain" round>
-                      {{ $t('ai.drawer.tokenMetric', {
-                        prompt: usage?.promptTokens ?? 0,
-                        completion: usage?.completionTokens ?? 0,
-                        total: usage?.totalTokens ?? 0
-                      }) }}
-                    </ElTag>
-                  </div>
-                </div>
-
                 <ElScrollbar class="min-h-0 flex-1 bg-g-100/35 px-5 py-5">
                   <div class="space-y-5">
                     <div v-if="!messages.length" class="rounded-3xl border border-dashed border-[var(--el-border-color)] bg-slate-50/80 px-4 py-8 text-center text-sm text-[var(--art-gray-500)]">
@@ -489,7 +511,7 @@
   const sessionKeyword = ref('')
   const loadingRuntime = ref(false)
   const streaming = ref(false)
-  const runtimePanelExpanded = ref(false)
+  const runtimePreviewCollapsed = ref(true)
   const tracePanelExpanded = ref(false)
   const messagesBottomRef = ref(null)
   const renameDialog = reactive({
@@ -583,7 +605,7 @@
 
   watch(isDrawerVisible, async (visible) => {
     if (visible) {
-      runtimePanelExpanded.value = false
+      runtimePreviewCollapsed.value = true
       tracePanelExpanded.value = false
       await initializeDrawer()
       scrollMessagesToBottom()
@@ -1050,16 +1072,16 @@
   }
 
   .ai-chat-sidebar {
-    width: 320px;
+    width: 248px;
   }
 
   .ai-chat-workspace {
     display: flex;
-    gap: 16px;
+    gap: 12px;
   }
 
   .ai-chat-trace-column {
-    width: 360px;
+    width: 312px;
     flex-shrink: 0;
     transition:
       width 0.2s ease,
@@ -1067,7 +1089,7 @@
   }
 
   .ai-chat-trace-column--collapsed {
-    width: 88px;
+    width: 72px;
   }
 
   .ai-chat-trace-collapsed-label {
diff --git a/rsf-design/src/components/core/layouts/art-page-content/index.vue b/rsf-design/src/components/core/layouts/art-page-content/index.vue
index b6c9e1a..c208db8 100644
--- a/rsf-design/src/components/core/layouts/art-page-content/index.vue
+++ b/rsf-design/src/components/core/layouts/art-page-content/index.vue
@@ -14,7 +14,7 @@
       </div>
     </div>
 
-    <div v-if="routeRenderError" class="art-page-view art-route-state">
+    <div v-if="routeRenderError" :key="route.fullPath" class="art-page-view art-route-state">
       <div class="art-route-state__panel">
         <div class="art-route-state__title">{{ t('message.routeRenderFailedTitle') }}</div>
         <div class="art-route-state__desc">{{ routeRenderError }}</div>
@@ -27,7 +27,7 @@
     <RouterView v-else-if="isRefresh" v-slot="{ Component, route }">
       <!-- 缂撳瓨璺敱鍔ㄧ敾 -->
       <Transition :name="showTransitionMask ? '' : actualTransition" mode="out-in" appear>
-        <div v-if="route.meta.keepAlive" class="art-page-view" :style="contentStyle">
+        <div v-if="route.meta.keepAlive" :key="route.path" class="art-page-view" :style="contentStyle">
           <KeepAlive :max="10" :exclude="keepAliveExclude">
             <component :is="Component" :key="route.path" />
           </KeepAlive>
@@ -36,7 +36,7 @@
 
       <!-- 闈炵紦瀛樿矾鐢卞姩鐢� -->
       <Transition :name="showTransitionMask ? '' : actualTransition" mode="out-in" appear>
-        <div v-if="!route.meta.keepAlive" class="art-page-view" :style="contentStyle">
+        <div v-if="!route.meta.keepAlive" :key="route.path" class="art-page-view" :style="contentStyle">
           <component :is="Component" :key="route.path" />
         </div>
       </Transition>
diff --git a/rsf-design/src/locales/langs/en.json b/rsf-design/src/locales/langs/en.json
index caac110..0741f93 100644
--- a/rsf-design/src/locales/langs/en.json
+++ b/rsf-design/src/locales/langs/en.json
@@ -645,6 +645,8 @@
       "runtimeOverview": "Runtime Overview",
       "runtimeExpand": "Show Overview",
       "runtimeCollapse": "Hide Overview",
+      "runtimePreviewExpand": "Show Runtime Preview",
+      "runtimePreviewCollapse": "Collapse Runtime Preview",
       "loadingRuntime": "Loading AI runtime info...",
       "emptyHint": "AI responses stream back through SSE here. You can also maintain parameters, prompts, and MCP mounts from the quick links above.",
       "userRole": "You",
diff --git a/rsf-design/src/locales/langs/zh.json b/rsf-design/src/locales/langs/zh.json
index 3995d16..1a9a3ff 100644
--- a/rsf-design/src/locales/langs/zh.json
+++ b/rsf-design/src/locales/langs/zh.json
@@ -647,6 +647,8 @@
       "runtimeOverview": "杩愯姒傝",
       "runtimeExpand": "灞曞紑姒傝",
       "runtimeCollapse": "鏀惰捣姒傝",
+      "runtimePreviewExpand": "灞曞紑杩愯棰勮",
+      "runtimePreviewCollapse": "鎶樺彔杩愯棰勮",
       "loadingRuntime": "姝e湪鍔犺浇 AI 杩愯鏃朵俊鎭�...",
       "emptyHint": "杩欓噷浼氶�氳繃 SSE 娴佸紡杩斿洖 AI 鍥炲銆備綘涔熷彲浠ュ厛鍘讳笂闈㈢殑蹇嵎鍏ュ彛缁存姢鍙傛暟銆丳rompt 鍜� MCP 鎸傝浇銆�",
       "userRole": "浣�",
diff --git a/rsf-design/src/router/guards/beforeEach.js b/rsf-design/src/router/guards/beforeEach.js
index 1b797f0..96c6a21 100644
--- a/rsf-design/src/router/guards/beforeEach.js
+++ b/rsf-design/src/router/guards/beforeEach.js
@@ -50,6 +50,32 @@
   pendingRouteLocation = null
   return queuedRoute
 }
+function isSameRouteLocation(firstRoute, secondRoute) {
+  if (!firstRoute || !secondRoute) {
+    return false
+  }
+  return (
+    firstRoute.path === secondRoute.path &&
+    JSON.stringify(firstRoute.query || {}) === JSON.stringify(secondRoute.query || {}) &&
+    (firstRoute.hash || '') === (secondRoute.hash || '')
+  )
+}
+function schedulePendingRouteNavigation(router, routeLocation) {
+  if (!routeLocation) {
+    return
+  }
+  setTimeout(() => {
+    const currentRoute = router.currentRoute?.value
+    if (isSameRouteLocation(currentRoute, routeLocation)) {
+      return
+    }
+    void router.push({
+      path: routeLocation.path,
+      query: routeLocation.query,
+      hash: routeLocation.hash
+    })
+  }, 0)
+}
 function setupBeforeEachGuard(router) {
   routeRegistry = new RouteRegistry(router)
   router.beforeEach(async (to, from, next) => {
@@ -176,33 +202,53 @@
     menuStore.addRemoveRouteFns(routeRegistry?.getRemoveRouteFns() || [])
     IframeRouteManager.getInstance().save()
     useWorktabStore().validateWorktabs(router)
-    const targetLocation = consumePendingRoute() || createRouteLocation(to)
-    if (isStaticRoute(targetLocation.path)) {
+    const initialTargetLocation = createRouteLocation(to)
+    const queuedTargetLocation = consumePendingRoute()
+    if (isStaticRoute(initialTargetLocation.path)) {
       routeInitInProgress = false
-      next(targetLocation)
+      next(initialTargetLocation)
+      if (queuedTargetLocation) {
+        schedulePendingRouteNavigation(router, queuedTargetLocation)
+      }
       return
     }
     const { homePath } = useCommon()
-    const { path: validatedPath, hasPermission } = RoutePermissionValidator.validatePath(
-      targetLocation.path,
+    const initialValidation = RoutePermissionValidator.validatePath(
+      initialTargetLocation.path,
       menuList,
       homePath.value || '/'
     )
+    const queuedValidation = queuedTargetLocation
+      ? RoutePermissionValidator.validatePath(
+          queuedTargetLocation.path,
+          menuList,
+          homePath.value || '/'
+        )
+      : null
     routeInitInProgress = false
-    if (!hasPermission) {
+    if (!initialValidation.hasPermission) {
       closeLoading()
-      console.warn(`[RouteGuard] 鐢ㄦ埛鏃犳潈闄愯闂矾寰�: ${targetLocation.path}锛屽凡璺宠浆鍒伴椤礰)
+      console.warn(`[RouteGuard] 鐢ㄦ埛鏃犳潈闄愯闂矾寰�: ${initialTargetLocation.path}锛屽凡璺宠浆鍒伴椤礰)
       next({
-        path: validatedPath,
+        path: initialValidation.path,
         replace: true
       })
     } else {
       next({
-        ...targetLocation,
-        path: validatedPath,
+        ...initialTargetLocation,
+        path: initialValidation.path,
         replace: true
       })
     }
+    if (queuedValidation) {
+      if (!queuedValidation.hasPermission) {
+        console.warn(`[RouteGuard] 鐢ㄦ埛鏃犳潈闄愯闂矾寰�: ${queuedTargetLocation.path}锛屽凡璺宠浆鍒伴椤礰)
+      }
+      schedulePendingRouteNavigation(router, {
+        ...queuedTargetLocation,
+        path: queuedValidation.path
+      })
+    }
   } catch (error) {
     console.error('[RouteGuard] 鍔ㄦ�佽矾鐢辨敞鍐屽け璐�:', error)
     closeLoading()

--
Gitblit v1.9.1