Prechádzať zdrojové kódy

完成页面icon进行补充80%

肖栋 2 týždňov pred
rodič
commit
fc9e05c202

+ 16 - 1
src/api/mcp.js

@@ -1,10 +1,15 @@
-import { get } from "@/utils/request";
+import { get, post } from "@/utils/request";
 
 /** 获取 MCP Token */
 export function getMcpToken() {
   return get("/mcp/token");
 }
 
+/** 创建 MCP Token */
+export function createMcpToken(data = {}) {
+  return post("/mcp/token/create", data);
+}
+
 /** 获取 MCP Server 列表 */
 export function getMcpServerList() {
   return get("/mcp/server/list");
@@ -20,3 +25,13 @@ export function getMcpToolDetail(id) {
   return get(`/mcp/tool/${id}`);
 }
 
+/** 连接 MCP Server */
+export function connectMcpServer(params = {}) {
+  return get("/mcp/server/connect", params);
+}
+
+/** MCP 工具在线测试调用:/mcp/handle/{code} */
+export function callMcpHandle(code, params = {}, headers = {}) {
+  return get(`/mcp/handle/${code}`, params, headers);
+}
+

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/connection_status.svg


+ 1 - 0
src/assets/icons/copy_icon_violet.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"><defs><clipPath id="master_svg0_19_7487"><rect x="0" y="0" width="11" height="11" rx="0"/></clipPath></defs><g clip-path="url(#master_svg0_19_7487)"><path d="M4.812502373486328,0C4.054106973486328,0,3.437507673486328,0.61659908,3.437507673486328,1.3749945L3.437507673486328,6.8749733C3.437507673486328,7.6333675,4.054106973486328,8.2499676,4.812502373486328,8.2499676L8.937485673486329,8.2499676C9.695879973486328,8.2499676,10.312479973486328,7.6333675,10.312479973486328,6.8749733L10.312479973486328,2.5652239C10.312479973486328,2.1913974,10.159941673486328,1.8326099,9.889239273486329,1.57265L8.649596173486328,0.38242033C8.39393327348633,0.13749945,8.052332373486328,0,7.697841673486328,0L4.812502373486328,0ZM2.062513573486328,2.749989C1.304118153486328,2.749989,0.6875190734863281,3.3665881,0.6875190734863281,4.1249838L0.6875190734863281,9.6249619C0.6875190734863281,10.383357,1.304118153486328,10.999956,2.062513573486328,10.999956L6.187496673486328,10.999956C6.945891373486329,10.999956,7.562491373486328,10.383357,7.562491373486328,9.6249619L7.562491373486328,9.2812138L6.187496673486328,9.2812138L6.187496673486328,9.6249619L2.062513573486328,9.6249619L2.062513573486328,4.1249838L2.4062621734863283,4.1249838L2.4062621734863283,2.749989L2.062513573486328,2.749989Z" fill="#A78BFA" fill-opacity="1" style="mix-blend-mode:passthrough"/></g></svg>

+ 1 - 0
src/assets/icons/copy_icon_white.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="M4.8125119233398435,0C4.054126723339843,0,3.4375360233398435,0.61659086,3.4375360233398435,1.374976L3.4375360233398435,6.8748803C3.4375360233398435,7.633265,4.054126723339843,8.249856,4.8125119233398435,8.249856L8.937439923339843,8.249856C9.695825623339843,8.249856,10.312416123339844,7.633265,10.312416123339844,6.8748803L10.312416123339844,2.5651896C10.312416123339844,2.1913679,10.159879723339843,1.8325851,9.889181123339844,1.5726287L8.649554723339843,0.38241521C8.393895123339844,0.1374976,8.052299023339843,0,7.697813023339844,0L4.8125119233398435,0ZM2.062559923339844,2.7499521C1.3041747833398438,2.7499521,0.6875839233398438,3.3665428,0.6875839233398438,4.124928L0.6875839233398438,9.6248322C0.6875839233398438,10.383217,1.3041747833398438,10.999808,2.062559923339844,10.999808L6.187488123339843,10.999808C6.945873223339844,10.999808,7.562464223339844,10.383217,7.562464223339844,9.6248322L7.562464223339844,9.2810879L6.187488123339843,9.2810879L6.187488123339843,9.6248322L2.062559923339844,9.6248322L2.062559923339844,4.124928L2.406304023339844,4.124928L2.406304023339844,2.7499521L2.062559923339844,2.7499521Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

+ 1 - 0
src/assets/icons/http_copy.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="16" height="16" viewBox="0 0 16 16"><path d="M13,10.5L7,10.5C6.7249999,10.5,6.5,10.275,6.5,10L6.5,2C6.5,1.725,6.7249999,1.5,7,1.5L11.171875,1.5C11.303125,1.5,11.43125,1.553125,11.525,1.646875L13.353125,3.4749999C13.446875,3.5687499,13.5,3.6968749,13.5,3.828125L13.5,10C13.5,10.275,13.275,10.5,13,10.5ZM7,12L13,12C14.103125,12,15,11.103125,15,10L15,3.828125C15,3.296875,14.790625,2.7874999,14.415625,2.4124999L12.584375,0.58437502C12.209375,0.20937499,11.703125,0,11.171875,0L7,0C5.8968749,0,5,0.89687502,5,2L5,10C5,11.103125,5.8968749,12,7,12ZM3,4C1.89687502,4,1,4.8968749,1,6L1,14C1,15.103125,1.89687502,16,3,16L9,16C10.1031246,16,11,15.103125,11,14L11,13.5L9.5,13.5L9.5,14C9.5,14.275,9.2749996,14.5,9,14.5L3,14.5C2.725,14.5,2.5,14.275,2.5,14L2.5,6C2.5,5.7249999,2.725,5.5,3,5.5L3.5,5.5L3.5,4L3,4Z" fill="#94A3B8" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/http_url.svg


+ 1 - 0
src/assets/icons/key_token.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="14.000127792358398" height="14.000127792358398" viewBox="0 0 14.000127792358398 14.000127792358398"><path d="M9.1875839,9.6250877C11.845421,9.6250877,14.000128,7.4703813,14.000128,4.8125439C14.000128,2.1547067,11.845421,0,9.1875839,0C6.5297465,0,4.3750401,2.1547072,4.3750401,4.8125439C4.3750401,5.3238769,4.4543376,5.8188033,4.601995,6.2809167L0.19140799,10.691504C0.068360001,10.814552,0,10.98135,0,11.156352L0,13.343872C0,13.707546,0.29258078,14.000128,0.65625596,14.000128L2.843776,14.000128C3.2074513,14.000128,3.5000319,13.707546,3.5000319,13.343872L3.5000319,12.250112L4.593792,12.250112C4.9574671,12.250112,5.2500477,11.957531,5.2500477,11.593856L5.2500477,10.500095L6.3438077,10.500095C6.5188093,10.500095,6.6856079,10.431736,6.8086557,10.308688L7.7192106,9.3981333C8.181324,9.5457907,8.6762505,9.6250877,9.1875839,9.6250877ZM10.281343,2.6250238C10.885411,2.6250238,11.375104,3.114717,11.375104,3.7187839C11.375104,4.3228507,10.885411,4.8125439,10.281343,4.8125439C9.6772766,4.8125439,9.1875839,4.3228507,9.1875839,3.7187839C9.1875839,3.114717,9.6772766,2.6250238,10.281343,2.6250238Z" fill="#FFFFFF" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

+ 1 - 0
src/assets/icons/number_of_files.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="20" height="20" viewBox="0 0 20 20"><path d="M0.944889305,9.4723133875L0.080078125,12.0594186875L0.080078125,4.7231804875000005C0.080078125,3.4296280875000003,1.131776525,2.3779296875,2.425328925,2.3779296875L7.507927925,2.3779296875C8.013622325,2.3779296875,8.508323625,2.5428301375,8.915077225,2.8469798275000002L10.322228125,3.9023426875C10.523773125,4.0562495875,10.772956125,4.1368677875,11.025803125,4.1368677875L15.324209125,4.1368677875C16.617761125,4.1368677875,17.669460125,5.1885659875000005,17.669460125,6.4821185875L17.669460125,7.0684313875L4.279543925,7.0684313875C2.766124225,7.0684313875,1.421269525,8.0358471875,0.941226005,9.4723133875L0.944889305,9.4723133875ZM16.401558125,17.6220606875L2.520604625,17.6220606875C1.318663625,17.6220606875,0.472174645,16.4457706875,0.853277925,15.3061246875L2.612216025,10.0293101875C2.850405725,9.3110770875,3.524665325,8.8273691875,4.279542925,8.8273691875L18.160496125,8.8273691875C19.362434125,8.8273691875,20.208925125,10.0036596875,19.827822125,11.1433047875L18.068886125,16.420119687499998C17.830696125,17.1383536875,17.156435125,17.6220606875,16.401558125,17.6220606875Z" fill="#10B981" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/online_reference_services_blue.svg


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/online_reference_services_white.svg


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/related_service.svg


+ 1 - 0
src/assets/icons/related_service_redirection.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="12" height="12" viewBox="0 0 12 12"><path d="M11.780104462493897,6.529699572384644C12.073078462493896,6.236724572384643,12.073078462493896,5.760933172384644,11.780104462493897,5.467958172384644L8.030024062493897,1.7178780323846436C7.737049062493896,1.4249030203846436,7.261258162493896,1.4249030203846436,6.968283162493896,1.7178780323846436C6.675308262493896,2.0108530523846433,6.675308262493896,2.486644562384644,6.968283162493896,2.7796195723846435L9.438647762493897,5.249984772384643L0.7501814324938965,5.249984772384643C0.3353288424938965,5.249984772384643,0.00016546249389648438,5.585148072384643,0.00016546249389648438,6.000000672384644C0.00016546249389648438,6.414853772384643,0.3353288424938965,6.7500169723846435,0.7501814324938965,6.7500169723846435L9.438647762493897,6.7500169723846435L6.968283162493896,9.220381472384643C6.675308262493896,9.513356972384644,6.675308262493896,9.989147872384644,6.968283162493896,10.282122372384643C7.261258162493896,10.575097772384643,7.737049062493896,10.575097772384643,8.030024062493897,10.282122372384643L11.780104462493897,6.532042772384644L11.780104462493897,6.529699572384644Z" fill="#94A3B8" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/requests_in_the_past_days.svg


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/required_parameter.svg


+ 1 - 0
src/assets/icons/response_time.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="20" height="20" viewBox="0 0 20 20"><path d="M14.220277697937012,0.22510133653320313C14.657745697937012,0.5412545765332031,14.819496697937012,1.1147417965332032,14.620982697937011,1.614705096533203L11.738841997937012,8.823733296533202L17.05830469793701,8.823733296533202C17.554590697937012,8.823733296533202,17.99573569793701,9.132533996533203,18.16484169793701,9.599411996533203C18.333946697937012,10.066289696533204,18.19057569793701,10.588310696533203,17.811924697937012,10.904463696533202L7.224468697937012,19.727342696533203C6.809057697937011,20.072904696533204,6.217189797937012,20.091285696533202,5.779722197937012,19.775134696533204C5.342254197937011,19.458976696533202,5.180501197937012,18.885490696533203,5.379016197937012,18.385530696533202L8.261156997937011,11.176502696533204L2.9416955979370116,11.176502696533204C2.445408527937012,11.176502696533204,2.004264477937012,10.867701696533203,1.8351592499370117,10.400823696533203C1.6660540179370118,9.933945696533202,1.8094258499370117,9.411926296533203,2.188074497937012,9.095772696533203L12.775530697937011,0.2728916965332031C13.190940697937013,-0.07267111546679687,13.782809697937012,-0.09105211446679687,14.220277697937012,0.22510133653320313Z" fill="#1678FF" fill-opacity="1"/></svg>

+ 1 - 0
src/assets/icons/return_example.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="16" height="16" viewBox="0 0 16 16"><path d="M6.382680888360595,0.8242394846245117C6.757620788360596,0.9804644776245117,7.001331788360596,1.3429064776245117,7.001331788360596,1.7490914476245116L7.001331788360596,4.248691277624512L10.500772288360595,4.248691277624512C13.537786288360596,4.248691277624512,15.999892288360595,6.710797277624511,15.999892288360595,9.747811277624512C15.999892288360595,13.287869877624512,13.453424288360596,14.868865877624511,12.869142288360596,15.187564877624512C12.791029288360596,15.231308877624512,12.703544288360595,15.246930877624512,12.616058288360597,15.246930877624512C12.275487288360596,15.246930877624512,12.000530288360595,14.968849877624512,12.000530288360595,14.631404877624512C12.000530288360595,14.397066877624512,12.134883288360596,14.181475877624512,12.306730288360596,14.022126877624512C12.600434288360596,13.747171877624512,13.000370288360596,13.197257877624512,13.000370288360596,12.250534877624512C13.000370288360596,10.594550177624512,11.656836288360596,9.251015677624512,10.000850288360596,9.251015677624512L7.001330888360596,9.251015677624512L7.001330888360596,11.750614877624512C7.001330888360596,12.153674877624512,6.757619888360596,12.519240877624512,6.382679988360596,12.675466877624512C6.007739588360596,12.831691877624511,5.579683288360596,12.74420587762451,5.292229188360595,12.459876877624511L0.2930291583605957,7.4606766776245115C-0.0975333306394043,7.070114577624512,-0.0975333306394043,6.435840577624512,0.2930291583605957,6.0452785776245115L5.292229188360595,1.0460785076245118C5.5796827883605955,0.7586244992245117,6.007739088360596,0.6742630376245117,6.382679488360596,0.8304880336245117L6.382680888360595,0.8242394846245117Z" fill="#2563EB" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
src/assets/icons/tool_description.svg


+ 1 - 0
src/assets/icons/tool_description_off.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="16" height="16.00006103515625" viewBox="0 0 16 16.00006103515625"><path d="M7.0398779370544435,0.2561323C7.633026637054443,-0.085377455,8.363976937054442,-0.085377455,8.957125137054444,0.2561323L14.226562137054444,3.2967677C14.819710137054443,3.6382773,15.185187137054443,4.273366,15.185187137054443,4.9563851L15.185187137054443,11.037656C15.185187137054443,11.723671,14.819710137054443,12.355763,14.226562137054444,12.697273L8.957125137054444,15.7439C8.363976937054442,16.085411,7.633026137054443,16.085411,7.039877437054443,15.7439L1.7734371470544432,12.703265C1.1802885570544435,12.361755,0.8148131370544434,11.726666,0.8148131370544434,11.043647L0.8148131370544434,4.9623771C0.8148131370544434,4.2763619,1.1802885570544435,3.6442692,1.7734371470544432,3.3027592L7.0398779370544435,0.2561323ZM13.264943137054443,11.040651L13.264943137054443,6.0647941L8.957125137054444,8.5512247L8.957125137054444,13.527082L13.264943137054443,11.040651Z" fill="#94A3B8" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

+ 1 - 0
src/assets/icons/tool_description_on.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="16" height="16.00006103515625" viewBox="0 0 16 16.00006103515625"><path d="M7.0398779370544435,0.2561323C7.633026637054443,-0.085377455,8.363976937054442,-0.085377455,8.957125137054444,0.2561323L14.226562137054444,3.2967677C14.819710137054443,3.6382773,15.185187137054443,4.273366,15.185187137054443,4.9563851L15.185187137054443,11.037656C15.185187137054443,11.723671,14.819710137054443,12.355763,14.226562137054444,12.697273L8.957125137054444,15.7439C8.363976937054442,16.085411,7.633026137054443,16.085411,7.039877437054443,15.7439L1.7734371470544432,12.703265C1.1802885570544435,12.361755,0.8148131370544434,11.726666,0.8148131370544434,11.043647L0.8148131370544434,4.9623771C0.8148131370544434,4.2763619,1.1802885570544435,3.6442692,1.7734371470544432,3.3027592L7.0398779370544435,0.2561323ZM13.264943137054443,11.040651L13.264943137054443,6.0647941L8.957125137054444,8.5512247L8.957125137054444,13.527082L13.264943137054443,11.040651Z" fill="#6366F1" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

+ 1 - 0
src/assets/icons/yellow_alert.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="14" height="14.0001220703125" viewBox="0 0 14 14.0001220703125"><path d="M7.0000639,14.000128C10.866093,14.000128,14.000128,10.866093,14.000128,7.0000639C14.000128,3.1340351,10.866093,0,7.0000639,0C3.1340351,0,0,3.1340351,0,7.0000639C0,10.866093,3.1340351,14.000128,7.0000639,14.000128ZM6.1250558,4.3750401C6.1250558,3.8917863,6.5168104,3.5000319,7.0000639,3.5000319C7.4833179,3.5000319,7.875072,3.8917863,7.875072,4.3750401C7.875072,4.8582935,7.4833179,5.2500477,7.0000639,5.2500477C6.5168104,5.2500477,6.1250558,4.8582935,6.1250558,4.3750401ZM5.9063039,6.1250558L7.2188158,6.1250558C7.5824909,6.1250558,7.875072,6.4176364,7.875072,6.781312L7.875072,9.1875839L8.0938234,9.1875839C8.4574986,9.1875839,8.7500801,9.4801645,8.7500801,9.8438396C8.7500801,10.207515,8.4574986,10.500095,8.0938234,10.500095L5.9063039,10.500095C5.5426288,10.500095,5.2500477,10.207515,5.2500477,9.8438396C5.2500477,9.4801645,5.5426288,9.1875839,5.9063039,9.1875839L6.5625601,9.1875839L6.5625601,7.4375677L5.9063039,7.4375677C5.5426288,7.4375677,5.2500477,7.1449866,5.2500477,6.781312C5.2500477,6.4176369,5.5426288,6.1250558,5.9063039,6.1250558Z" fill="#CA8A04" fill-opacity="1" style="mix-blend-mode:passthrough"/></svg>

BIN
src/assets/images/app_download.png


BIN
src/assets/images/service_WeChat.png


+ 15 - 8
src/components/pcHeader.vue

@@ -19,6 +19,14 @@
                         {{ navigationItem.label }}
                     </button>
 
+                    <button
+                        type="button"
+                        class="navigation-item active"
+                        @click="navigateToRoute('/mcp')"
+                    >
+                        MCP
+                    </button>
+
                     <div class="upload-metrial">
                         <div class="top-load">
                             <img src="https://cdntest.eiacloud.com/ai/approval/icons/upload_metrial.png" alt="上传">
@@ -290,11 +298,9 @@ const uploadMaterialItems = Object.freeze([
 
 /**
  * 判断当前导航项是否需要高亮。
- * 仅对站内路由做状态映射,避免站外链接误高亮。
+ * MCP 项目由独立 MCP 标签定位,云助手AI 不高亮。
  */
-const isNavigationActive = (navigationItem) => {
-    return navigationItem.key === "approveUpload";
-};
+const isNavigationActive = () => false;
 
 /**
  * 处理顶部导航点击。
@@ -427,11 +433,11 @@ const openExternalLink = (targetUrl) => {
         min-width: 0;
         display: flex;
         align-items: center;
-        gap: 30px;
+        gap: 20px;
     }
 
     .brand-wrapper {
-        width: 240px;
+        width: auto;
         flex-shrink: 0;
         display: flex;
         align-items: center;
@@ -462,8 +468,8 @@ const openExternalLink = (targetUrl) => {
         min-width: 0;
         display: flex;
         align-items: center;
-        flex-wrap: wrap;
-        gap: 4px;
+        flex-wrap: nowrap;
+        gap: 0;
     }
 
     .upload-metrial {
@@ -584,6 +590,7 @@ const openExternalLink = (targetUrl) => {
         color: #333333;
         white-space: nowrap;
         position: relative;
+        flex-shrink: 0;
 
         &:hover,
         &.active {

+ 27 - 8
src/pages/guide/index.vue

@@ -49,7 +49,7 @@
                     <icon-svg name="token_copy" size="14px" class="home-guide-token-icon" @click="copyText(guideToken)" />
                   </div>
                 </div>
-                <button type="button" class="home-guide-token-btn">
+                <button type="button" class="home-guide-token-btn" @click="createToken">
                   <icon-svg name="gain_token" size="14px" class="home-guide-token-btn-icon" />
                   <span>获取 Token</span>
                 </button>
@@ -372,7 +372,7 @@
 </template>
 <script setup>
 import { computed, onMounted, ref } from "vue";
-import { getMcpToken } from "@/api/mcp";
+import { getMcpToken, createMcpToken } from "@/api/mcp";
 import globalStore from "@/store/modules/global";
 
 const global = globalStore();
@@ -390,15 +390,34 @@ const guideTokenMasked = computed(() => {
   };
 });
 
+const applyToken = (data) => {
+  // 兼容 data 为字符串,或 { token, expireTime, status, ... } 对象
+  const token =
+    typeof data === "string"
+      ? data
+      : data && typeof data === "object"
+        ? data.token
+        : "";
+  if (token === null || token === undefined || token === "") {
+    global.setMcpToken("");
+    return;
+  }
+  global.setMcpToken(String(token));
+};
+
 const fetchMcpToken = async () => {
   try {
     const res = await getMcpToken();
-    const data = res?.data;
-    if (data === null || data === undefined || data === "") {
-      global.setMcpToken("");
-      return;
-    }
-    global.setMcpToken(typeof data === "string" ? data : String(data));
+    applyToken(res?.data);
+  } catch (error) {
+    console.error(error);
+  }
+};
+
+const createToken = async () => {
+  try {
+    const res = await createMcpToken();
+    applyToken(res?.data);
   } catch (error) {
     console.error(error);
   }

+ 148 - 18
src/pages/home/index.vue

@@ -15,7 +15,7 @@
           面向标准核查、报告编制、合规咨询、政策问答等场景,为 AI 工具提供专业、可信、可追溯的环境知识服务。
         </p>
         <div class="home-hero-actions">
-          <button type="button" class="home-hero-btn home-hero-btn-primary" @click="router.push({ name: 'mcp' })">
+          <button type="button" class="home-hero-btn home-hero-btn-primary" @click="router.push({ name: 'mcpServer' })">
             <icon-svg name="cut_in_Mcp" size="16px" />
             <span>立即接入 MCP</span>
           </button>
@@ -23,7 +23,7 @@
             <icon-svg name="connection_guide" size="16px" />
             <span>接入指南</span>
           </button>
-          <button type="button" class="home-hero-btn home-hero-btn-secondary">
+          <button type="button" class="home-hero-btn home-hero-btn-secondary" @click="router.push({ name: 'mcpOnlineTest' })">
             <icon-svg name="effect_experience" size="16px" />
             <span>效果体验</span>
           </button>
@@ -251,10 +251,10 @@
                   <div class="home-guide-token-icons">
                     <icon-svg :name="guideTokenVisible ? 'eye_not_displayed' : 'eye_show'" size="24px"
                       class="home-guide-token-icon" @click="guideTokenVisible = !guideTokenVisible" />
-                    <icon-svg name="token_copy" size="14px" class="home-guide-token-icon" @click="copyText(guideToken)" />
+                    <icon-svg name="copy_close_icon" size="14px" class="home-guide-token-icon" @click="copyText(guideToken)" />
                   </div>
                 </div>
-                <button type="button" class="home-guide-token-btn">
+                <button type="button" class="home-guide-token-btn" @click="createToken">
                   <icon-svg name="gain_token" size="14px" class="home-guide-token-btn-icon" />
                   <span>获取 Token</span>
                 </button>
@@ -368,7 +368,7 @@
                       </div>
                       <div class="home-guide-code-title">mcp.json</div>
                       <button type="button" class="home-guide-code-copy" @click="copyGuideMcpJson">
-                        <icon-svg name="" size="14px" class="home-guide-code-copy-icon" />
+                        <icon-svg name="copy_icon_white" size="14px" class="home-guide-code-copy-icon" />
                         <span>复制</span>
                       </button>
                     </div>
@@ -526,7 +526,7 @@
                           <code class="home-guide-cli-command-text">{{ command.text }}</code>
                         </div>
                         <button type="button" class="home-guide-cli-command-copy" @click="copyText(command.text)">
-                          <icon-svg name="" size="12px" class="home-guide-cli-command-copy-icon" />
+                          <icon-svg name="copy_icon_violet" size="12px" class="home-guide-cli-command-copy-icon" />
                           <span>复制</span>
                         </button>
                       </div>
@@ -534,7 +534,7 @@
                     <div class="home-guide-cli-footer">
                       <span class="home-guide-cli-footer-tip">初始化命令已自动填入真实 Token</span>
                       <button type="button" class="home-guide-cli-copy-all" @click="copyAllCliCommands">
-                        <icon-svg name="" size="12px" class="home-guide-cli-copy-all-icon" />
+                        <icon-svg name="copy_icon_white" size="12px" class="home-guide-cli-copy-all-icon" />
                         <span>全部复制</span>
                       </button>
                     </div>
@@ -565,7 +565,12 @@
                   <div v-for="(question, questionIndex) in card.questions" :key="`${card.title}-${questionIndex}`"
                     class="home-guide-verify-item">
                     <span>{{ question }}</span>
-                    <icon-svg name="Connection_Copy" size="13px" />
+                    <icon-svg
+                      name="Connection_Copy"
+                      size="13px"
+                      class="home-guide-verify-copy"
+                      @click="copyText(question)"
+                    />
                   </div>
                 </div>
               </div>
@@ -591,8 +596,21 @@
         </div>
         <div class="home-footer-right">
           <div class="home-footer-icons">
-            <icon-svg name="footer_WeChat" size="24px" />
-            <icon-svg name="footer_phone" size="24px" />
+            <div class="home-footer-icon-item">
+              <icon-svg name="footer_WeChat" size="24px" class="home-footer-icon" />
+              <div class="home-footer-popover">
+                <img src="@/assets/images/service_WeChat.png" alt="云助手客服微信" class="home-footer-popover-img" />
+                <div class="home-footer-popover-sub">云助手客服微信</div>
+              </div>
+            </div>
+            <div class="home-footer-icon-item">
+              <icon-svg name="footer_phone" size="24px" class="home-footer-icon" />
+              <div class="home-footer-popover">
+                <div class="home-footer-popover-title">官方应用下载</div>
+                <img src="@/assets/images/app_download.png" alt="官方应用下载" class="home-footer-popover-img" />
+                <div class="home-footer-popover-sub">环评云助手APP</div>
+              </div>
+            </div>
           </div>
           <div class="home-footer-links">
             <span>关于我们</span>
@@ -609,7 +627,8 @@
 <script setup>
 import { computed, onMounted, ref } from "vue";
 import { useRouter } from "vue-router";
-import { getMcpToken } from "@/api/mcp";
+import { ElMessage } from "element-plus";
+import { getMcpToken, createMcpToken } from "@/api/mcp";
 import globalStore from "@/store/modules/global";
 
 const router = useRouter();
@@ -634,15 +653,34 @@ const guideTokenMasked = computed(() => {
   };
 });
 
+const applyToken = (data) => {
+  // 兼容 data 为字符串,或 { token, expireTime, status, ... } 对象
+  const token =
+    typeof data === "string"
+      ? data
+      : data && typeof data === "object"
+        ? data.token
+        : "";
+  if (token === null || token === undefined || token === "") {
+    global.setMcpToken("");
+    return;
+  }
+  global.setMcpToken(String(token));
+};
+
 const fetchMcpToken = async () => {
   try {
     const res = await getMcpToken();
-    const data = res?.data;
-    if (data === null || data === undefined || data === "") {
-      global.setMcpToken("");
-      return;
-    }
-    global.setMcpToken(typeof data === "string" ? data : String(data));
+    applyToken(res?.data);
+  } catch (error) {
+    console.error(error);
+  }
+};
+
+const createToken = async () => {
+  try {
+    const res = await createMcpToken();
+    applyToken(res?.data);
   } catch (error) {
     console.error(error);
   }
@@ -735,10 +773,29 @@ const guideMcpJson = `{
 }`;
 const guideMcpLines = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11];
 const copyText = async (text) => {
+  if (!text) {
+    ElMessage.warning("暂无内容可复制");
+    return;
+  }
   try {
-    await navigator.clipboard.writeText(text);
+    if (navigator.clipboard?.writeText) {
+      await navigator.clipboard.writeText(text);
+    } else {
+      const textarea = document.createElement("textarea");
+      textarea.value = text;
+      textarea.setAttribute("readonly", "");
+      textarea.style.position = "fixed";
+      textarea.style.left = "-9999px";
+      document.body.appendChild(textarea);
+      textarea.select();
+      const ok = document.execCommand("copy");
+      document.body.removeChild(textarea);
+      if (!ok) throw new Error("execCommand copy failed");
+    }
+    ElMessage.success("复制成功");
   } catch (error) {
     console.error(error);
+    ElMessage.error("复制失败");
   }
 };
 const copyGuideMcpJson = () => copyText(guideMcpJson);
@@ -1788,6 +1845,7 @@ const guideVerifyCards = [
   width: 24px;
   height: 24px;
   color: #FFFFFF;
+  cursor: pointer;
 }
 
 .home-guide-block-token .home-guide-token-btn {
@@ -2860,6 +2918,12 @@ const guideVerifyCards = [
   text-overflow: ellipsis;
 }
 
+.home-guide-verify-copy {
+  flex-shrink: 0;
+  color: #94A3B8;
+  cursor: pointer;
+}
+
 .home-footer {
   background: #F5F7FA;
   border-radius: 0;
@@ -2926,6 +2990,72 @@ const guideVerifyCards = [
     gap: 16px;
   }
 
+  .home-footer-icon-item {
+    position: relative;
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+
+    &:hover {
+      .home-footer-popover {
+        opacity: 1;
+        visibility: visible;
+        transform: translateX(-50%) translateY(0);
+        pointer-events: auto;
+      }
+    }
+  }
+
+  .home-footer-icon {
+    cursor: pointer;
+  }
+
+  .home-footer-popover {
+    box-sizing: border-box;
+    position: absolute;
+    left: 50%;
+    bottom: calc(100% + 10px);
+    z-index: 20;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    gap: 4px;
+    width: 140px;
+    padding: 10px;
+    background: #FFFFFF;
+    box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
+    border-radius: 8px;
+    opacity: 0;
+    visibility: hidden;
+    transform: translateX(-50%) translateY(4px);
+    pointer-events: none;
+    transition: opacity 0.2s ease, transform 0.2s ease, visibility 0.2s ease;
+  }
+
+  .home-footer-popover-img {
+    display: block;
+    width: 100%;
+    height: auto;
+  }
+
+  .home-footer-popover-title {
+    font-family: PingFang SC, PingFang SC;
+    font-weight: 400;
+    font-size: 14px;
+    color: #333333;
+    line-height: 20px;
+    text-align: left;
+  }
+
+  .home-footer-popover-sub {
+    font-family: PingFang SC, PingFang SC;
+    font-weight: 400;
+    font-size: 12px;
+    color: #666666;
+    line-height: 16px;
+    text-align: left;
+  }
+
   .home-footer-links {
     display: flex;
     align-items: center;

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 673 - 228
src/pages/mcp/onlineTest.vue


+ 156 - 35
src/pages/mcp/serverDetail.vue

@@ -7,20 +7,38 @@
           <div class="mcp-server-header">
             <div class="mcp-server-header-left">
               <span class="mcp-server-header-icon">
-                <icon-svg name="" size="24px" />
+                <icon-svg name="connection_guide" size="24px" />
               </span>
               <div class="mcp-server-title">{{ pageData.title }}</div>
               <div class="mcp-server-desc">{{ pageData.description }}</div>
             </div>
             <div class="mcp-server-header-actions">
               <button type="button" class="mcp-server-btn mcp-server-btn-primary" @click="goOnlineTest">
-                <icon-svg name="" size="14px" />
+                <icon-svg name="effect_experience" size="14px" />
                 <span>在线测试</span>
               </button>
-              <button type="button" class="mcp-server-btn mcp-server-btn-outline">
-                <icon-svg name="" size="14px" />
-                <span>在线咨询</span>
-              </button>
+              <div class="mcp-server-consult">
+                <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'"
+                    size="14px"
+                  />
+                  <span>在线咨询</span>
+                </button>
+                <div v-show="consultOpen" class="mcp-server-consult-popover">
+                  <img
+                    src="@/assets/images/service_WeChat.png"
+                    alt="云助手客服微信"
+                    class="mcp-server-consult-popover-img"
+                  />
+                  <div class="mcp-server-consult-popover-sub">云助手客服微信</div>
+                </div>
+              </div>
             </div>
           </div>
 
@@ -59,7 +77,7 @@
         <section class="mcp-server-section mcp-server-section-tools">
           <div class="mcp-server-tools-head">
             <span class="mcp-server-tools-head-icon">
-              <icon-svg name="" size="16px" />
+              <icon-svg name="tool_description" size="16px" />
             </span>
             <div class="mcp-server-tools-head-title">工具说明</div>
           </div>
@@ -76,7 +94,10 @@
               @click="selectedToolIndex = index"
             >
               <span class="mcp-server-tool-icon">
-                <icon-svg :name="tool.icon || ''" size="16px" />
+                <icon-svg
+                  :name="selectedToolIndex === index ? 'tool_description_on' : 'tool_description_off'"
+                  size="16px"
+                />
               </span>
               <span class="mcp-server-tool-name">{{ index + 1 }}. {{ tool.name || tool }}</span>
               <span v-if="tool.badge" class="mcp-server-tool-badge">{{ tool.badge }}</span>
@@ -87,7 +108,7 @@
 
         <section class="mcp-server-section mcp-server-section-params">
           <div class="mcp-server-params-head">
-            <icon-svg name="" size="14px" class="mcp-server-params-head-icon" />
+            <icon-svg name="required_parameter" size="14px" class="mcp-server-params-head-icon" />
             <span class="mcp-server-params-head-title">请求参数</span>
             <span class="mcp-server-params-head-count">({{ pageData.params?.length || 0 }}个参数)</span>
           </div>
@@ -118,7 +139,7 @@
               <span class="mcp-server-params-desc">{{ param.desc }}</span>
             </div>
             <div v-if="pageData.paramNote" class="mcp-server-params-note">
-              <icon-svg name="" size="14px" class="mcp-server-params-note-icon" />
+              <icon-svg name="yellow_alert" size="14px" class="mcp-server-params-note-icon" />
               <span>{{ pageData.paramNote }}</span>
             </div>
           </div>
@@ -126,7 +147,7 @@
 
         <section class="mcp-server-section mcp-server-section-response">
           <div class="mcp-server-response-head">
-            <icon-svg name="" size="16px" class="mcp-server-response-head-icon" />
+            <icon-svg name="return_example" size="16px" class="mcp-server-response-head-icon" />
             <span class="mcp-server-response-head-title">返回示例</span>
             <span class="mcp-server-response-head-count">(最多10条,以下为示例)</span>
           </div>
@@ -138,7 +159,7 @@
                 <span></span>
               </div>
               <button type="button" class="mcp-server-response-code-copy" @click="copyResponseExample">
-                <icon-svg name="" size="14px" class="mcp-server-response-code-copy-icon" />
+                <icon-svg name="token_copy" size="12px" class="mcp-server-response-code-copy-icon" />
                 <span>复制</span>
               </button>
             </div>
@@ -146,7 +167,7 @@
               <div class="mcp-server-response-code-lines">
                 <span v-for="line in responseExampleLines" :key="line">{{ line }}</span>
               </div>
-              <pre class="mcp-server-response-code-pre">{{ pageData.responseExample }}</pre>
+              <pre class="mcp-server-response-code-pre" v-html="responseExampleHtml"></pre>
             </div>
           </div>
         </section>
@@ -157,7 +178,7 @@
       <div class="mcp-server-side-card mcp-server-side-card-token">
         <div class="mcp-server-token-head">
           <span class="mcp-server-token-head-icon">
-            <icon-svg name="" size="14px" />
+            <icon-svg name="key_token" size="14px" />
           </span>
           <div class="mcp-server-token-head-title">我的 Token</div>
         </div>
@@ -184,7 +205,7 @@
 
       <div class="mcp-server-side-card mcp-server-side-card-connect">
         <div class="mcp-server-connect-head">
-          <icon-svg name="" size="16px" class="mcp-server-connect-head-icon" />
+          <icon-svg name="connection_status" size="16px" class="mcp-server-connect-head-icon" />
           <span class="mcp-server-connect-head-title">连接状态</span>
         </div>
 
@@ -196,7 +217,7 @@
 
         <div class="mcp-server-connect-url-box">
           <div class="mcp-server-connect-url-left">
-            <icon-svg name="" size="16px" class="mcp-server-connect-url-icon" />
+            <icon-svg name="http_url" size="16px" class="mcp-server-connect-url-icon" />
             <span class="mcp-server-connect-url-text">{{ pageData.serverUrl }}</span>
           </div>
           <div class="mcp-server-connect-url-right">
@@ -207,7 +228,7 @@
               {{ connectionOk ? "正常" : "异常" }}
             </span>
             <icon-svg
-              name=""
+              name="http_copy"
               size="16px"
               class="mcp-server-connect-copy-icon"
               @click="copyText(pageData.serverUrl)"
@@ -225,15 +246,15 @@
 
         <div class="mcp-server-connect-code">
           <button type="button" class="mcp-server-connect-code-copy" @click="copyText(pageData.mcpConfig)">
-            <icon-svg name="" size="14px" />
+            <icon-svg name="token_copy" size="14px" />
           </button>
-          <pre class="mcp-server-connect-code-pre">{{ pageData.mcpConfig }}</pre>
+          <pre class="mcp-server-connect-code-pre" v-html="mcpConfigHtml"></pre>
         </div>
       </div>
 
       <div class="mcp-server-side-card mcp-server-side-card-related">
         <div class="mcp-server-related-head">
-          <icon-svg name="" size="16px" class="mcp-server-related-head-icon" />
+          <icon-svg name="related_service" size="16px" class="mcp-server-related-head-icon" />
           <span class="mcp-server-related-head-title">相关服务</span>
         </div>
         <div class="mcp-server-related-list">
@@ -250,7 +271,7 @@
                 :class="item.dot === 'yellow' ? 'mcp-server-related-dot--yellow' : 'mcp-server-related-dot--blue'"
               ></span>
               <span class="mcp-server-related-name">{{ item.label }}</span>
-              <icon-svg name="" size="12px" class="mcp-server-related-arrow" />
+              <icon-svg name="related_service_redirection" size="12px" class="mcp-server-related-arrow" />
             </div>
             <div class="mcp-server-related-desc">{{ item.desc }}</div>
           </button>
@@ -279,6 +300,7 @@ const serverDetail = ref(null);
 const toolDetail = ref(null);
 const selectedToolIndex = ref(0);
 const tokenVisible = ref(false);
+const consultOpen = ref(false);
 const serverRequestId = ref(0);
 const toolRequestId = ref(0);
 
@@ -412,19 +434,19 @@ const mapServerDetail = (data) => {
         label: "平均响应时间",
         value: formatMetricValue(data.avgResponseTime, "ms"),
         type: "blue",
-        icon: "",
+        icon: "response_time",
       },
       {
         label: "文件数",
         value: formatMetricValue(data.fileCount),
         type: "green",
-        icon: "",
+        icon: "number_of_files",
       },
       {
         label: "近30天请求",
         value: formatMetricValue(data.requestCount30d),
         type: "purple",
-        icon: "",
+        icon: "requests_in_the_past_days",
       },
     ],
     tools,
@@ -465,6 +487,45 @@ const responseExampleLines = computed(() => {
   return Array.from({ length: Math.max(count, 1) }, (_, index) => index + 1);
 });
 
+const escapeHtml = (str) =>
+  String(str)
+    .replace(/&/g, "&amp;")
+    .replace(/</g, "&lt;")
+    .replace(/>/g, "&gt;")
+    .replace(/"/g, "&quot;");
+
+/** 黑框 JSON:键 #7DD3FC,标点 #94A3B8,值 #86EFAC */
+const highlightJson = (jsonText) => {
+  const text = String(jsonText ?? "");
+  const re =
+    /("(?:\\.|[^"\\])*")\s*:|"((?:\\.|[^"\\])*)"|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)|\b(true|false|null)\b|([{}[\],:])/g;
+  let last = 0;
+  let html = "";
+  let match;
+  while ((match = re.exec(text)) !== null) {
+    if (match.index > last) {
+      html += `<span class="otp-json-punct">${escapeHtml(text.slice(last, match.index))}</span>`;
+    }
+    if (match[1] !== undefined) {
+      html += `<span class="otp-json-key">${escapeHtml(match[1])}</span><span class="otp-json-punct">:</span>`;
+    } else if (match[2] !== undefined) {
+      html += `<span class="otp-json-value">"${escapeHtml(match[2])}"</span>`;
+    } else if (match[3] !== undefined || match[4] !== undefined) {
+      html += `<span class="otp-json-value">${escapeHtml(match[0])}</span>`;
+    } else {
+      html += `<span class="otp-json-punct">${escapeHtml(match[0])}</span>`;
+    }
+    last = re.lastIndex;
+  }
+  if (last < text.length) {
+    html += `<span class="otp-json-punct">${escapeHtml(text.slice(last))}</span>`;
+  }
+  return html;
+};
+
+const responseExampleHtml = computed(() => highlightJson(pageData.value.responseExample || ""));
+const mcpConfigHtml = computed(() => highlightJson(pageData.value.mcpConfig || ""));
+
 const ensureServerList = async () => {
   if (global.mcpServerList?.length) return global.mcpServerList;
   const res = await getMcpServerList();
@@ -845,6 +906,50 @@ const goOnlineTest = () => {
   font-size: 14px;
   color: #475569;
   line-height: 20px;
+
+  &.is-active {
+    border: 1px solid #1678FF;
+    color: #1678FF;
+  }
+}
+
+.mcp-server-consult {
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  align-items: flex-end;
+}
+
+.mcp-server-consult-popover {
+  box-sizing: border-box;
+  position: absolute;
+  top: calc(100% + 8px);
+  right: 0;
+  z-index: 20;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 4px;
+  width: 140px;
+  padding: 10px;
+  background: #FFFFFF;
+  box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.1);
+  border-radius: 8px;
+}
+
+.mcp-server-consult-popover-img {
+  display: block;
+  width: 100%;
+  height: auto;
+}
+
+.mcp-server-consult-popover-sub {
+  font-family: PingFang SC, PingFang SC;
+  font-weight: 400;
+  font-size: 12px;
+  color: #666666;
+  line-height: 16px;
+  text-align: left;
 }
 
 .mcp-server-btn-primary {
@@ -1245,21 +1350,23 @@ const goOnlineTest = () => {
   align-items: center;
   gap: 4px;
   margin: 0;
-  padding: 6px 10px;
+  padding: 0;
   border: none;
-  border-radius: 6px;
-  background: #1678FF;
+  background: transparent;
   font-family: PingFang SC, PingFang SC;
-  font-weight: 500;
-  font-size: 12px;
-  color: #FFFFFF;
-  line-height: 16px;
+  font-weight: 400;
+  font-size: 11px;
+  color: #AFC2DC;
+  line-height: 14px;
+  text-align: center;
   cursor: pointer;
 }
 
 .mcp-server-response-code-copy-icon {
-  width: 14px;
-  height: 14px;
+  width: 12px;
+  height: 12px;
+  flex-shrink: 0;
+  color: #AFC2DC;
 }
 
 .mcp-server-response-code-body {
@@ -1291,7 +1398,7 @@ const goOnlineTest = () => {
   line-height: 22px;
   white-space: pre-wrap;
   word-break: break-word;
-  color: #E2E8F0;
+  color: #94A3B8;
 }
 
 .mcp-server-side {
@@ -1465,7 +1572,7 @@ const goOnlineTest = () => {
   font-family: Consolas, Monaco, monospace;
   font-size: 12px;
   line-height: 20px;
-  color: #E2E8F0;
+  color: #94A3B8;
   white-space: pre-wrap;
   word-break: break-all;
 }
@@ -1659,3 +1766,17 @@ const goOnlineTest = () => {
   text-align: left;
 }
 </style>
+
+<style lang="scss">
+.otp-json-key {
+  color: #7DD3FC;
+}
+
+.otp-json-punct {
+  color: #94A3B8;
+}
+
+.otp-json-value {
+  color: #86EFAC;
+}
+</style>

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov