Ver Fonte

文档问题修复

肖栋 há 2 semanas atrás
pai
commit
3f34223d63

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
src/assets/icons/Qoderwork_icon.svg


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
src/assets/icons/doubao_icon.svg


Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 0
src/assets/icons/qianwen_icon.svg


+ 1 - 0
src/assets/icons/tip_token_icon.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="11" height="11" viewBox="0 0 11 11"><path d="M5.4999042,10.999808C2.4623909,10.999808,0,8.5374174,0,5.4999042C0,2.4623909,2.4623909,0,5.4999042,0C8.5374174,0,10.999808,2.4623909,10.999808,5.4999042C10.999808,8.5374174,8.5374174,10.999808,5.4999042,10.999808ZM8.0350161,3.1302187C7.8051372,2.9626436,7.4828773,3.0142055,7.3153024,3.2440841L4.7501125,6.7717571L3.6307962,5.6524405C3.4288466,5.4504914,3.1022894,5.4504914,2.9024887,5.6524405C2.7026877,5.8543901,2.7005389,6.1809473,2.9024887,6.3807483L4.4493365,7.9275961C4.5567565,8.0350161,4.702848,8.088726,4.8532357,8.0779839C5.003624,8.0672426,5.1411214,7.9898996,5.2292061,7.8674412L8.148881,3.8499329C8.3164558,3.620054,8.2648945,3.2977941,8.0350161,3.1302187Z" fill="#10B981" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

Diff do ficheiro suprimidas por serem muito extensas
+ 0 - 1
src/assets/icons/workbuddy_icon.svg


+ 0 - 1
src/assets/icons/workbuddy_icon_blue.svg

@@ -1 +0,0 @@
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="14" height="14" viewBox="0 0 14 14"><defs><linearGradient x1="0.021847622469067574" y1="0.35381415486335754" x2="0.9781570381722661" y2="0.646170684637561" id="master_svg0_14_26923"><stop offset="0%" stop-color="#3B82F6" stop-opacity="1"/><stop offset="100%" stop-color="#6366F1" stop-opacity="1"/></linearGradient></defs><path d="M3.8916094,8.270728580718995L0.91950333,8.270728580718995C0.20447493,8.270728580718995,-0.23487991,7.492524180718994,0.13268492,6.878002180718994L1.6517613,4.345251080718994C1.9015906,3.928869280718994,2.3495603,3.676168180718994,2.8348603,3.676168180718994L5.56288,3.676168180718994C7.748168,-0.02532389428100586,11.007434,-0.21197789928100585,13.186979,0.10676944071899414C13.554543,0.16132988071899415,13.841703,0.44848987071899415,13.893393,0.8131830107189941C14.21214,2.9927274807189943,14.025487,6.251993180718994,10.323994,8.437281180718994L10.323994,11.165301080718994C10.323994,11.650601080718994,10.071293,12.098571080718994,9.654911,12.348401080718995L7.1221595,13.867478080718994C6.510509,14.235042080718994,5.7294335,13.792817080718994,5.7294335,13.080658080718994L5.7294335,10.108552080718994C5.7294335,9.094878680718994,4.9052839,8.270728580718995,3.8916094,8.270728580718995L3.8887377,8.270728580718995L3.8916094,8.270728580718995ZM11.702361,3.676168180718994C11.702361,2.9149164807189942,11.085245,2.2978002807189943,10.323994,2.2978002807189943C9.5627413,2.2978002807189943,8.9456253,2.9149164807189942,8.9456253,3.676168180718994C8.9456253,4.437419880718994,9.5627413,5.054536380718994,10.323994,5.054536380718994C11.085245,5.054536380718994,11.702361,4.437419880718994,11.702361,3.676168180718994Z" fill="url(#master_svg0_14_26923)" fill-opacity="1"/></svg>

+ 12 - 22
src/components/mcpSiderBar.vue

@@ -40,8 +40,8 @@
           v-for="child in item.children"
           :key="child.key"
           class="mcp-sidebar-child-wrap"
-          :ref="(el) => setSupportRef(child, el)"
-          @click.stop
+          @mouseenter="child.key === 'support' && (supportOpen = true)"
+          @mouseleave="child.key === 'support' && (supportOpen = false)"
         >
           <button
             type="button"
@@ -65,7 +65,7 @@
   </aside>
 </template>
 <script setup>
-import { computed, onMounted, onUnmounted, ref, watch } from "vue";
+import { computed, onMounted, ref, watch } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { getMcpServerList } from "@/api/mcp";
 import globalStore from "@/store/modules/global";
@@ -76,13 +76,6 @@ const global = globalStore();
 
 const serviceChildren = ref([]);
 const supportOpen = ref(false);
