|
|
@@ -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, "&")
|
|
|
+ .replace(/</g, "<")
|
|
|
+ .replace(/>/g, ">")
|
|
|
+ .replace(/"/g, """);
|
|
|
+
|
|
|
+/** 黑框 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>
|