<!DOCTYPE html>
|
<html lang="zh-CN">
|
<head>
|
<meta charset="UTF-8" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<title>MCP挂载</title>
|
<link rel="stylesheet" href="../../static/vue/element/element.css" />
|
<style>
|
body {
|
margin: 0;
|
font-family: "Avenir Next", "PingFang SC", "Microsoft YaHei", sans-serif;
|
background:
|
radial-gradient(1200px 500px at 10% -10%, rgba(17, 94, 89, 0.12), transparent 48%),
|
radial-gradient(980px 460px at 100% 0%, rgba(12, 120, 168, 0.12), transparent 55%),
|
#f3f7fb;
|
}
|
.container {
|
max-width: 1640px;
|
margin: 16px auto;
|
padding: 0 14px 20px;
|
}
|
.hero {
|
background: linear-gradient(135deg, #0f5964 0%, #176a8b 48%, #289c88 100%);
|
color: #fff;
|
border-radius: 16px;
|
padding: 14px 16px;
|
margin-bottom: 12px;
|
box-shadow: 0 12px 28px rgba(19, 69, 97, 0.2);
|
}
|
.hero-top {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
gap: 10px;
|
flex-wrap: wrap;
|
}
|
.hero-title {
|
display: flex;
|
align-items: center;
|
gap: 10px;
|
}
|
.hero-title .main {
|
font-size: 16px;
|
font-weight: 700;
|
}
|
.hero-title .sub {
|
font-size: 12px;
|
opacity: 0.92;
|
margin-top: 2px;
|
}
|
.hero-actions {
|
display: flex;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
.summary-grid {
|
margin-top: 10px;
|
display: grid;
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
gap: 8px;
|
}
|
.summary-card {
|
border-radius: 10px;
|
background: rgba(255, 255, 255, 0.16);
|
border: 1px solid rgba(255, 255, 255, 0.24);
|
padding: 8px 10px;
|
min-height: 56px;
|
backdrop-filter: blur(3px);
|
}
|
.summary-card .k {
|
font-size: 11px;
|
opacity: 0.88;
|
}
|
.summary-card .v {
|
margin-top: 4px;
|
font-size: 22px;
|
font-weight: 700;
|
line-height: 1.1;
|
}
|
.mount-board {
|
border-radius: 14px;
|
border: 1px solid #dbe5f2;
|
background:
|
radial-gradient(760px 220px at -8% 0, rgba(17, 100, 116, 0.06), transparent 52%),
|
radial-gradient(720px 240px at 108% 16%, rgba(29, 143, 124, 0.07), transparent 58%),
|
#f9fbff;
|
box-shadow: 0 8px 30px rgba(26, 53, 84, 0.1);
|
padding: 12px;
|
min-height: 64vh;
|
}
|
.mount-grid {
|
display: grid;
|
grid-template-columns: repeat(auto-fill, minmax(410px, 1fr));
|
gap: 12px;
|
}
|
.mount-card {
|
border-radius: 14px;
|
border: 1px solid #e4ebf5;
|
background: linear-gradient(180deg, #ffffff 0%, #fbfdff 100%);
|
box-shadow: 0 10px 24px rgba(14, 38, 68, 0.08);
|
padding: 12px;
|
transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease;
|
animation: card-in 0.24s ease both;
|
}
|
.mount-card:hover {
|
transform: translateY(-2px);
|
box-shadow: 0 14px 26px rgba(14, 38, 68, 0.12);
|
border-color: #d4e2f2;
|
}
|
.mount-card.disabled {
|
opacity: 0.82;
|
}
|
.mount-head {
|
display: flex;
|
align-items: flex-start;
|
justify-content: space-between;
|
gap: 8px;
|
margin-bottom: 10px;
|
}
|
.mount-title {
|
flex: 1;
|
min-width: 0;
|
display: flex;
|
flex-direction: column;
|
gap: 5px;
|
}
|
.mount-id-line {
|
color: #8294aa;
|
font-size: 11px;
|
white-space: nowrap;
|
overflow: hidden;
|
text-overflow: ellipsis;
|
}
|
.mount-state {
|
display: flex;
|
gap: 6px;
|
flex-wrap: wrap;
|
justify-content: flex-end;
|
max-width: 48%;
|
}
|
.mount-fields {
|
display: grid;
|
grid-template-columns: 1fr 1fr;
|
gap: 8px;
|
}
|
.field-full {
|
grid-column: 1 / -1;
|
}
|
.field-label {
|
font-size: 11px;
|
color: #6f8094;
|
margin-bottom: 4px;
|
}
|
.switch-line {
|
margin-top: 10px;
|
display: grid;
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
gap: 8px;
|
}
|
.switch-item {
|
border: 1px solid #e7edf7;
|
border-radius: 10px;
|
padding: 6px 8px;
|
background: #fff;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
font-size: 12px;
|
color: #2f3f53;
|
}
|
.stats-box {
|
margin-top: 10px;
|
border: 1px solid #e8edf6;
|
border-radius: 10px;
|
background: linear-gradient(180deg, #fcfdff 0%, #f7faff 100%);
|
padding: 8px 10px;
|
font-size: 12px;
|
color: #4c5f76;
|
line-height: 1.6;
|
}
|
.stats-box .light {
|
color: #7f91a8;
|
}
|
.mount-actions {
|
margin-top: 10px;
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
.action-left, .action-right {
|
display: flex;
|
align-items: center;
|
gap: 8px;
|
flex-wrap: wrap;
|
}
|
.empty-shell {
|
min-height: 48vh;
|
border-radius: 12px;
|
border: 1px dashed #cfd8e5;
|
display: flex;
|
flex-direction: column;
|
align-items: center;
|
justify-content: center;
|
color: #7d8ea4;
|
gap: 8px;
|
background: rgba(255, 255, 255, 0.55);
|
}
|
.tool-table-hint {
|
color: #74879d;
|
font-size: 12px;
|
margin-bottom: 8px;
|
}
|
.mono {
|
font-family: Menlo, Monaco, Consolas, "Liberation Mono", monospace;
|
font-size: 12px;
|
}
|
@keyframes card-in {
|
from { opacity: 0; transform: translateY(8px); }
|
to { opacity: 1; transform: translateY(0); }
|
}
|
@media (max-width: 1280px) {
|
.summary-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
.mount-grid { grid-template-columns: repeat(auto-fill, minmax(340px, 1fr)); }
|
.mount-fields { grid-template-columns: 1fr; }
|
.switch-line { grid-template-columns: 1fr; }
|
}
|
</style>
|
</head>
|
<body>
|
<div id="app" class="container">
|
<div class="hero">
|
<div class="hero-top">
|
<div class="hero-title">
|
<div v-html="headerIcon" style="display:flex;"></div>
|
<div>
|
<div class="main">MCP挂载中心</div>
|
<div class="sub">统一管理内置 WCS MCP 与外部 MCP 服务,AI 助手通过这里聚合工具</div>
|
</div>
|
</div>
|
<div class="hero-actions">
|
<el-button type="primary" size="mini" @click="addMount">新增挂载</el-button>
|
<el-button size="mini" @click="loadMounts">刷新挂载</el-button>
|
<el-button size="mini" @click="refreshTools">刷新工具缓存</el-button>
|
<el-button size="mini" @click="openToolDialog">已加载工具</el-button>
|
</div>
|
</div>
|
<div class="summary-grid">
|
<div class="summary-card">
|
<div class="k">总挂载</div>
|
<div class="v">{{ summary.total }}</div>
|
</div>
|
<div class="summary-card">
|
<div class="k">启用</div>
|
<div class="v">{{ summary.enabled }}</div>
|
</div>
|
<div class="summary-card">
|
<div class="k">测试失败</div>
|
<div class="v">{{ summary.failed }}</div>
|
</div>
|
<div class="summary-card">
|
<div class="k">SSE / HTTP</div>
|
<div class="v">{{ summary.sse }} / {{ summary.http }}</div>
|
</div>
|
<div class="summary-card">
|
<div class="k">已加载工具</div>
|
<div class="v">{{ loadedTools.length }}</div>
|
</div>
|
</div>
|
</div>
|
|
<div class="mount-board" v-loading="loading">
|
<div v-if="!mounts || mounts.length === 0" class="empty-shell">
|
<div style="font-size:14px;font-weight:600;">暂无MCP挂载</div>
|
<div style="font-size:12px;">初始化后会自动生成一条内置 WCS MCP 挂载,你也可以手动新增外部服务</div>
|
<el-button type="primary" size="mini" @click="initDefaults">初始化默认挂载</el-button>
|
</div>
|
<div v-else class="mount-grid">
|
<div class="mount-card" :class="{disabled: mount.status !== 1}" v-for="(mount, idx) in mounts" :key="mount.id ? ('mount_' + mount.id) : ('new_' + idx)">
|
<div class="mount-head">
|
<div class="mount-title">
|
<el-input v-model="mount.name" size="mini" placeholder="挂载名称"></el-input>
|
<div class="mount-id-line">#{{ mount.id || 'new' }} · {{ mount.mountCode || '未命名编码' }} · {{ transportLabel(mount.transportType) }}</div>
|
</div>
|
<div class="mount-state">
|
<el-tag size="mini" :type="mount.status === 1 ? 'success' : 'info'">{{ mount.status === 1 ? '启用' : '禁用' }}</el-tag>
|
<el-tag size="mini" type="danger" v-if="mount.lastTestOk === 0">最近测试失败</el-tag>
|
</div>
|
</div>
|
|
<div class="mount-fields">
|
<div>
|
<div class="field-label">挂载编码</div>
|
<el-input v-model="mount.mountCode" class="mono" size="mini" placeholder="必填,例如 docs_center"></el-input>
|
</div>
|
<div>
|
<div class="field-label">传输类型</div>
|
<el-select v-model="mount.transportType" size="mini" style="width:100%;" @change="handleTransportChange(mount)">
|
<el-option v-for="item in transportTypes" :key="item.code" :label="item.label" :value="item.code"></el-option>
|
</el-select>
|
</div>
|
<div class="field-full">
|
<div class="field-label">固定URL</div>
|
<el-input v-model="mount.url" class="mono" size="mini" placeholder="必填,例如 http://127.0.0.1:9090/wcs/ai/mcp/sse"></el-input>
|
</div>
|
<div>
|
<div class="field-label">优先级</div>
|
<el-input-number v-model="mount.priority" size="mini" :min="0" :max="99999" :controls="false" style="width:100%;"></el-input-number>
|
</div>
|
<div>
|
<div class="field-label">超时毫秒</div>
|
<el-input-number v-model="mount.requestTimeoutMs" size="mini" :min="1000" :max="300000" :controls="false" style="width:100%;"></el-input-number>
|
</div>
|
<div class="field-full">
|
<div class="field-label">备注</div>
|
<el-input v-model="mount.memo" type="textarea" :rows="2" placeholder="可填写该挂载的用途、来源或接入说明"></el-input>
|
</div>
|
</div>
|
|
<div class="switch-line">
|
<div class="switch-item">
|
<span>启用</span>
|
<el-switch v-model="mount.status" :active-value="1" :inactive-value="0"></el-switch>
|
</div>
|
</div>
|
|
<div class="stats-box">
|
<div>实际连接地址: <span class="mono">{{ displayUrl(mount) }}</span></div>
|
<div class="light">工具名前缀: {{ (mount.mountCode || '-') + '_' }}</div>
|
<div class="light">最近测试: {{ formatDateTime(mount.lastTestTime) }} / {{ testStatusLabel(mount.lastTestOk) }}</div>
|
<div class="light">测试摘要: {{ mount.lastTestSummary || '-' }}</div>
|
</div>
|
|
<div class="mount-actions">
|
<div class="action-left">
|
<el-button type="primary" size="mini" @click="saveMount(mount)">保存</el-button>
|
<el-button size="mini" :loading="mount.__testing === true" @click="testMount(mount)">
|
{{ mount.__testing === true ? '测试中...' : '测试' }}
|
</el-button>
|
</div>
|
<div class="action-right">
|
<el-button size="mini" plain @click="previewMountTools(mount)">预览工具名</el-button>
|
<el-button size="mini" type="danger" plain @click="deleteMount(mount, idx)">删除</el-button>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<el-dialog title="已加载工具" :visible.sync="toolDialogVisible" width="88%" :close-on-click-modal="false">
|
<div class="tool-table-hint">这里展示当前 AI 助手实际可用的聚合工具名。所有 MCP 工具统一按挂载编码命名,例如 `wcs_local_task_query`。</div>
|
<el-table :data="loadedTools" border stripe height="60vh" v-loading="toolLoading" :header-cell-style="{background:'#f7f9fc', color:'#2e3a4d', fontWeight:600}">
|
<el-table-column prop="name" label="工具名" min-width="200"></el-table-column>
|
<el-table-column prop="originalName" label="原始工具名" min-width="180"></el-table-column>
|
<el-table-column prop="mountName" label="挂载" min-width="140"></el-table-column>
|
<el-table-column prop="mountCode" label="挂载编码" min-width="140"></el-table-column>
|
<el-table-column prop="transportType" label="传输" width="150"></el-table-column>
|
<el-table-column label="描述" min-width="260">
|
<template slot-scope="scope">
|
<div>{{ scope.row.description || '-' }}</div>
|
</template>
|
</el-table-column>
|
</el-table>
|
</el-dialog>
|
</div>
|
|
<script type="text/javascript" src="../../static/vue/js/vue.min.js"></script>
|
<script type="text/javascript" src="../../static/vue/element/element.js"></script>
|
<script type="text/javascript" src="../../static/js/common.js" charset="utf-8"></script>
|
<script>
|
new Vue({
|
el: '#app',
|
data: function() {
|
return {
|
headerIcon: getAiIconHtml(34, 34),
|
loading: false,
|
toolLoading: false,
|
mounts: [],
|
transportTypes: [],
|
loadedTools: [],
|
toolDialogVisible: false
|
};
|
},
|
computed: {
|
summary: function() {
|
var total = this.mounts.length;
|
var enabled = 0;
|
var failed = 0;
|
var sse = 0;
|
var http = 0;
|
for (var i = 0; i < this.mounts.length; i++) {
|
var x = this.mounts[i] || {};
|
if (x.status === 1) enabled++;
|
if (x.lastTestOk === 0) failed++;
|
if (x.transportType === 'STREAMABLE_HTTP') http++;
|
else sse++;
|
}
|
return {
|
total: total,
|
enabled: enabled,
|
failed: failed,
|
sse: sse,
|
http: http
|
};
|
}
|
},
|
methods: {
|
authHeaders: function() {
|
return { 'token': localStorage.getItem('token') };
|
},
|
transportLabel: function(code) {
|
for (var i = 0; i < this.transportTypes.length; i++) {
|
if (this.transportTypes[i].code === code) {
|
return this.transportTypes[i].label;
|
}
|
}
|
return code || '-';
|
},
|
transportDefaultUrl: function(code) {
|
for (var i = 0; i < this.transportTypes.length; i++) {
|
if (this.transportTypes[i].code === code) {
|
return this.transportTypes[i].defaultUrl;
|
}
|
}
|
return code === 'STREAMABLE_HTTP'
|
? 'http://127.0.0.1:9090/wcs/ai/mcp'
|
: 'http://127.0.0.1:9090/wcs/ai/mcp/sse';
|
},
|
handleTransportChange: function(mount) {
|
if (!mount) {
|
return;
|
}
|
if (!mount.url) {
|
mount.url = this.transportDefaultUrl(mount.transportType);
|
}
|
},
|
formatDateTime: function(input) {
|
if (!input) return '-';
|
var d = input instanceof Date ? input : new Date(input);
|
if (isNaN(d.getTime())) return String(input);
|
var pad = function(n) { return n < 10 ? ('0' + n) : String(n); };
|
return d.getFullYear() + '-'
|
+ pad(d.getMonth() + 1) + '-'
|
+ pad(d.getDate()) + ' '
|
+ pad(d.getHours()) + ':'
|
+ pad(d.getMinutes()) + ':'
|
+ pad(d.getSeconds());
|
},
|
testStatusLabel: function(value) {
|
if (value === 1) return '成功';
|
if (value === 0) return '失败';
|
return '未测试';
|
},
|
displayUrl: function(mount) {
|
if (!mount) return '-';
|
return mount.url || '-';
|
},
|
newMountTemplate: function() {
|
return {
|
id: null,
|
name: '',
|
mountCode: '',
|
transportType: this.transportTypes.length > 0 ? this.transportTypes[0].code : 'SSE',
|
url: this.transportTypes.length > 0 ? this.transportTypes[0].defaultUrl : 'http://127.0.0.1:9090/wcs/ai/mcp/sse',
|
requestTimeoutMs: 20000,
|
priority: 100,
|
status: 1,
|
memo: '',
|
lastTestOk: null,
|
lastTestTime: null,
|
lastTestSummary: ''
|
};
|
},
|
addMount: function() {
|
this.mounts.unshift(this.newMountTemplate());
|
},
|
buildPayload: function(mount) {
|
return {
|
id: mount.id,
|
name: mount.name,
|
mountCode: mount.mountCode,
|
transportType: mount.transportType,
|
url: mount.url,
|
requestTimeoutMs: mount.requestTimeoutMs,
|
priority: mount.priority,
|
status: mount.status,
|
memo: mount.memo
|
};
|
},
|
loadTransportTypes: function() {
|
var self = this;
|
return fetch(baseUrl + '/ai/mcp/mount/types/auth', { headers: self.authHeaders() })
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (res && res.code === 200 && Array.isArray(res.data)) {
|
self.transportTypes = res.data;
|
}
|
});
|
},
|
loadMounts: function() {
|
var self = this;
|
self.loading = true;
|
fetch(baseUrl + '/ai/mcp/mount/list/auth', { headers: self.authHeaders() })
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
self.loading = false;
|
if (res && res.code === 200) {
|
self.mounts = Array.isArray(res.data) ? res.data : [];
|
} else {
|
self.$message.error((res && res.msg) ? res.msg : '加载挂载失败');
|
}
|
})
|
.catch(function(){
|
self.loading = false;
|
self.$message.error('加载挂载失败');
|
});
|
},
|
loadToolList: function(showMessage) {
|
var self = this;
|
self.toolLoading = true;
|
fetch(baseUrl + '/ai/mcp/mount/toolList/auth', { headers: self.authHeaders() })
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
self.toolLoading = false;
|
if (res && res.code === 200) {
|
self.loadedTools = Array.isArray(res.data) ? res.data : [];
|
if (showMessage === true) {
|
self.$message.success('已刷新工具列表');
|
}
|
} else {
|
self.$message.error((res && res.msg) ? res.msg : '加载工具失败');
|
}
|
})
|
.catch(function(){
|
self.toolLoading = false;
|
self.$message.error('加载工具失败');
|
});
|
},
|
openToolDialog: function() {
|
this.toolDialogVisible = true;
|
this.loadToolList(false);
|
},
|
initDefaults: function() {
|
var self = this;
|
fetch(baseUrl + '/ai/mcp/mount/initDefaults/auth', {
|
method: 'POST',
|
headers: self.authHeaders()
|
})
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (res && res.code === 200) {
|
self.$message.success('默认挂载已初始化');
|
self.loadMounts();
|
self.loadToolList(false);
|
} else {
|
self.$message.error((res && res.msg) ? res.msg : '初始化失败');
|
}
|
})
|
.catch(function(){
|
self.$message.error('初始化失败');
|
});
|
},
|
saveMount: function(mount) {
|
var self = this;
|
fetch(baseUrl + '/ai/mcp/mount/save/auth', {
|
method: 'POST',
|
headers: Object.assign({ 'Content-Type': 'application/json' }, self.authHeaders()),
|
body: JSON.stringify(self.buildPayload(mount))
|
})
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (res && res.code === 200) {
|
self.$message.success('保存成功');
|
self.loadMounts();
|
self.loadToolList(false);
|
} else {
|
self.$message.error((res && res.msg) ? res.msg : '保存失败');
|
}
|
})
|
.catch(function(){
|
self.$message.error('保存失败');
|
});
|
},
|
deleteMount: function(mount, idx) {
|
var self = this;
|
if (!mount.id) {
|
self.mounts.splice(idx, 1);
|
return;
|
}
|
self.$confirm('确定删除该挂载吗?', '提示', { type: 'warning' }).then(function() {
|
fetch(baseUrl + '/ai/mcp/mount/delete/auth?id=' + encodeURIComponent(mount.id), {
|
method: 'POST',
|
headers: self.authHeaders()
|
})
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (res && res.code === 200) {
|
self.$message.success('删除成功');
|
self.loadMounts();
|
self.loadToolList(false);
|
} else {
|
self.$message.error((res && res.msg) ? res.msg : '删除失败');
|
}
|
})
|
.catch(function(){
|
self.$message.error('删除失败');
|
});
|
}).catch(function(){});
|
},
|
testMount: function(mount) {
|
var self = this;
|
if (mount.__testing === true) return;
|
self.$set(mount, '__testing', true);
|
fetch(baseUrl + '/ai/mcp/mount/test/auth', {
|
method: 'POST',
|
headers: Object.assign({ 'Content-Type': 'application/json' }, self.authHeaders()),
|
body: JSON.stringify(self.buildPayload(mount))
|
})
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (!res || res.code !== 200) {
|
self.$message.error((res && res.msg) ? res.msg : '测试失败');
|
return;
|
}
|
var data = res.data || {};
|
var ok = data.ok === true;
|
var msg = ''
|
+ '挂载: ' + (mount.name || '-') + '\n'
|
+ 'URL: ' + (data.url || '-') + '\n'
|
+ '传输: ' + (data.transportType || '-') + '\n'
|
+ '耗时: ' + (data.latencyMs != null ? data.latencyMs : '-') + ' ms\n'
|
+ '工具数: ' + (data.toolCount != null ? data.toolCount : 0) + '\n'
|
+ '结果: ' + (data.message || '-') + '\n'
|
+ '工具名: ' + ((data.toolNames || []).join(', ') || '-');
|
self.$alert(msg, ok ? '连接成功' : '连接失败', {
|
confirmButtonText: '确定',
|
type: ok ? 'success' : 'error'
|
});
|
mount.lastTestOk = ok ? 1 : 0;
|
mount.lastTestTime = new Date().getTime();
|
mount.lastTestSummary = data.message || '';
|
if (mount.id) {
|
self.loadMounts();
|
}
|
})
|
.catch(function(){
|
self.$message.error('测试失败');
|
})
|
.finally(function(){
|
self.$set(mount, '__testing', false);
|
});
|
},
|
previewMountTools: function(mount) {
|
var self = this;
|
fetch(baseUrl + '/ai/mcp/mount/test/auth', {
|
method: 'POST',
|
headers: Object.assign({ 'Content-Type': 'application/json' }, self.authHeaders()),
|
body: JSON.stringify(self.buildPayload(mount))
|
})
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (!res || res.code !== 200) {
|
self.$message.error((res && res.msg) ? res.msg : '预览失败');
|
return;
|
}
|
var data = res.data || {};
|
var names = Array.isArray(data.toolNames) ? data.toolNames : [];
|
self.$alert(names.length > 0 ? names.join('\n') : '未发现工具', '工具名预览', {
|
confirmButtonText: '确定'
|
});
|
})
|
.catch(function(){
|
self.$message.error('预览失败');
|
});
|
},
|
refreshTools: function() {
|
var self = this;
|
fetch(baseUrl + '/ai/mcp/mount/refresh/auth', {
|
method: 'POST',
|
headers: self.authHeaders()
|
})
|
.then(function(r){ return r.json(); })
|
.then(function(res){
|
if (res && res.code === 200) {
|
self.loadedTools = Array.isArray(res.data) ? res.data : [];
|
self.$message.success('工具缓存已刷新');
|
} else {
|
self.$message.error((res && res.msg) ? res.msg : '刷新失败');
|
}
|
})
|
.catch(function(){
|
self.$message.error('刷新失败');
|
});
|
}
|
},
|
mounted: function() {
|
var self = this;
|
Promise.all([
|
self.loadTransportTypes(),
|
self.loadMounts(),
|
self.loadToolList(false)
|
]).then(function(){
|
if (!self.mounts || self.mounts.length === 0) {
|
return;
|
}
|
for (var i = 0; i < self.mounts.length; i++) {
|
if (!self.mounts[i].url) {
|
self.mounts[i].url = self.transportDefaultUrl(self.mounts[i].transportType);
|
}
|
}
|
});
|
}
|
});
|
</script>
|
</body>
|
</html>
|