-const supportRef = ref(null);
-
-const setSupportRef = (child, el) => {
-  if (child?.key === "support") {
-    supportRef.value = el;
-  }
-};
 
 const menuList = computed(() => [
   {
@@ -153,11 +146,6 @@ const fetchServerList = async () => {
 
 onMounted(() => {
   fetchServerList();
-  document.addEventListener("click", handleDocumentClick);
-});
-
-onUnmounted(() => {
-  document.removeEventListener("click", handleDocumentClick);
 });
 
 const getItemIcon = (item) => {
@@ -185,7 +173,6 @@ const navigateToServer = (child) => {
 
 const handleChildClick = (child) => {
   if (child?.key === "support") {
-    supportOpen.value = !supportOpen.value;
     return;
   }
   supportOpen.value = false;
@@ -198,12 +185,6 @@ const handleChildClick = (child) => {
   }
 };
 
-const handleDocumentClick = (event) => {
-  if (!supportOpen.value) return;
-  if (supportRef.value?.contains?.(event.target)) return;
-  supportOpen.value = false;
-};
-
 const handleItemClick = (item) => {
   if (item.children?.length) {
     const index = expandedKeys.value.indexOf(item.key);
@@ -373,6 +354,15 @@ const handleItemClick = (item) => {
   background: #FFFFFF;
   box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
   border-radius: 8px;
+
+  &::before {
+    content: "";
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: -12px;
+    width: 12px;
+  }
 }
 
 .mcp-sidebar-support-popover-img {

+ 3 - 2
src/components/pcHeader.vue

@@ -24,7 +24,7 @@
                         class="navigation-item active"
                         @click="navigateToRoute('/')"
                     >
-                        MCP
+                        MCP服务
                     </button>
 
                     <div class="upload-metrial">
@@ -359,7 +359,7 @@ const handleUploadMaterialClick = (shareType) => {
 
 /**
  * 退出当前登录账号。
- * 成功后清理全局登录状态,停留在当前页面
+ * 成功后清理全局登录状态,并跳转登录页
  */
 const logoutHandle = async () => {
     if (isLoggingOut.value) {
@@ -374,6 +374,7 @@ const logoutHandle = async () => {
         if (response.code === 200) {
             ElMessage.success("退出登录成功!");
             await global.logout();
+            router.replace({ name: "login" });
             return;
         }
 

+ 366 - 97
src/pages/guide/index.vue

@@ -4,7 +4,7 @@
         <div class="home-guide-inner">
           <div class="home-guide-heading">
             <div class="home-guide-title">3 步接入,5 分钟上手</div>
-            <div class="home-guide-subtitle">MCP 协议 + CLI 命令行双栖接入,共用一套 Token</div>
+            <div class="home-guide-subtitle">MCP 协议 / CLI 命令行一键接入,共用一套 Token</div>
           </div>
           <div class="home-guide-timeline">
             <div class="home-guide-timeline-item">
@@ -85,7 +85,7 @@
               <div class="home-guide-block-num">2</div>
               <div class="home-guide-block-copy">
                 <div class="home-guide-block-title">选择你的 AI 工具并复制配置</div>
-                <div class="home-guide-block-desc">推荐 WorkBuddy 内置连接器一键接入,也支持自定义 JSON 配置</div>
+                <div class="home-guide-block-desc">推荐使用 WorkBuddy 一键接入</div>
               </div>
             </div>
             <div class="home-guide-access-tabs">
@@ -103,8 +103,7 @@
                 <div class="home-guide-tool-tabs">
                   <button v-for="tool in guideTools" :key="tool.key" type="button" class="home-guide-tool-tab"
                     :class="{ 'is-active': guideToolTab === tool.key }" @click="guideToolTab = tool.key">
-                    <icon-svg v-if="tool.icon"
-                      :name="guideToolTab === tool.key ? 'workbuddy_icon' : 'workbuddy_icon_blue'" size="14px" />
+                    <icon-svg :name="tool.iconName" size="20px" class="home-guide-tool-tab-icon" />
                     <span>{{ tool.label }}</span>
                   </button>
                 </div>
@@ -222,12 +221,92 @@
                       src="@/assets/images/doubao_img.png"
                       alt="豆包自定义连接器示意"
                       class="home-guide-doubao-img"
-                      width="608"
-                      height="472"
                     />
                   </div>
                 </div>
 
+                <div v-else-if="guideToolTab === 'qianwen'" class="home-guide-tool-body">
+                  <div class="home-guide-tool-steps">
+                    <div class="home-guide-tool-step">
+                      <div class="home-guide-tool-step-num">1</div>
+                      <div class="home-guide-tool-step-copy">
+                        <div class="home-guide-tool-step-title">打开配置入口</div>
+                        <div class="home-guide-tool-step-desc">
+                          左侧「扩展」→「连接器」→ 右上角「+ 添加」→「粘贴 JSON 配置」
+                        </div>
+                      </div>
+                    </div>
+                    <div class="home-guide-tool-step">
+                      <div class="home-guide-tool-step-num">2</div>
+                      <div class="home-guide-tool-step-copy">
+                        <div class="home-guide-tool-step-title">粘贴 MCP 配置</div>
+                        <div class="home-guide-tool-step-desc">
+                          在「通过 JSON 导入」对话框粘贴右侧全量 JSON → 点击「导入」
+                        </div>
+                      </div>
+                    </div>
+                    <div class="home-guide-note">
+                      <div class="home-guide-note-head">
+                        <div class="home-guide-note-icon-box">
+                          <icon-svg name="WorkBuddy_attention" size="12px" class="home-guide-note-icon" />
+                        </div>
+                        <div class="home-guide-note-title">注意事项</div>
+                      </div>
+                      <ul class="home-guide-note-list">
+                        <li>将 YOUR_ACCESS_TOKEN 替换为页面「获取 Token」生成的真实 Token</li>
+                        <li>导入后可在「连接器 → 已安装」查看安装状态</li>
+                      </ul>
+                    </div>
+                  </div>
+                  <div class="home-guide-code">
+                    <div class="home-guide-code-bar">
+                      <div class="home-guide-code-dots">
+                        <span></span>
+                        <span></span>
+                        <span></span>
+                      </div>
+                      <div class="home-guide-code-title">mcp.json</div>
+                      <button type="button" class="home-guide-code-copy" @click="copyGuideMcpJson">
+                        <icon-svg name="copy_icon_white" size="14px" class="home-guide-code-copy-icon" />
+                        <span>复制</span>
+                      </button>
+                    </div>
+                    <div class="home-guide-code-body">
+                      <div class="home-guide-code-lines">
+                        <span v-for="line in guideMcpLines" :key="line">{{ line }}</span>
+                      </div>
+                      <pre class="home-guide-code-pre"><span class="code-punct">{</span>
+  <span class="code-key">"mcpServers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+    <span class="code-key">"hpyzs-law-keyword"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-keyword"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-law-semantic"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-semantic"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-qa"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/qa"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-emission"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/emission"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">}</span>
+  <span class="code-punct">}</span>
+<span class="code-punct">}</span></pre>
+                    </div>
+                  </div>
+                </div>
+
                 <div v-else-if="guideToolTab === 'qoderwork'" class="home-guide-tool-body">
                   <div class="home-guide-tool-steps">
                     <div class="home-guide-tool-step">
@@ -235,7 +314,7 @@
                       <div class="home-guide-tool-step-copy">
                         <div class="home-guide-tool-step-title">打开配置入口</div>
                         <div class="home-guide-tool-step-desc">
-                          左侧「扩展」→「连接器」→ 右上角「+ 添加」→「粘贴 JSON 配置」→ 在「通过 JSON 导入」对话框粘贴下方全量 JSON → 点击「导入」2
+                          左侧「扩展」→「连接器」→ 右上角「+ 添加」→「粘贴 JSON 配置」→ 在「通过 JSON 导入」对话框粘贴下方全量 JSON → 点击「导入」
                         </div>
                       </div>
                     </div>
@@ -244,7 +323,7 @@
                       <div class="home-guide-tool-step-copy">
                         <div class="home-guide-tool-step-title">粘贴 MCP 配置</div>
                         <div class="home-guide-tool-step-desc">
-                          将右侧「配置」中的 JSON 粘贴到客户端对应位置并保存
+                          将右侧「配置」中的 JSON 粘贴到客户端对应位置并保存
                         </div>
                       </div>
                     </div>
@@ -280,38 +359,40 @@
                         <span></span>
                       </div>
                       <div class="home-guide-code-title">mcp.json</div>
-                      <button type="button" class="home-guide-code-copy" @click="copyGuideQoderworkJson">
+                      <button type="button" class="home-guide-code-copy" @click="copyGuideMcpJson">
                         <icon-svg name="copy_icon_white" size="14px" class="home-guide-code-copy-icon" />
                         <span>复制</span>
                       </button>
                     </div>
                     <div class="home-guide-code-body">
                       <div class="home-guide-code-lines">
-                        <span v-for="line in guideQoderworkLines" :key="line">{{ line }}</span>
+                        <span v-for="line in guideMcpLines" :key="line">{{ line }}</span>
                       </div>
                       <pre class="home-guide-code-pre"><span class="code-punct">{</span>
-  <span class="code-key">"hpyzs-law-keyword"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-keyword"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
-    <span class="code-punct">}</span>
-  <span class="code-punct">},</span>
-  <span class="code-key">"hpyzs-law-semantic"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-semantic"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
-    <span class="code-punct">}</span>
-  <span class="code-punct">},</span>
-  <span class="code-key">"hpyzs-qa"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/qa"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
-    <span class="code-punct">}</span>
-  <span class="code-punct">},</span>
-  <span class="code-key">"hpyzs-emission"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/emission"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+  <span class="code-key">"mcpServers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+    <span class="code-key">"hpyzs-law-keyword"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-keyword"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-law-semantic"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-semantic"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-qa"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/qa"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-emission"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/emission"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
     <span class="code-punct">}</span>
   <span class="code-punct">}</span>
 <span class="code-punct">}</span></pre>
@@ -502,6 +583,10 @@ const fetchMcpToken = async () => {
 
 const createToken = async () => {
   if (!requireUserLogin("请先登录", { redirect: true })) return;
+  if (guideToken.value) {
+    ElMessage.warning("Token 已存在,无需重复获取");
+    return;
+  }
   try {
     const res = await createMcpToken();
     applyToken(res?.data);
@@ -516,9 +601,10 @@ onMounted(() => {
 const guideAccessTab = ref("mcp");
 const guideToolTab = ref("workbuddy");
 const guideTools = [
-  { key: "workbuddy", label: "WorkBuddy", icon: true },
-  { key: "doubao", label: "豆包", icon: false },
-  { key: "qoderwork", label: "Qoderwork", icon: false },
+  { key: "workbuddy", label: "WorkBuddy", iconName: "WorkBuddy_icon" },
+  { key: "doubao", label: "豆包", iconName: "doubao_icon" },
+  { key: "qianwen", label: "千问办公", iconName: "qianwen_icon" },
+  { key: "qoderwork", label: "Qoderwork", iconName: "Qoderwork_icon" },
 ];
 const guideDoubaoContent = {
   alert: "豆包只能通过新建自定义连接器接入,不支持直接粘贴 JSON 配置。",
@@ -609,41 +695,6 @@ const guideMcpJson = computed(() => {
 const guideMcpLines = computed(() =>
   Array.from({ length: guideMcpJson.value.split("\n").length }, (_, i) => i + 1),
 );
-const guideQoderworkJson = computed(() => {
-  const token = guideAccessToken.value;
-  return `{
-  "hpyzs-law-keyword": {
-    "url": "https://mcp.eiacloud.com/law-keyword",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  },
-  "hpyzs-law-semantic": {
-    "url": "https://mcp.eiacloud.com/law-semantic",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  },
-  "hpyzs-qa": {
-    "url": "https://mcp.eiacloud.com/qa",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  },
-  "hpyzs-emission": {
-    "url": "https://mcp.eiacloud.com/emission",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  }
-}`;
-});
-const guideQoderworkLines = computed(() =>
-  Array.from(
-    { length: guideQoderworkJson.value.split("\n").length },
-    (_, i) => i + 1,
-  ),
-);
 const copyText = async (text) => {
   if (!text) {
     ElMessage.warning("暂无内容可复制");
@@ -671,7 +722,6 @@ const copyText = async (text) => {
   }
 };
 const copyGuideMcpJson = () => copyText(guideMcpJson.value);
-const copyGuideQoderworkJson = () => copyText(guideQoderworkJson.value);
 const guideCliSteps = [
   {
     num: 1,
@@ -706,13 +756,9 @@ const guideCliCommands = computed(() => {
       text: `hpyzs-mcp init --authorization "Bearer ${token}"`,
     },
     {
-      label: "标准导则关键词查询",
+      label: "执行查询",
       text: 'hpyzs-mcp law-keyword search_keyword_standard --name "地表水环境治疗标准"',
     },
-    {
-      label: "法规政策关键词查询",
-      text: 'hpyzs-mcp law-keyword search_keyword_policy --name "噪声源分类指导目录"',
-    },
   ];
 });
 const guideCliFooterTip = computed(() =>
@@ -760,19 +806,24 @@ const guideVerifyCards = [
 .home-content-stack {
   width: 100%;
   box-sizing: border-box;
+  min-width: 0;
+  --guide-inline-padding: 40px;
 }
 
 .home-guide {
   box-sizing: border-box;
   width: 100%;
-  padding: 60px;
+  min-width: 0;
+  padding: var(--guide-inline-padding);
   background: #FFFFFF;
   box-shadow: 0px 4px 24px 0px rgba(15, 23, 42, 0.06);
   border-radius: 8px 8px 8px 8px;
+  overflow-x: hidden;
 }
 
 .home-guide-inner {
   width: 100%;
+  min-width: 0;
   display: flex;
   flex-direction: column;
   align-items: stretch;
@@ -809,11 +860,14 @@ const guideVerifyCards = [
   align-items: flex-start;
   justify-content: center;
   margin-top: 8px;
+  width: 100%;
+  max-width: 100%;
+  min-width: 0;
 }
 
 .home-guide-timeline-item {
-  flex: 0 0 210px;
-  width: 210px;
+  flex: 1 1 0;
+  min-width: 0;
   max-width: 210px;
   display: flex;
   flex-direction: column;
@@ -875,17 +929,21 @@ const guideVerifyCards = [
 .home-guide-timeline-desc {
   margin-top: 2px;
   width: 100%;
+  padding: 0 4px;
+  box-sizing: border-box;
   font-family: PingFang SC, PingFang SC;
   font-weight: 400;
   font-size: 14px;
   color: #64748B;
   line-height: 20px;
   text-align: center;
+  word-break: break-word;
 }
 
 .home-guide-timeline-line {
-  flex-shrink: 0;
-  width: 245px;
+  flex: 1 1 0;
+  min-width: 24px;
+  max-width: 245px;
   height: 4px;
   margin-top: 24px;
   border-radius: 2px;
@@ -1029,7 +1087,9 @@ const guideVerifyCards = [
   box-sizing: border-box;
   display: flex;
   align-items: center;
-  width: 400px;
+  width: 100%;
+  max-width: 400px;
+  min-width: 0;
   height: 44px;
   padding: 12px 14px;
   background: #1E293B;
@@ -1073,13 +1133,18 @@ const guideVerifyCards = [
   width: 24px;
   height: 24px;
   color: #FFFFFF;
+  cursor: pointer;
 }
 
 .home-guide-block-token .home-guide-token-btn {
   display: inline-flex;
   align-items: center;
+  justify-content: center;
   gap: 6px;
-  padding: 12px 14px;
+  flex-shrink: 0;
+  box-sizing: border-box;
+  min-height: 44px;
+  padding: 12px 16px;
   border: none;
   border-radius: 8px;
   background: linear-gradient(107deg, #3B82F6 0%, #6366F1 100%);
@@ -1089,7 +1154,7 @@ const guideVerifyCards = [
   font-size: 14px;
   color: #FFFFFF;
   line-height: 20px;
-  text-align: left;
+  white-space: nowrap;
   cursor: pointer;
 }
 
@@ -1100,7 +1165,7 @@ const guideVerifyCards = [
 
 .home-guide-features {
   display: flex;
-  align-items: center;
+  align-items: flex-start;
   justify-content: space-between;
   gap: 24px;
   margin-top: 16px;
@@ -1113,7 +1178,7 @@ const guideVerifyCards = [
 .home-guide-feature {
   flex: 1;
   display: flex;
-  align-items: center;
+  align-items: flex-start;
   gap: 12px;
   min-width: 0;
 
@@ -1122,6 +1187,11 @@ const guideVerifyCards = [
     height: 38px;
     flex-shrink: 0;
   }
+
+  > div {
+    flex: 1;
+    min-width: 0;
+  }
 }
 
 .home-guide-feature-title {
@@ -1131,7 +1201,6 @@ const guideVerifyCards = [
   color: #0F172A;
   line-height: 26px;
   text-align: left;
-  white-space: nowrap;
 }
 
 .home-guide-feature-desc {
@@ -1141,7 +1210,7 @@ const guideVerifyCards = [
   color: #64748B;
   line-height: 20px;
   text-align: left;
-  white-space: nowrap;
+  word-break: break-word;
 }
 
 .home-guide-access-tabs {
@@ -1249,6 +1318,7 @@ const guideVerifyCards = [
 .home-guide-tool-tab {
   display: inline-flex;
   align-items: center;
+  justify-content: center;
   gap: 8px;
   margin: 0;
   padding: 10px 20px;
@@ -1260,10 +1330,16 @@ const guideVerifyCards = [
   font-size: 14px;
   color: #475569;
   line-height: 20px;
-  text-align: left;
+  text-align: center;
   cursor: pointer;
 }
 
+.home-guide-tool-tab-icon {
+  width: 20px;
+  height: 20px;
+  flex-shrink: 0;
+}
+
 .home-guide-tool-tab.is-active {
   border: none;
   background: linear-gradient(107deg, #3B82F6 0%, #6366F1 100%);
@@ -1277,6 +1353,7 @@ const guideVerifyCards = [
   gap: 20px;
   margin-top: 20px;
   align-items: stretch;
+  min-width: 0;
 }
 
 .home-guide-ima-body {
@@ -1293,22 +1370,28 @@ const guideVerifyCards = [
 
 .home-guide-doubao-body {
   align-items: flex-start;
+  min-width: 0;
 }
 
 .home-guide-doubao-body .home-guide-ima-left {
-  flex: 1;
+  flex: 1 1 0;
   min-width: 0;
 }
 
 .home-guide-doubao-right {
-  flex: 0 0 auto;
+  flex: 1 1 0;
+  min-width: 0;
+  max-width: 608px;
+  display: flex;
+  justify-content: flex-end;
+  align-items: flex-start;
 }
 
 .home-guide-doubao-img {
   display: block;
-  width: 608px;
-  height: 472px;
+  width: 100%;
   max-width: 100%;
+  height: auto;
   object-fit: contain;
 }
 
@@ -1719,9 +1802,8 @@ const guideVerifyCards = [
   display: block;
   margin-top: 6px;
   min-width: 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
+  white-space: pre-wrap;
+  word-break: break-all;
   font-family: Consolas, Monaco, monospace;
   font-size: 13px;
   color: #7DD3FC;
@@ -2032,7 +2114,8 @@ const guideVerifyCards = [
   font-family: Consolas, Monaco, monospace;
   font-size: 13px;
   line-height: 22px;
-  white-space: pre;
+  white-space: pre-wrap;
+  word-break: break-all;
   color: #E2E8F0;
 }
 
@@ -2191,4 +2274,190 @@ const guideVerifyCards = [
   color: #94A3B8;
   cursor: pointer;
 }
+
+@media (max-width: 1200px) {
+  .home-content-stack {
+    --guide-inline-padding: 24px;
+  }
+
+  .home-guide-timeline-item {
+    max-width: 160px;
+  }
+
+  .home-guide-timeline-dot {
+    width: 44px;
+    height: 44px;
+    font-size: 18px;
+    line-height: 26px;
+  }
+
+  .home-guide-timeline-name {
+    margin-top: 8px;
+    font-size: 16px;
+    line-height: 22px;
+  }
+
+  .home-guide-timeline-desc {
+    font-size: 12px;
+    line-height: 18px;
+  }
+
+  .home-guide-timeline-line {
+    max-width: 100px;
+    margin-top: 20px;
+  }
+
+  .home-guide-block-token .home-guide-block-head {
+    flex-wrap: wrap;
+    row-gap: 16px;
+  }
+
+  .home-guide-block-token .home-guide-block-copy {
+    flex: 1 1 0;
+    min-width: 0;
+  }
+
+  .home-guide-block-token .home-guide-token {
+    flex: 1 1 100%;
+    width: 100%;
+    margin-left: 0;
+    flex-wrap: nowrap;
+    min-width: 0;
+  }
+
+  .home-guide-token-bar {
+    flex: 1 1 auto;
+    width: auto;
+    min-width: 0;
+    max-width: none;
+  }
+
+  .home-guide-block-token .home-guide-token-btn {
+    flex-shrink: 0;
+  }
+
+  .home-guide-doubao-body {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .home-guide-doubao-right {
+    max-width: 100%;
+    justify-content: center;
+  }
+
+  .home-guide-tool-body:not(.home-guide-ima-body) {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .home-guide-tool-body:not(.home-guide-ima-body) .home-guide-tool-steps {
+    flex: none;
+    width: 100%;
+  }
+
+  .home-guide-tool-body:not(.home-guide-ima-body) .home-guide-code {
+    flex: none;
+    width: 100%;
+    min-height: 360px;
+  }
+
+  .home-guide-ima-body {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .home-guide-cli-body {
+    grid-template-columns: 1fr;
+    grid-template-areas:
+      "head"
+      "title"
+      "steps"
+      "panel";
+  }
+}
+
+@media (max-width: 768px) {
+  .home-content-stack {
+    --guide-inline-padding: 16px;
+  }
+
+  .home-guide-inner {
+    gap: 24px;
+  }
+
+  .home-guide-title {
+    font-size: 24px;
+    line-height: 32px;
+  }
+
+  .home-guide-subtitle {
+    font-size: 14px;
+    line-height: 22px;
+  }
+
+  .home-guide-timeline-item {
+    max-width: 108px;
+  }
+
+  .home-guide-timeline-dot {
+    width: 40px;
+    height: 40px;
+    font-size: 16px;
+    line-height: 24px;
+  }
+
+  .home-guide-timeline-name {
+    font-size: 14px;
+    line-height: 20px;
+  }
+
+  .home-guide-timeline-desc {
+    font-size: 11px;
+    line-height: 16px;
+  }
+
+  .home-guide-timeline-line {
+    min-width: 12px;
+    max-width: 48px;
+    margin-top: 18px;
+    height: 3px;
+  }
+
+  .home-guide-features {
+    flex-direction: column;
+    gap: 16px;
+    padding: 20px 16px;
+  }
+
+  .home-guide-feature {
+    width: 100%;
+  }
+
+  .home-guide-tool-tabs {
+    flex-wrap: wrap;
+  }
+
+  .home-guide-verify {
+    flex-direction: column;
+  }
+
+  .home-guide-block {
+    padding: 16px;
+  }
+
+  .home-guide-block-token .home-guide-token {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .home-guide-token-bar {
+    width: 100%;
+  }
+
+  .home-guide-block-token .home-guide-token-btn {
+    width: 100%;
+    justify-content: center;
+  }
+}
 </style>

+ 465 - 126
src/pages/home/index.vue

@@ -71,7 +71,7 @@
                 <div class="home-overview-icon-box">
                   <icon-svg name="Mcp_serve" size="22px" />
                 </div>
-                <div class="home-overview-card-value">3+</div>
+                <div class="home-overview-card-value">4+</div>
                 <div class="home-overview-card-label">MCP 服务</div>
               </div>
             </div>
@@ -120,7 +120,7 @@
           <div class="home-scenes-heading">
             <div class="home-scenes-title">热门服务速览</div>
             <div class="home-scenes-subtitle">
-              基于 MCP 协议的下一代法律 AI 服务,让大模型具备专业法律知识和推理能力
+              快速接入高价值环保数据与智能能力
             </div>
           </div>
           <div class="home-scenes-cards">
@@ -188,7 +188,7 @@
         <div class="home-guide-inner">
           <div class="home-guide-heading">
             <div class="home-guide-title">3 步接入,5 分钟上手</div>
-            <div class="home-guide-subtitle">MCP 协议 + CLI 命令行双栖接入,共用一套 Token</div>
+            <div class="home-guide-subtitle">MCP 协议 / CLI 命令行一键接入,共用一套 Token</div>
           </div>
           <div class="home-guide-timeline">
             <div class="home-guide-timeline-item">
@@ -270,7 +270,7 @@
               <div class="home-guide-block-num">2</div>
               <div class="home-guide-block-copy">
                 <div class="home-guide-block-title">选择你的 AI 工具并复制配置</div>
-                <div class="home-guide-block-desc">推荐 WorkBuddy 内置连接器一键接入,也支持自定义 JSON 配置</div>
+                <div class="home-guide-block-desc">推荐使用 WorkBuddy 一键接入</div>
               </div>
             </div>
             <div class="home-guide-access-tabs">
@@ -288,8 +288,7 @@
                 <div class="home-guide-tool-tabs">
                   <button v-for="tool in guideTools" :key="tool.key" type="button" class="home-guide-tool-tab"
                     :class="{ 'is-active': guideToolTab === tool.key }" @click="guideToolTab = tool.key">
-                    <icon-svg v-if="tool.icon"
-                      :name="guideToolTab === tool.key ? 'workbuddy_icon' : 'workbuddy_icon_blue'" size="14px" />
+                    <icon-svg :name="tool.iconName" size="20px" class="home-guide-tool-tab-icon" />
                     <span>{{ tool.label }}</span>
                   </button>
                 </div>
@@ -407,12 +406,92 @@
                       src="@/assets/images/doubao_img.png"
                       alt="豆包自定义连接器示意"
                       class="home-guide-doubao-img"
-                      width="608"
-                      height="472"
                     />
                   </div>
                 </div>
 
+                <div v-else-if="guideToolTab === 'qianwen'" class="home-guide-tool-body">
+                  <div class="home-guide-tool-steps">
+                    <div class="home-guide-tool-step">
+                      <div class="home-guide-tool-step-num">1</div>
+                      <div class="home-guide-tool-step-copy">
+                        <div class="home-guide-tool-step-title">打开配置入口</div>
+                        <div class="home-guide-tool-step-desc">
+                          左侧「扩展」→「连接器」→ 右上角「+ 添加」→「粘贴 JSON 配置」
+                        </div>
+                      </div>
+                    </div>
+                    <div class="home-guide-tool-step">
+                      <div class="home-guide-tool-step-num">2</div>
+                      <div class="home-guide-tool-step-copy">
+                        <div class="home-guide-tool-step-title">粘贴 MCP 配置</div>
+                        <div class="home-guide-tool-step-desc">
+                          在「通过 JSON 导入」对话框粘贴右侧全量 JSON → 点击「导入」
+                        </div>
+                      </div>
+                    </div>
+                    <div class="home-guide-note">
+                      <div class="home-guide-note-head">
+                        <div class="home-guide-note-icon-box">
+                          <icon-svg name="WorkBuddy_attention" size="12px" class="home-guide-note-icon" />
+                        </div>
+                        <div class="home-guide-note-title">注意事项</div>
+                      </div>
+                      <ul class="home-guide-note-list">
+                        <li>将 YOUR_ACCESS_TOKEN 替换为页面「获取 Token」生成的真实 Token</li>
+                        <li>导入后可在「连接器 → 已安装」查看安装状态</li>
+                      </ul>
+                    </div>
+                  </div>
+                  <div class="home-guide-code">
+                    <div class="home-guide-code-bar">
+                      <div class="home-guide-code-dots">
+                        <span></span>
+                        <span></span>
+                        <span></span>
+                      </div>
+                      <div class="home-guide-code-title">mcp.json</div>
+                      <button type="button" class="home-guide-code-copy" @click="copyGuideMcpJson">
+                        <icon-svg name="copy_icon_white" size="14px" class="home-guide-code-copy-icon" />
+                        <span>复制</span>
+                      </button>
+                    </div>
+                    <div class="home-guide-code-body">
+                      <div class="home-guide-code-lines">
+                        <span v-for="line in guideMcpLines" :key="line">{{ line }}</span>
+                      </div>
+                      <pre class="home-guide-code-pre"><span class="code-punct">{</span>
+  <span class="code-key">"mcpServers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+    <span class="code-key">"hpyzs-law-keyword"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-keyword"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-law-semantic"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-semantic"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-qa"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/qa"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-emission"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/emission"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">}</span>
+  <span class="code-punct">}</span>
+<span class="code-punct">}</span></pre>
+                    </div>
+                  </div>
+                </div>
+
                 <div v-else-if="guideToolTab === 'qoderwork'" class="home-guide-tool-body">
                   <div class="home-guide-tool-steps">
                     <div class="home-guide-tool-step">
@@ -420,7 +499,7 @@
                       <div class="home-guide-tool-step-copy">
                         <div class="home-guide-tool-step-title">打开配置入口</div>
                         <div class="home-guide-tool-step-desc">
-                          左侧「扩展」→「连接器」→ 右上角「+ 添加」→「粘贴 JSON 配置」→ 在「通过 JSON 导入」对话框粘贴下方全量 JSON → 点击「导入」2
+                          左侧「扩展」→「连接器」→ 右上角「+ 添加」→「粘贴 JSON 配置」→ 在「通过 JSON 导入」对话框粘贴下方全量 JSON → 点击「导入」
                         </div>
                       </div>
                     </div>
@@ -429,7 +508,7 @@
                       <div class="home-guide-tool-step-copy">
                         <div class="home-guide-tool-step-title">粘贴 MCP 配置</div>
                         <div class="home-guide-tool-step-desc">
-                          将右侧「配置」中的 JSON 粘贴到客户端对应位置并保存
+                          将右侧「配置」中的 JSON 粘贴到客户端对应位置并保存
                         </div>
                       </div>
                     </div>
@@ -465,38 +544,40 @@
                         <span></span>
                       </div>
                       <div class="home-guide-code-title">mcp.json</div>
-                      <button type="button" class="home-guide-code-copy" @click="copyGuideQoderworkJson">
+                      <button type="button" class="home-guide-code-copy" @click="copyGuideMcpJson">
                         <icon-svg name="copy_icon_white" size="14px" class="home-guide-code-copy-icon" />
                         <span>复制</span>
                       </button>
                     </div>
                     <div class="home-guide-code-body">
                       <div class="home-guide-code-lines">
-                        <span v-for="line in guideQoderworkLines" :key="line">{{ line }}</span>
+                        <span v-for="line in guideMcpLines" :key="line">{{ line }}</span>
                       </div>
                       <pre class="home-guide-code-pre"><span class="code-punct">{</span>
-  <span class="code-key">"hpyzs-law-keyword"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-keyword"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
-    <span class="code-punct">}</span>
-  <span class="code-punct">},</span>
-  <span class="code-key">"hpyzs-law-semantic"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-semantic"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
-    <span class="code-punct">}</span>
-  <span class="code-punct">},</span>
-  <span class="code-key">"hpyzs-qa"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/qa"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
-    <span class="code-punct">}</span>
-  <span class="code-punct">},</span>
-  <span class="code-key">"hpyzs-emission"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-    <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/emission"</span><span class="code-punct">,</span>
-    <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
-      <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+  <span class="code-key">"mcpServers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+    <span class="code-key">"hpyzs-law-keyword"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-keyword"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-law-semantic"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/law-semantic"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-qa"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/qa"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
+    <span class="code-punct">},</span>
+    <span class="code-key">"hpyzs-emission"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+      <span class="code-key">"url"</span><span class="code-punct">:</span> <span class="code-string">"https://mcp.eiacloud.com/emission"</span><span class="code-punct">,</span>
+      <span class="code-key">"headers"</span><span class="code-punct">:</span> <span class="code-punct">{</span>
+        <span class="code-key">"Authorization"</span><span class="code-punct">:</span> <span class="code-string">"Bearer {{ guideAccessToken }}"</span>
+      <span class="code-punct">}</span>
     <span class="code-punct">}</span>
   <span class="code-punct">}</span>
 <span class="code-punct">}</span></pre>
@@ -818,9 +899,10 @@ onMounted(() => {
 const guideAccessTab = ref("mcp");
 const guideToolTab = ref("workbuddy");
 const guideTools = [
-  { key: "workbuddy", label: "WorkBuddy", icon: true },
-  { key: "doubao", label: "豆包", icon: false },
-  { key: "qoderwork", label: "Qoderwork", icon: false },
+  { key: "workbuddy", label: "WorkBuddy", iconName: "WorkBuddy_icon" },
+  { key: "doubao", label: "豆包", iconName: "doubao_icon" },
+  { key: "qianwen", label: "千问办公", iconName: "qianwen_icon" },
+  { key: "qoderwork", label: "Qoderwork", iconName: "Qoderwork_icon" },
 ];
 const guideDoubaoContent = {
   alert: "豆包只能通过新建自定义连接器接入,不支持直接粘贴 JSON 配置。",
@@ -911,41 +993,6 @@ const guideMcpJson = computed(() => {
 const guideMcpLines = computed(() =>
   Array.from({ length: guideMcpJson.value.split("\n").length }, (_, i) => i + 1),
 );
-const guideQoderworkJson = computed(() => {
-  const token = guideAccessToken.value;
-  return `{
-  "hpyzs-law-keyword": {
-    "url": "https://mcp.eiacloud.com/law-keyword",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  },
-  "hpyzs-law-semantic": {
-    "url": "https://mcp.eiacloud.com/law-semantic",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  },
-  "hpyzs-qa": {
-    "url": "https://mcp.eiacloud.com/qa",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  },
-  "hpyzs-emission": {
-    "url": "https://mcp.eiacloud.com/emission",
-    "headers": {
-      "Authorization": "Bearer ${token}"
-    }
-  }
-}`;
-});
-const guideQoderworkLines = computed(() =>
-  Array.from(
-    { length: guideQoderworkJson.value.split("\n").length },
-    (_, i) => i + 1,
-  ),
-);
 const copyText = async (text) => {
   if (!text) {
     ElMessage.warning("暂无内容可复制");
@@ -973,7 +1020,6 @@ const copyText = async (text) => {
   }
 };
 const copyGuideMcpJson = () => copyText(guideMcpJson.value);
-const copyGuideQoderworkJson = () => copyText(guideQoderworkJson.value);
 const guideCliSteps = [
   {
     num: 1,
@@ -1008,13 +1054,9 @@ const guideCliCommands = computed(() => {
       text: `hpyzs-mcp init --authorization "Bearer ${token}"`,
     },
     {
-      label: "标准导则关键词查询",
+      label: "执行查询",
       text: 'hpyzs-mcp law-keyword search_keyword_standard --name "地表水环境治疗标准"',
     },
-    {
-      label: "法规政策关键词查询",
-      text: 'hpyzs-mcp law-keyword search_keyword_policy --name "噪声源分类指导目录"',
-    },
   ];
 });
 const guideCliFooterTip = computed(() =>
@@ -1058,6 +1100,8 @@ const guideVerifyCards = [
 <style scoped lang="less">
 .home-page {
   width: 100%;
+  --home-inline-padding: 70px;
+  box-sizing: border-box;
 }
 
 .home-section {
@@ -1069,7 +1113,8 @@ const guideVerifyCards = [
   align-items: stretch;
   justify-content: center;
   gap: 12px;
-  padding: 70px;
+  padding: 70px var(--home-inline-padding);
+  box-sizing: border-box;
   background: url("@/assets/images/home_one_bg.png") center / cover no-repeat;
 }
 
@@ -1215,14 +1260,25 @@ const guideVerifyCards = [
 }
 
 .home-content-stack {
-  display: grid;
-  grid-template-columns: minmax(0, 1fr) max-content minmax(0, 1fr);
+  width: 100%;
+  box-sizing: border-box;
 }
 
 .home-content-stack>.home-section {
-  display: grid;
-  grid-template-columns: subgrid;
-  grid-column: 1 / -1;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+.home-section-inner-padding() {
+  padding-left: var(--home-inline-padding);
+  padding-right: var(--home-inline-padding);
+  box-sizing: border-box;
+}
+
+.home-section-content-width() {
+  max-width: 1320px;
+  margin-left: auto;
+  margin-right: auto;
 }
 
 .home-overview {
@@ -1231,13 +1287,13 @@ const guideVerifyCards = [
 }
 
 .home-overview-inner {
-  grid-column: 2;
   display: flex;
   flex-direction: column;
   align-items: stretch;
   gap: 120px;
-  width: max-content;
-  max-width: 100%;
+  width: 100%;
+  .home-section-inner-padding();
+  .home-section-content-width();
 }
 
 .home-overview-block {
@@ -1248,8 +1304,7 @@ const guideVerifyCards = [
 }
 
 .home-overview-block:last-child {
-  width: 0;
-  min-width: 100%;
+  width: 100%;
 }
 
 .home-overview-heading {
@@ -1279,26 +1334,33 @@ const guideVerifyCards = [
 
 .home-overview-cards {
   display: flex;
-  justify-content: center;
+  justify-content: stretch;
   gap: 16px;
+  width: 100%;
+  min-width: 0;
 }
 
 .home-overview-stats {
   display: flex;
-  justify-content: center;
+  justify-content: stretch;
   gap: 16px;
+  width: 100%;
+  min-width: 0;
 }
 
 .home-overview-stat-card {
+  flex: 1 1 0;
+  min-width: 0;
   display: flex;
   flex-direction: column;
   align-items: center;
   justify-content: center;
   gap: 10px;
-  padding: 30px 100px;
+  padding: 30px 20px;
   background: #FFFFFF;
   box-shadow: 0px 4px 24px 0px rgba(15, 23, 42, 0.06);
   border-radius: 16px;
+  box-sizing: border-box;
 }
 
 .home-overview-icon-box {
@@ -1425,22 +1487,22 @@ const guideVerifyCards = [
 }
 
 .home-scenes-upper {
-  grid-column: 1 / -1;
-  display: grid;
-  grid-template-columns: subgrid;
+  display: flex;
+  flex-direction: column;
   row-gap: 20px;
+  width: 100%;
   padding: 80px 0;
   background: linear-gradient(180deg, #FFFFFF 0%, #F3F7FF 100%);
   border-radius: 0;
+  .home-section-inner-padding();
 }
 
 .home-scenes-heading {
-  grid-column: 2;
   display: flex;
   flex-direction: column;
   align-items: center;
-  width: 0;
-  min-width: 100%;
+  width: 100%;
+  .home-section-content-width();
 }
 
 .home-scenes-title {
@@ -1462,11 +1524,11 @@ const guideVerifyCards = [
 }
 
 .home-scenes-cards {
-  grid-column: 2;
   display: flex;
-  width: 0;
-  min-width: 100%;
+  width: 100%;
+  min-width: 0;
   gap: 16px;
+  .home-section-content-width();
 }
 
 .home-scenes-card {
@@ -1587,12 +1649,12 @@ const guideVerifyCards = [
 }
 
 .home-scenes-lower {
-  grid-column: 2;
-  width: 0;
-  min-width: 100%;
+  width: 100%;
   background: #F3F7FF;
   border-radius: 10px;
   margin: 36px 0 80px;
+  padding: 36px var(--home-inline-padding);
+  box-sizing: border-box;
 }
 
 .home-scenes-demo {
@@ -1600,6 +1662,7 @@ const guideVerifyCards = [
   grid-template-columns: 1fr 1fr;
   align-items: start;
   gap: 40px;
+  .home-section-content-width();
 }
 
 .home-scenes-demo-copy {
@@ -1734,13 +1797,13 @@ const guideVerifyCards = [
 }
 
 .home-guide-inner {
-  grid-column: 2;
-  width: 0;
-  min-width: 100%;
+  width: 100%;
   display: flex;
   flex-direction: column;
   align-items: stretch;
   gap: 32px;
+  .home-section-inner-padding();
+  .home-section-content-width();
 }
 
 .home-guide-heading {
@@ -1773,11 +1836,14 @@ const guideVerifyCards = [
   align-items: flex-start;
   justify-content: center;
   margin-top: 8px;
+  width: 100%;
+  max-width: 100%;
+  min-width: 0;
 }
 
 .home-guide-timeline-item {
-  flex: 0 0 210px;
-  width: 210px;
+  flex: 1 1 0;
+  min-width: 0;
   max-width: 210px;
   display: flex;
   flex-direction: column;
@@ -1839,17 +1905,21 @@ const guideVerifyCards = [
 .home-guide-timeline-desc {
   margin-top: 2px;
   width: 100%;
+  padding: 0 4px;
+  box-sizing: border-box;
   font-family: PingFang SC, PingFang SC;
   font-weight: 400;
   font-size: 14px;
   color: #64748B;
   line-height: 20px;
   text-align: center;
+  word-break: break-word;
 }
 
 .home-guide-timeline-line {
-  flex-shrink: 0;
-  width: 245px;
+  flex: 1 1 0;
+  min-width: 24px;
+  max-width: 245px;
   height: 4px;
   margin-top: 24px;
   border-radius: 2px;
@@ -1993,7 +2063,10 @@ const guideVerifyCards = [
   box-sizing: border-box;
   display: flex;
   align-items: center;
-  width: 400px;
+  flex: 1 1 auto;
+  width: auto;
+  min-width: 0;
+  max-width: 400px;
   height: 44px;
   padding: 12px 14px;
   background: #1E293B;
@@ -2043,8 +2116,12 @@ const guideVerifyCards = [
 .home-guide-block-token .home-guide-token-btn {
   display: inline-flex;
   align-items: center;
+  justify-content: center;
   gap: 6px;
-  padding: 12px 14px;
+  flex-shrink: 0;
+  box-sizing: border-box;
+  min-height: 44px;
+  padding: 12px 16px;
   border: none;
   border-radius: 8px;
   background: linear-gradient(107deg, #3B82F6 0%, #6366F1 100%);
@@ -2054,7 +2131,7 @@ const guideVerifyCards = [
   font-size: 14px;
   color: #FFFFFF;
   line-height: 20px;
-  text-align: left;
+  white-space: nowrap;
   cursor: pointer;
 }
 
@@ -2077,7 +2154,7 @@ const guideVerifyCards = [
 .home-guide-feature {
   flex: 1;
   display: flex;
-  align-items: center;
+  align-items: flex-start;
   gap: 12px;
   min-width: 0;
 }
@@ -2088,6 +2165,11 @@ const guideVerifyCards = [
   height: 38px;
 }
 
+.home-guide-feature > div {
+  flex: 1;
+  min-width: 0;
+}
+
 .home-guide-feature-title {
   font-family: PingFang SC, PingFang SC;
   font-weight: 500;
@@ -2104,7 +2186,7 @@ const guideVerifyCards = [
   color: #64748B;
   line-height: 20px;
   text-align: left;
-  white-space: nowrap;
+  word-break: break-word;
 }
 
 .home-guide-access-tabs {
@@ -2212,6 +2294,7 @@ const guideVerifyCards = [
 .home-guide-tool-tab {
   display: inline-flex;
   align-items: center;
+  justify-content: center;
   gap: 8px;
   margin: 0;
   padding: 10px 20px;
@@ -2223,10 +2306,16 @@ const guideVerifyCards = [
   font-size: 14px;
   color: #475569;
   line-height: 20px;
-  text-align: left;
+  text-align: center;
   cursor: pointer;
 }
 
+.home-guide-tool-tab-icon {
+  width: 20px;
+  height: 20px;
+  flex-shrink: 0;
+}
+
 .home-guide-tool-tab.is-active {
   border: none;
   background: linear-gradient(107deg, #3B82F6 0%, #6366F1 100%);
@@ -2240,6 +2329,7 @@ const guideVerifyCards = [
   gap: 20px;
   margin-top: 20px;
   align-items: stretch;
+  min-width: 0;
 }
 
 .home-guide-pending-body {
@@ -2252,22 +2342,28 @@ const guideVerifyCards = [
 
 .home-guide-doubao-body {
   align-items: flex-start;
+  min-width: 0;
 }
 
 .home-guide-doubao-body .home-guide-ima-left {
-  flex: 1;
+  flex: 1 1 0;
   min-width: 0;
 }
 
 .home-guide-doubao-right {
-  flex: 0 0 auto;
+  flex: 1 1 0;
+  min-width: 0;
+  max-width: 608px;
+  display: flex;
+  justify-content: flex-end;
+  align-items: flex-start;
 }
 
 .home-guide-doubao-img {
   display: block;
-  width: 608px;
-  height: 472px;
+  width: 100%;
   max-width: 100%;
+  height: auto;
   object-fit: contain;
 }
 
@@ -2686,9 +2782,8 @@ const guideVerifyCards = [
   display: block;
   margin-top: 6px;
   min-width: 0;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
+  white-space: pre-wrap;
+  word-break: break-all;
   font-family: Consolas, Monaco, monospace;
   font-size: 13px;
   color: #7DD3FC;
@@ -2999,7 +3094,8 @@ const guideVerifyCards = [
   font-family: Consolas, Monaco, monospace;
   font-size: 13px;
   line-height: 22px;
-  white-space: pre;
+  white-space: pre-wrap;
+  word-break: break-all;
   color: #E2E8F0;
 }
 
@@ -3166,7 +3262,12 @@ const guideVerifyCards = [
 
   .home-footer-inner {
     box-sizing: border-box;
-    padding: 0 24px;
+    width: 100%;
+    max-width: 1320px;
+    margin-left: auto;
+    margin-right: auto;
+    padding-left: calc(var(--home-inline-padding) + 24px);
+    padding-right: calc(var(--home-inline-padding) + 24px);
     flex-direction: row;
     justify-content: space-between;
     align-items: flex-start;
@@ -3330,4 +3431,242 @@ const guideVerifyCards = [
     }
   }
 }
+
+@media (max-width: 1200px) {
+  .home-page {
+    --home-inline-padding: 40px;
+  }
+
+  .home-hero {
+    padding-top: 48px;
+    padding-bottom: 48px;
+    gap: 0;
+  }
+
+  .home-hero-visual {
+    display: none;
+  }
+
+  .home-hero-copy {
+    flex: 1 1 auto;
+    width: 100%;
+    max-width: 100%;
+  }
+
+  .home-hero-title-main {
+    font-size: 40px;
+    line-height: 56px;
+  }
+
+  .home-hero-title-sub {
+    font-size: 32px;
+    line-height: 44px;
+  }
+
+  .home-hero-desc {
+    margin-top: 40px;
+    font-size: 16px;
+    line-height: 28px;
+  }
+
+  .home-hero-actions {
+    margin-top: 48px;
+  }
+
+  .home-overview-inner {
+    gap: 80px;
+  }
+
+  .home-overview-stat-card {
+    padding: 24px 12px;
+  }
+
+  .home-overview-stat-card .home-overview-card-label {
+    font-size: 16px;
+    line-height: 22px;
+  }
+
+  .home-scenes-lower {
+    padding-top: 36px;
+    padding-bottom: 36px;
+  }
+
+  .home-scenes-demo {
+    grid-template-columns: 1fr;
+    gap: 32px;
+  }
+
+  .home-scenes-demo-preview {
+    align-self: stretch;
+    width: 100%;
+  }
+
+  .home-guide-timeline-item {
+    max-width: 160px;
+  }
+
+  .home-guide-timeline-dot {
+    width: 44px;
+    height: 44px;
+    font-size: 18px;
+    line-height: 26px;
+  }
+
+  .home-guide-timeline-name {
+    margin-top: 8px;
+    font-size: 16px;
+    line-height: 22px;
+  }
+
+  .home-guide-timeline-desc {
+    font-size: 12px;
+    line-height: 18px;
+  }
+
+  .home-guide-timeline-line {
+    max-width: 100px;
+    margin-top: 20px;
+  }
+
+  .home-guide-doubao-body {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .home-guide-doubao-right {
+    max-width: 100%;
+    justify-content: center;
+  }
+
+  .home-guide-tool-body:not(.home-guide-ima-body) {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .home-guide-tool-body:not(.home-guide-ima-body) .home-guide-tool-steps {
+    flex: none;
+    width: 100%;
+  }
+
+  .home-guide-tool-body:not(.home-guide-ima-body) .home-guide-code {
+    flex: none;
+    width: 100%;
+    min-height: 360px;
+  }
+}
+
+@media (max-width: 768px) {
+  .home-page {
+    --home-inline-padding: 24px;
+  }
+
+  .home-hero {
+    padding-top: 32px;
+    padding-bottom: 32px;
+  }
+
+  .home-hero-title {
+    margin-top: 20px;
+  }
+
+  .home-hero-title-main {
+    font-size: 32px;
+    line-height: 44px;
+  }
+
+  .home-hero-title-sub {
+    font-size: 24px;
+    line-height: 34px;
+  }
+
+  .home-hero-desc {
+    margin-top: 24px;
+    font-size: 14px;
+    line-height: 24px;
+  }
+
+  .home-hero-actions {
+    margin-top: 32px;
+    gap: 12px;
+  }
+
+  .home-hero-btn {
+    font-size: 14px;
+  }
+
+  .home-hero-btn-primary,
+  .home-hero-btn-secondary {
+    padding: 12px 20px;
+  }
+
+  .home-overview-inner {
+    gap: 48px;
+  }
+
+  .home-overview,
+  .home-scenes-upper,
+  .home-guide {
+    padding-top: 48px;
+    padding-bottom: 48px;
+  }
+
+  .home-overview-stats,
+  .home-overview-services,
+  .home-scenes-cards {
+    flex-direction: column;
+  }
+
+  .home-overview-stat-card,
+  .home-overview-service-card,
+  .home-scenes-card {
+    width: 100%;
+  }
+
+  .home-overview-stat-card {
+    padding: 24px 20px;
+  }
+
+  .home-scenes-lower {
+    margin: 24px 0 48px;
+    padding-top: 24px;
+    padding-bottom: 24px;
+  }
+
+  .home-scenes-demo {
+    gap: 24px;
+  }
+
+  .home-scenes-demo-title {
+    font-size: 24px;
+    line-height: 32px;
+  }
+
+  .home-guide-timeline-item {
+    max-width: 108px;
+  }
+
+  .home-guide-timeline-dot {
+    width: 40px;
+    height: 40px;
+    font-size: 16px;
+    line-height: 24px;
+  }
+
+  .home-guide-timeline-name {
+    font-size: 14px;
+    line-height: 20px;
+  }
+
+  .home-guide-timeline-desc {
+    font-size: 11px;
+    line-height: 16px;
+  }
+
+  .home-guide-timeline-line {
+    min-width: 12px;
+    max-width: 48px;
+    margin-top: 18px;
+    height: 3px;
+  }
+}
 </style>

+ 191 - 9
src/pages/mcp/onlineTest.vue

@@ -1,6 +1,10 @@
 <template>
   <div class="otp-page">
     <div class="otp-breadcrumb">
+      <button type="button" class="otp-breadcrumb-link" @click="goHome">
+        环评云助手MCP
+      </button>
+      <span class="otp-breadcrumb-sep">›</span>
       <button type="button" class="otp-breadcrumb-link" @click="goBackToService">
         {{ breadcrumbServiceName }}
       </button>
@@ -151,7 +155,7 @@
                 获取 Token
               </button>
               <div v-if="headerToken" class="otp-token-tip">
-                <icon-svg name="" size="12px" class="otp-token-tip-icon" />
+                <icon-svg name="tip_token_icon" size="12px" class="otp-token-tip-icon" />
                 <span>格式正确</span>
               </div>
             </div>
@@ -392,7 +396,7 @@
     <el-dialog
       v-model="resultDetailVisible"
       class="otp-result-dialog"
-      width="980px"
+      width="80%"
       align-center
       :show-close="false"
       append-to-body
@@ -712,7 +716,33 @@ const syncParamsFromTool = () => {
     requestJsonText.value = "{}";
     return;
   }
-  requestParams.value = mapReqFormParams(tool.reqParams);
+
+  // 切换工具时:同名参数保留已填写内容
+  const prevValues = {};
+  requestParams.value.forEach((item) => {
+    if (!item?.name) return;
+    prevValues[item.name] = item.value;
+  });
+  if (jsonMode.value) {
+    try {
+      const parsed = JSON.parse(requestJsonText.value || "{}");
+      if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
+        Object.keys(parsed).forEach((key) => {
+          if (!(key in prevValues)) prevValues[key] = parsed[key];
+        });
+      }
+    } catch {
+      // ignore invalid json
+    }
+  }
+
+  const nextParams = mapReqFormParams(tool.reqParams);
+  nextParams.forEach((item) => {
+    if (!Object.prototype.hasOwnProperty.call(prevValues, item.name)) return;
+    const val = prevValues[item.name];
+    item.value = val === null || val === undefined ? "" : String(val);
+  });
+  requestParams.value = nextParams;
   responseStructure.value = mapResStructure(tool.resParams);
   requestJsonText.value = JSON.stringify(buildRequestParamsFromForm(), null, 2);
 };
@@ -887,6 +917,10 @@ const breadcrumbServiceName = computed(
   () => currentService.value.name || currentService.value.serviceName || "MCP 服务",
 );
 
+const goHome = () => {
+  router.push({ name: "mcp" });
+};
+
 const goBackToService = () => {
   const id = activeServiceId.value || currentService.value.id;
   if (id) {
@@ -1218,6 +1252,8 @@ const runTest = async () => {
   min-height: calc(100vh - 64px);
   padding: 16px 20px 24px;
   background: #F7F9FC;
+  min-width: 0;
+  overflow-x: hidden;
 }
 
 .otp-breadcrumb {
@@ -1258,11 +1294,13 @@ const runTest = async () => {
   display: flex;
   align-items: flex-start;
   gap: 16px;
+  width: 100%;
+  min-width: 0;
 }
 
 .otp-side {
-  width: 300px;
-  flex-shrink: 0;
+  flex: 1 1 0;
+  min-width: 0;
   box-sizing: border-box;
   padding: 16px;
   background: #FFFFFF;
@@ -1314,12 +1352,15 @@ const runTest = async () => {
 }
 
 .otp-side-item-name {
+  flex: 1;
+  min-width: 0;
   font-family: PingFang SC, PingFang SC;
   font-weight: 600;
   font-size: 14px;
   color: #0F172A;
   line-height: 20px;
   text-align: left;
+  word-break: break-word;
 }
 
 .otp-side-item-status {
@@ -1343,10 +1384,11 @@ const runTest = async () => {
   color: #64748B;
   line-height: 18px;
   text-align: left;
+  word-break: break-word;
 }
 
 .otp-main {
-  flex: 1;
+  flex: 3 3 0;
   min-width: 0;
   display: flex;
   flex-direction: column;
@@ -1406,22 +1448,26 @@ const runTest = async () => {
 }
 
 .otp-header-title {
+  min-width: 0;
   font-family: PingFang SC, PingFang SC;
   font-weight: 600;
   font-size: 16px;
   color: #1E293B;
   line-height: 22px;
   text-align: left;
+  word-break: break-word;
 }
 
 .otp-header-desc {
   margin-top: 10px;
+  min-width: 0;
   font-family: PingFang SC, PingFang SC;
   font-weight: 400;
   font-size: 14px;
   color: #64748B;
   line-height: 20px;
   text-align: left;
+  word-break: break-word;
 }
 
 .otp-header-right {
@@ -1625,6 +1671,12 @@ const runTest = async () => {
   align-items: flex-start;
 }
 
+.otp-info-row--token .otp-info-label {
+  display: inline-flex;
+  align-items: center;
+  min-height: 36px;
+}
+
 .otp-token-wrap {
   flex: 1;
   min-width: 0;
@@ -1637,6 +1689,7 @@ const runTest = async () => {
   box-sizing: border-box;
   width: 100%;
   min-width: 0;
+  min-height: 36px;
   padding: 6px 12px;
   background: #F8FAFC;
   border-radius: 8px;
@@ -1647,6 +1700,7 @@ const runTest = async () => {
   display: inline-flex;
   align-items: center;
   justify-content: center;
+  min-height: 36px;
   padding: 8px 14px;
   border: none;
   border-radius: 8px;
@@ -2175,8 +2229,8 @@ const runTest = async () => {
 }
 
 .otp-result {
-  width: 620px;
-  flex-shrink: 0;
+  flex: 1.5 1.5 0;
+  min-width: 0;
 }
 
 .otp-result-card {
@@ -2461,6 +2515,126 @@ const runTest = async () => {
   text-align: left;
   cursor: pointer;
 }
+
+@media (max-width: 1200px) {
+  .otp-layout {
+    gap: 12px;
+  }
+
+  .otp-page {
+    padding: 12px 16px 20px;
+  }
+
+  .otp-side {
+    padding: 12px;
+  }
+
+  .otp-side-title {
+    font-size: 14px;
+    line-height: 20px;
+
+    :deep(.svg-icon) {
+      width: 16px !important;
+      height: 16px !important;
+    }
+  }
+
+  .otp-side-list {
+    margin-top: 10px;
+    gap: 8px;
+  }
+
+  .otp-side-item {
+    padding: 10px;
+  }
+
+  .otp-side-item-name {
+    font-size: 13px;
+    line-height: 18px;
+  }
+
+  .otp-side-item-desc {
+    margin-top: 6px;
+    font-size: 11px;
+    line-height: 16px;
+  }
+
+  .otp-card-header {
+    padding: 14px;
+  }
+
+  .otp-header-top {
+    flex-wrap: wrap;
+    row-gap: 10px;
+  }
+
+  .otp-header-right {
+    flex-direction: row;
+    align-items: center;
+    width: 100%;
+    justify-content: flex-start;
+    flex-wrap: wrap;
+    gap: 8px;
+  }
+
+  .otp-header-icon {
+    width: 28px;
+    height: 28px;
+  }
+
+  .otp-header-title {
+    font-size: 15px;
+    line-height: 21px;
+  }
+
+  .otp-header-desc {
+    margin-top: 8px;
+    font-size: 13px;
+    line-height: 18px;
+  }
+
+  .otp-info-label {
+    width: 68px;
+    font-size: 13px;
+  }
+
+  .otp-info-value,
+  .otp-token-text {
+    font-size: 13px;
+  }
+
+  .otp-token-get-btn {
+    padding: 8px 12px;
+    font-size: 13px;
+  }
+
+  .otp-result-card {
+    min-height: 480px;
+    padding: 12px;
+  }
+
+  .otp-result-code {
+    font-size: 11px;
+    line-height: 16px;
+  }
+}
+
+@media (max-width: 768px) {
+  .otp-layout {
+    flex-direction: column;
+  }
+
+  .otp-side,
+  .otp-main,
+  .otp-result {
+    flex: none;
+    width: 100%;
+  }
+
+  .otp-result-card {
+    min-height: 360px;
+  }
+}
 </style>
 
 <style lang="scss">
@@ -2477,6 +2651,7 @@ const runTest = async () => {
 }
 
 .otp-result-dialog.el-dialog {
+  max-width: 960px;
   padding: 0;
   border-radius: 8px;
   border: 1px solid #E2E8F0;
@@ -2489,6 +2664,9 @@ const runTest = async () => {
 
   .el-dialog__body {
     padding: 16px;
+    box-sizing: border-box;
+    max-height: 80vh;
+    overflow: hidden;
   }
 }
 
@@ -2496,6 +2674,9 @@ const runTest = async () => {
   display: flex;
   flex-direction: column;
   gap: 16px;
+  height: calc(80vh - 32px);
+  min-height: 360px;
+  box-sizing: border-box;
 }
 
 .otp-detail-head {
@@ -2634,7 +2815,8 @@ const runTest = async () => {
 
 .otp-detail-viewer {
   box-sizing: border-box;
-  height: 418px;
+  flex: 1;
+  min-height: 0;
   overflow: hidden;
   border-radius: 8px;
   background: #1E293B;

+ 426 - 23
src/pages/mcp/serverDetail.vue

@@ -1,6 +1,41 @@
 <template>
   <div class="mcp-server-page">
-    <div class="mcp-server-main">
+    <div v-if="pageLoading" class="mcp-server-main">
+      <div class="mcp-server-card mcp-server-card-overview mcp-server-skeleton-card">
+        <div class="mcp-server-skeleton-bar mcp-server-skeleton-bar--gradient"></div>
+        <div class="mcp-server-skeleton-panel">
+          <div class="mcp-server-skeleton-header">
+            <div class="mcp-server-skeleton-block mcp-server-skeleton-block--circle"></div>
+            <div class="mcp-server-skeleton-header-copy">
+              <div class="mcp-server-skeleton-block mcp-server-skeleton-block--title"></div>
+              <div class="mcp-server-skeleton-block mcp-server-skeleton-block--desc"></div>
+              <div class="mcp-server-skeleton-block mcp-server-skeleton-block--desc-short"></div>
+            </div>
+          </div>
+          <div class="mcp-server-skeleton-metrics">
+            <div v-for="n in 3" :key="`metric-${n}`" class="mcp-server-skeleton-metric"></div>
+          </div>
+          <div class="mcp-server-skeleton-tags">
+            <div v-for="n in 4" :key="`tag-${n}`" class="mcp-server-skeleton-block mcp-server-skeleton-block--tag"></div>
+          </div>
+        </div>
+      </div>
+      <div class="mcp-server-card mcp-server-card-detail mcp-server-skeleton-card">
+        <div class="mcp-server-skeleton-block mcp-server-skeleton-block--section-title"></div>
+        <div class="mcp-server-skeleton-block mcp-server-skeleton-block--section-sub"></div>
+        <div class="mcp-server-skeleton-tools">
+          <div v-for="n in 2" :key="`tool-${n}`" class="mcp-server-skeleton-tool"></div>
+        </div>
+        <div class="mcp-server-skeleton-block mcp-server-skeleton-block--section-title"></div>
+        <div class="mcp-server-skeleton-params">
+          <div v-for="n in 4" :key="`param-${n}`" class="mcp-server-skeleton-param"></div>
+        </div>
+        <div class="mcp-server-skeleton-block mcp-server-skeleton-block--section-title"></div>
+        <div class="mcp-server-skeleton-code"></div>
+      </div>
+    </div>
+
+    <div v-show="!pageLoading" class="mcp-server-main">
       <div
         class="mcp-server-card mcp-server-card-overview"
         :class="{ 'is-consult-open': consultOpen }"
@@ -29,12 +64,15 @@
                 <icon-svg name="effect_experience" size="14px" />
                 <span>在线测试</span>
               </button>
-              <div ref="consultRef" class="mcp-server-consult">
+              <div
+                class="mcp-server-consult"
+                @mouseenter="consultOpen = true"
+                @mouseleave="consultOpen = false"
+              >
                 <button
                   type="button"
                   class="mcp-server-btn mcp-server-btn-outline"
                   :class="{ 'is-active': consultOpen }"
-                  @click="consultOpen = !consultOpen"
                 >
                   <icon-svg
                     :name="consultOpen ? 'online_reference_services_blue' : 'online_reference_services_white'"
@@ -222,6 +260,20 @@
         </div>
       </div>
 
+      <template v-if="pageLoading">
+        <div class="mcp-server-side-card mcp-server-skeleton-card">
+          <div class="mcp-server-skeleton-block mcp-server-skeleton-block--side-title"></div>
+          <div class="mcp-server-skeleton-block mcp-server-skeleton-block--url"></div>
+          <div class="mcp-server-skeleton-block mcp-server-skeleton-block--side-title"></div>
+          <div class="mcp-server-skeleton-code mcp-server-skeleton-code--side"></div>
+        </div>
+        <div class="mcp-server-side-card mcp-server-skeleton-card">
+          <div class="mcp-server-skeleton-block mcp-server-skeleton-block--side-title"></div>
+          <div v-for="n in 3" :key="`related-${n}`" class="mcp-server-skeleton-related"></div>
+        </div>
+      </template>
+
+      <template v-else>
       <div class="mcp-server-side-card mcp-server-side-card-connect">
         <div class="mcp-server-connect-head">
           <icon-svg name="connection_status" size="16px" class="mcp-server-connect-head-icon" />
@@ -296,11 +348,12 @@
           </button>
         </div>
       </div>
+      </template>
     </aside>
   </div>
 </template>
 <script setup>
-import { computed, onBeforeUnmount, onMounted, ref, watch } from "vue";
+import { computed, ref, watch } from "vue";
 import { useRoute, useRouter } from "vue-router";
 import { ElMessage } from "element-plus";
 import { getMcpServerDetail, getMcpServerList, getMcpToolDetail, createMcpToken } from "@/api/mcp";
@@ -323,21 +376,7 @@ const toolDetail = ref(null);
 const selectedToolIndex = ref(0);
 const tokenVisible = ref(false);
 const consultOpen = ref(false);
-const consultRef = ref(null);
-
-const handleConsultOutsideClick = (event) => {
-  if (!consultOpen.value) return;
-  if (consultRef.value?.contains(event.target)) return;
-  consultOpen.value = false;
-};
-
-onMounted(() => {
-  document.addEventListener("click", handleConsultOutsideClick);
-});
-
-onBeforeUnmount(() => {
-  document.removeEventListener("click", handleConsultOutsideClick);
-});
+const pageLoading = ref(true);
 
 const serverRequestId = ref(0);
 const toolRequestId = ref(0);
@@ -632,6 +671,7 @@ const fetchServerDetail = async () => {
   ++toolRequestId.value;
   selectedToolIndex.value = 0;
   toolDetail.value = null;
+  pageLoading.value = true;
 
   try {
     const id = await resolveServerId();
@@ -670,6 +710,10 @@ const fetchServerDetail = async () => {
     console.error(error);
     serverDetail.value = null;
     toolDetail.value = null;
+  } finally {
+    if (requestId === serverRequestId.value) {
+      pageLoading.value = false;
+    }
   }
 };
 
@@ -728,7 +772,7 @@ const navigateTo = (key) => {
 };
 
 const goOnlineTest = () => {
-  if (!requireUserLogin()) return;
+  if (!requireUserLogin("请先登录", { redirect: true })) return;
   const id = route.query.id || serverDetail.value?.id;
   router.push({
     name: "mcpOnlineTest",
@@ -756,7 +800,180 @@ const createToken = async () => {
   align-items: flex-start;
   gap: 16px;
   width: 100%;
+  min-width: 0;
   min-height: 100%;
+  --mcp-server-side-width: 460px;
+}
+
+@keyframes mcp-server-skeleton-shimmer {
+  0% {
+    background-position: 200% 0;
+  }
+
+  100% {
+    background-position: -200% 0;
+  }
+}
+
+.mcp-server-skeleton-card {
+  overflow: hidden;
+}
+
+.mcp-server-skeleton-block,
+.mcp-server-skeleton-metric,
+.mcp-server-skeleton-tool,
+.mcp-server-skeleton-param,
+.mcp-server-skeleton-code,
+.mcp-server-skeleton-related {
+  background: linear-gradient(90deg, #eef2f7 25%, #e2e8f0 50%, #eef2f7 75%);
+  background-size: 200% 100%;
+  animation: mcp-server-skeleton-shimmer 1.2s ease-in-out infinite;
+  border-radius: 6px;
+}
+
+.mcp-server-skeleton-bar--gradient {
+  height: 4px;
+  border-radius: 8px 8px 0 0;
+  background: linear-gradient(90deg, #eef2f7 25%, #e2e8f0 50%, #eef2f7 75%);
+  background-size: 200% 100%;
+  animation: mcp-server-skeleton-shimmer 1.2s ease-in-out infinite;
+}
+
+.mcp-server-skeleton-panel {
+  padding: 24px;
+}
+
+.mcp-server-skeleton-header {
+  display: flex;
+  gap: 16px;
+}
+
+.mcp-server-skeleton-header-copy {
+  flex: 1;
+  min-width: 0;
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.mcp-server-skeleton-block--circle {
+  width: 48px;
+  height: 48px;
+  border-radius: 12px;
+  flex-shrink: 0;
+}
+
+.mcp-server-skeleton-block--title {
+  width: 42%;
+  height: 24px;
+}
+
+.mcp-server-skeleton-block--desc {
+  width: 88%;
+  height: 16px;
+}
+
+.mcp-server-skeleton-block--desc-short {
+  width: 62%;
+  height: 16px;
+}
+
+.mcp-server-skeleton-metrics {
+  display: flex;
+  gap: 16px;
+  margin-top: 24px;
+}
+
+.mcp-server-skeleton-metric {
+  flex: 1;
+  height: 72px;
+  border-radius: 10px;
+}
+
+.mcp-server-skeleton-tags {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-top: 20px;
+}
+
+.mcp-server-skeleton-block--tag {
+  width: 72px;
+  height: 28px;
+  border-radius: 14px;
+}
+
+.mcp-server-skeleton-block--section-title {
+  width: 120px;
+  height: 20px;
+  margin-bottom: 12px;
+}
+
+.mcp-server-skeleton-block--section-sub {
+  width: 180px;
+  height: 16px;
+  margin-bottom: 16px;
+}
+
+.mcp-server-skeleton-tools {
+  display: flex;
+  flex-direction: column;
+  gap: 10px;
+  margin-bottom: 24px;
+}
+
+.mcp-server-skeleton-tool {
+  height: 44px;
+  border-radius: 8px;
+}
+
+.mcp-server-skeleton-params {
+  display: flex;
+  flex-direction: column;
+  gap: 0;
+  margin-bottom: 24px;
+  padding: 8px 20px;
+  background: #f8fafc;
+  border: 1px solid #e2e8f0;
+  border-radius: 8px;
+}
+
+.mcp-server-skeleton-param {
+  height: 52px;
+  margin: 16px 0;
+  border-radius: 6px;
+}
+
+.mcp-server-skeleton-code {
+  height: 220px;
+  border-radius: 8px;
+}
+
+.mcp-server-skeleton-code--side {
+  height: 180px;
+  margin-top: 12px;
+}
+
+.mcp-server-skeleton-block--side-title {
+  width: 96px;
+  height: 18px;
+  margin-bottom: 12px;
+}
+
+.mcp-server-skeleton-block--url {
+  width: 100%;
+  height: 44px;
+  margin-bottom: 16px;
+}
+
+.mcp-server-skeleton-related {
+  height: 56px;
+  margin-top: 12px;
+  border-radius: 8px;
+}
+
+.mcp-server-card-detail.mcp-server-skeleton-card {
+  padding: 24px;
 }
 
 .mcp-server-main {
@@ -968,24 +1185,28 @@ const createToken = async () => {
   grid-column: 2;
   grid-row: 1;
   align-self: center;
+  min-width: 0;
   font-family: PingFang SC, PingFang SC;
   font-weight: 400;
   font-size: 30px;
   color: #1E293B;
   line-height: 38px;
   text-align: left;
+  word-break: break-word;
 }
 
 .mcp-server-desc {
   grid-column: 1 / 3;
   grid-row: 2;
   margin-top: 0;
+  min-width: 0;
   font-family: PingFang SC, PingFang SC;
   font-weight: 400;
   font-size: 15px;
   color: #64748B;
   line-height: 26px;
   text-align: left;
+  word-break: break-word;
 }
 
 .mcp-server-header-actions {
@@ -1044,6 +1265,15 @@ const createToken = async () => {
   background: #FFFFFF;
   box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
   border-radius: 8px;
+
+  &::before {
+    content: "";
+    position: absolute;
+    top: -8px;
+    left: 0;
+    right: 0;
+    height: 8px;
+  }
 }
 
 .mcp-server-consult-popover-img {
@@ -1213,7 +1443,7 @@ const createToken = async () => {
 .mcp-server-params-table {
   margin-top: 12px;
   box-sizing: border-box;
-  padding: 8px 20px;
+  padding: 0px 20px;
   background: #F8FAFC;
   border: 1px solid #E2E8F0;
   border-radius: 8px;
@@ -1223,7 +1453,7 @@ const createToken = async () => {
   display: flex;
   align-items: flex-start;
   gap: 12px;
-  padding: 8px 0;
+  padding: 16px 0;
   border-bottom: 1px solid #E2E8F0;
 
   &:last-child {
@@ -1515,8 +1745,9 @@ const createToken = async () => {
 }
 
 .mcp-server-side {
-  width: 460px;
+  width: var(--mcp-server-side-width);
   flex-shrink: 0;
+  min-width: 0;
   display: flex;
   flex-direction: column;
   gap: 16px;
@@ -1886,6 +2117,178 @@ const createToken = async () => {
   line-height: 18px;
   text-align: left;
 }
+
+@media (max-width: 1200px) {
+  .mcp-server-page {
+    --mcp-server-side-width: 260px;
+    gap: 12px;
+  }
+
+  .mcp-server-card,
+  .mcp-server-side-card-connect {
+    padding: 16px;
+  }
+
+  .mcp-server-overview-content {
+    padding: 16px;
+  }
+
+  .mcp-server-title {
+    font-size: 22px;
+    line-height: 30px;
+  }
+
+  .mcp-server-desc {
+    font-size: 14px;
+    line-height: 22px;
+  }
+
+  .mcp-server-header {
+    flex-wrap: wrap;
+    row-gap: 12px;
+  }
+
+  .mcp-server-header-left {
+    column-gap: 12px;
+    row-gap: 8px;
+  }
+
+  .mcp-server-header-icon {
+    padding: 8px;
+    border-radius: 10px;
+    box-shadow: 0px 4px 12px 0px rgba(5, 150, 105, 0.22);
+
+    :deep(.svg-icon) {
+      width: 20px !important;
+      height: 20px !important;
+    }
+  }
+
+  .mcp-server-header-icon-img {
+    width: 20px;
+    height: 20px;
+  }
+
+  .mcp-server-header-actions {
+    flex-direction: row;
+    align-items: center;
+    width: 100%;
+    justify-content: flex-start;
+    gap: 10px;
+  }
+
+  .mcp-server-btn-primary,
+  .mcp-server-btn-outline {
+    padding: 10px 16px;
+    font-size: 13px;
+    line-height: 18px;
+  }
+
+  .mcp-server-metrics {
+    flex-wrap: nowrap;
+    gap: 8px;
+    margin-top: 12px;
+  }
+
+  .mcp-server-metric {
+    padding: 10px 8px;
+    gap: 6px;
+    border-radius: 10px;
+  }
+
+  .mcp-server-metric-icon {
+    padding: 6px;
+    border-radius: 8px;
+
+    :deep(.svg-icon) {
+      width: 16px !important;
+      height: 16px !important;
+    }
+  }
+
+  .mcp-server-metric-value {
+    font-size: 16px;
+    line-height: 22px;
+  }
+
+  .mcp-server-metric-label {
+    margin-top: 2px;
+    font-size: 11px;
+    line-height: 14px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
+  }
+
+  .mcp-server-tags {
+    gap: 8px;
+    margin-top: 12px;
+  }
+
+  .mcp-server-tag {
+    padding: 4px 10px;
+    font-size: 12px;
+    line-height: 16px;
+  }
+
+  .mcp-server-connect-url-box {
+    flex-direction: column;
+    align-items: stretch;
+    gap: 10px;
+    padding: 12px;
+  }
+
+  .mcp-server-connect-url-right {
+    width: 100%;
+    justify-content: space-between;
+    gap: 12px;
+  }
+
+  .mcp-server-connect-url-text {
+    font-size: 12px;
+    line-height: 18px;
+  }
+
+  .mcp-server-connect-code-pre {
+    font-size: 11px;
+    line-height: 18px;
+  }
+
+  .mcp-server-token-value {
+    font-size: 12px;
+  }
+
+  .mcp-server-token-head-icon,
+  .mcp-server-connect-head-icon {
+    padding: 6px;
+
+    :deep(.svg-icon) {
+      width: 12px !important;
+      height: 12px !important;
+    }
+  }
+
+  .mcp-server-side-card {
+    padding: 12px;
+  }
+
+  .mcp-server-token-head-title,
+  .mcp-server-connect-head-title {
+    font-size: 14px;
+    line-height: 20px;
+  }
+
+  .mcp-server-params-row {
+    flex-wrap: wrap;
+    row-gap: 8px;
+  }
+
+  .mcp-server-params-name-col {
+    width: auto;
+    max-width: none;
+    min-width: 0;
+  }
+}
 </style>
 
 <style lang="scss">

+ 4 - 1
src/pages/pc/index.vue

@@ -60,7 +60,10 @@ watch(
 
 const openLoginDialog = () => {
   if (route.name === "login") return;
-  router.push({ name: "login" });
+  router.push({
+    name: "login",
+    query: { redirect: route.fullPath || "/" },
+  });
 };
 
 const saveUserInfo = async (userInfo) => {

+ 32 - 40
src/router/index.js

@@ -26,51 +26,41 @@ const routes = [
         },
       },
       {
-        path: "mcp",
-        component: () => import("@/pages/mcp/layout.vue"),
+        path: "online",
+        name: "mcpOnlineTest",
+        component: () => import("@/pages/mcp/onlineTest.vue"),
+      },
+      {
+        path: "auth",
+        name: "mcpAuth",
+        component: () => import("@/pages/mcp/authSuccess.vue"),
+        meta: {
+          title: "授权成功",
+        },
+      },
+      {
+        path: "",
+        component: () => import("@/pages/home/mcpIndex.vue"),
         children: [
           {
-            path: "online",
-            name: "mcpOnlineTest",
-            component: () => import("@/pages/mcp/onlineTest.vue"),
+            path: "guide",
+            name: "guide",
+            component: () => import("@/pages/guide/index.vue"),
+          },
+          {
+            path: "server",
+            name: "mcpServer",
+            component: () => import("@/pages/mcp/serverDetail.vue"),
           },
           {
-            path: "auth",
-            name: "mcpAuth",
-            component: () => import("@/pages/mcp/authSuccess.vue"),
-            meta: {
-              title: "授权成功",
-            },
+            path: "faq",
+            name: "faq",
+            component: () => import("@/pages/help/faq.vue"),
           },
           {
-            path: "",
-            component: () => import("@/pages/home/mcpIndex.vue"),
-            children: [
-              {
-                path: "",
-                redirect: { name: "mcp" },
-              },
-              {
-                path: "guide",
-                name: "guide",
-                component: () => import("@/pages/guide/index.vue"),
-              },
-              {
-                path: "server",
-                name: "mcpServer",
-                component: () => import("@/pages/mcp/serverDetail.vue"),
-              },
-              {
-                path: "faq",
-                name: "faq",
-                component: () => import("@/pages/help/faq.vue"),
-              },
-              {
-                path: "support",
-                name: "support",
-                component: () => import("@/pages/mcp/empty.vue"),
-              },
-            ],
+            path: "support",
+            name: "support",
+            component: () => import("@/pages/mcp/empty.vue"),
           },
         ],
       },
@@ -112,9 +102,11 @@ router.beforeEach((to, _from, next) => {
   }
 
   if (AUTH_REQUIRED_ROUTES.has(to.name) && !isLoggedIn()) {
+    // 在线测试未登录:登录后回首页;授权页仍回到原目标页
+    const redirect = to.name === "mcpOnlineTest" ? "/" : to.fullPath;
     next({
       name: "login",
-      query: { redirect: to.fullPath },
+      query: { redirect },
     });
     return;
   }

+ 1 - 1
vite.config.js

@@ -24,7 +24,7 @@ export default defineConfig({
       "@": resolve("src"),
     },
   },
-  base: "/", // History 模式下必须用绝对路径,避免 /mcp/guide 等深层路由把 JS 解析成 /mcp/assets/...
+  base: "/", // History 模式下必须用绝对路径,避免深层路由把 JS 解析成相对 assets 路径
   // 反向代理
   server: {
     port: 8060,

Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff