fix: 测试页面MCP工具参数编辑样式调整

This commit is contained in:
rainv123
2026-02-05 14:44:37 +08:00
parent 8dbc0b9689
commit 378d68aba9
3 changed files with 380 additions and 90 deletions
+154 -15
View File
@@ -1028,35 +1028,174 @@ body {
margin-bottom: 12px;
}
.property-item {
.properties-container #addMcpPropertyBtn {
display: block;
margin: 0 auto;
}
.properties-container #addMcpPropertyBtn:hover {
background: #4752c4;
}
.mcp-checkbox-label {
display: flex;
gap: 8px;
margin-bottom: 8px;
align-items: center;
gap: 8px;
margin-top: 8px;
color: #b9bbbe;
font-size: 13px;
cursor: pointer;
justify-content: flex-start;
}
.property-item input {
flex: 1;
padding: 6px 8px;
border: 1px solid #40444b;
border-radius: 4px;
background: #40444b;
.mcp-checkbox-label input[type="checkbox"] {
width: 16px;
height: 16px;
cursor: pointer;
}
.mcp-property-name {
color: white;
font-size: 12px;
font-size: 14px;
font-weight: 500;
}
.remove-property {
background: #ed4245;
.mcp-property-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.mcp-properties-list {
display: flex;
flex-wrap: wrap;
gap: 12px;
min-height: 60px;
padding: 8px;
}
.input-group-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.input-group-header label {
margin-bottom: 0;
}
.input-group-header .properties-btn-primary {
margin: 0;
}
.mcp-empty-state {
text-align: center;
padding: 20px;
color: #999;
font-size: 14px;
width: 100%;
display: none;
}
.properties-btn-primary {
background: #2196f3;
color: white;
border: none;
padding: 4px 8px;
padding: 6px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-weight: 500;
transition: background-color 0.2s;
display: block;
margin: 0 auto;
}
.remove-property:hover {
background: #c03537;
.properties-btn-primary:hover {
background: #2196f3;
}
.mcp-property-card {
background: #36393f;
border: 1px solid #40444b;
border-radius: 8px;
padding: 12px;
width: 100%;
cursor: pointer;
transition: all 0.2s ease;
}
.mcp-property-card:hover {
background: #40444b;
border-color: #5865f2;
}
.mcp-property-card:active {
transform: none;
}
.mcp-property-row-label {
display: flex;
align-items: flex-start;
margin-bottom: 6px;
gap: 8px;
}
.mcp-property-label {
color: #b9bbbe;
font-size: 13px;
width: 60px;
flex-shrink: 0;
text-align: left;
}
.mcp-property-value {
color: white;
font-size: 13px;
flex: 1;
word-break: break-all;
}
.mcp-property-required-badge {
color: #f44336;
font-size: 12px;
}
.mcp-property-row-action {
display: flex;
justify-content: flex-end;
margin-top: 6px;
padding-top: 6px;
border-top: 1px solid #40444b;
}
.mcp-property-delete-btn {
background: #f44336;
color: white;
border: none;
padding: 4px 12px;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
transition: background-color 0.2s;
}
.mcp-property-delete-btn:hover {
background: #d32f2f;
}
.mcp-property-card-optional {
background: #4f545c;
color: #b9bbbe;
font-size: 11px;
padding: 2px 8px;
border-radius: 4px;
}
.property-modal {
max-width: 450px;
}
/* ==================== 音频可视化器样式 ==================== */
+158 -66
View File
@@ -105,94 +105,167 @@ function renderMcpTools() {
*/
function renderMcpProperties() {
const container = document.getElementById('mcpPropertiesContainer');
const emptyState = document.getElementById('mcpEmptyState');
if (!container) {
return; // Container not found, skip rendering
}
if (mcpProperties.length === 0) {
container.innerHTML = '<div style="text-align: center; padding: 20px; color: #999; font-size: 14px;">暂无参数,点击下方按钮添加参数</div>';
if (emptyState) {
emptyState.style.display = 'block';
}
container.innerHTML = '';
return;
}
if (emptyState) {
emptyState.style.display = 'none';
}
container.innerHTML = mcpProperties.map((prop, index) => `
<div class="mcp-property-item">
<div class="mcp-property-header">
<span class="mcp-property-name">${prop.name}</span>
<button type="button" onclick="window.mcpModule.deleteMcpProperty(${index})"
style="padding: 3px 8px; border: none; border-radius: 3px; background-color: #f44336; color: white; cursor: pointer; font-size: 11px;">
删除
</button>
<div class="mcp-property-card" onclick="window.mcpModule.editMcpProperty(${index})">
<div class="mcp-property-row-label">
<span class="mcp-property-label">参数名称</span>
<span class="mcp-property-value">${prop.name}${prop.required ? ' <span class="mcp-property-required-badge">[必填]</span>' : ''}</span>
</div>
<div class="mcp-property-row">
<div>
<label class="mcp-small-label">参数名称 *</label>
<input type="text" class="mcp-small-input" value="${prop.name}"
onchange="window.mcpModule.updateMcpProperty(${index}, 'name', this.value)" required>
</div>
<div>
<label class="mcp-small-label">数据类型 *</label>
<select class="mcp-small-input" onchange="window.mcpModule.updateMcpProperty(${index}, 'type', this.value)">
<option value="string" ${prop.type === 'string' ? 'selected' : ''}>字符串</option>
<option value="integer" ${prop.type === 'integer' ? 'selected' : ''}>整数</option>
<option value="number" ${prop.type === 'number' ? 'selected' : ''}>数字</option>
<option value="boolean" ${prop.type === 'boolean' ? 'selected' : ''}>布尔值</option>
<option value="array" ${prop.type === 'array' ? 'selected' : ''}>数组</option>
<option value="object" ${prop.type === 'object' ? 'selected' : ''}>对象</option>
</select>
</div>
<div class="mcp-property-row-label">
<span class="mcp-property-label">数据类型</span>
<span class="mcp-property-value">${getTypeLabel(prop.type)}</span>
</div>
${(prop.type === 'integer' || prop.type === 'number') ? `
<div class="mcp-property-row">
<div>
<label class="mcp-small-label">最小值</label>
<input type="number" class="mcp-small-input" value="${prop.minimum !== undefined ? prop.minimum : ''}"
placeholder="可选" onchange="window.mcpModule.updateMcpProperty(${index}, 'minimum', this.value ? parseFloat(this.value) : undefined)">
</div>
<div>
<label class="mcp-small-label">最大值</label>
<input type="number" class="mcp-small-input" value="${prop.maximum !== undefined ? prop.maximum : ''}"
placeholder="可选" onchange="window.mcpModule.updateMcpProperty(${index}, 'maximum', this.value ? parseFloat(this.value) : undefined)">
</div>
<div class="mcp-property-row-label">
<span class="mcp-property-label">描述</span>
<span class="mcp-property-value">${prop.description || '-'}</span>
</div>
` : ''}
<div class="mcp-property-row-full">
<label class="mcp-small-label">参数描述</label>
<input type="text" class="mcp-small-input" value="${prop.description || ''}"
placeholder="可选" onchange="window.mcpModule.updateMcpProperty(${index}, 'description', this.value)">
<div class="mcp-property-row-action">
<button class="mcp-property-delete-btn" onclick="event.stopPropagation(); window.mcpModule.deleteMcpProperty(${index})">删除</button>
</div>
<label class="mcp-checkbox-label">
<input type="checkbox" ${prop.required ? 'checked' : ''}
onchange="window.mcpModule.updateMcpProperty(${index}, 'required', this.checked)">
必填参数
</label>
</div>
`).join('');
}
/**
* 添加参数
* 获取数据类型标签
*/
function addMcpProperty() {
mcpProperties.push({ name: `param_${mcpProperties.length + 1}`, type: 'string', required: false, description: '' });
renderMcpProperties();
function getTypeLabel(type) {
const typeMap = {
'string': '字符串',
'integer': '整数',
'number': '数字',
'boolean': '布尔值',
'array': '数组',
'object': '对象'
};
return typeMap[type] || type;
}
/**
* 更新参数
* 添加参数 - 打开参数编辑模态框
*/
function updateMcpProperty(index, field, value) {
if (field === 'name') {
const isDuplicate = mcpProperties.some((p, i) => i !== index && p.name === value);
if (isDuplicate) {
alert('参数名称已存在,请使用不同的名称');
renderMcpProperties();
return;
function addMcpProperty() {
openPropertyModal();
}
/**
* 编辑参数 - 打开参数编辑模态框
*/
function editMcpProperty(index) {
openPropertyModal(index);
}
/**
* 打开参数编辑模态框
*/
function openPropertyModal(index = null) {
const form = document.getElementById('mcpPropertyForm');
const title = document.getElementById('mcpPropertyModalTitle');
document.getElementById('mcpPropertyIndex').value = index !== null ? index : -1;
if (index !== null) {
const prop = mcpProperties[index];
title.textContent = '编辑参数';
document.getElementById('mcpPropertyName').value = prop.name;
document.getElementById('mcpPropertyType').value = prop.type || 'string';
document.getElementById('mcpPropertyMinimum').value = prop.minimum !== undefined ? prop.minimum : '';
document.getElementById('mcpPropertyMaximum').value = prop.maximum !== undefined ? prop.maximum : '';
document.getElementById('mcpPropertyDescription').value = prop.description || '';
document.getElementById('mcpPropertyRequired').checked = prop.required || false;
} else {
title.textContent = '添加参数';
form.reset();
document.getElementById('mcpPropertyName').value = `param_${mcpProperties.length + 1}`;
document.getElementById('mcpPropertyType').value = 'string';
document.getElementById('mcpPropertyMinimum').value = '';
document.getElementById('mcpPropertyMaximum').value = '';
document.getElementById('mcpPropertyDescription').value = '';
document.getElementById('mcpPropertyRequired').checked = false;
}
updatePropertyRangeVisibility();
document.getElementById('mcpPropertyModal').style.display = 'flex';
}
/**
* 关闭参数编辑模态框
*/
function closePropertyModal() {
document.getElementById('mcpPropertyModal').style.display = 'none';
}
/**
* 更新数值范围输入框的可见性
*/
function updatePropertyRangeVisibility() {
const type = document.getElementById('mcpPropertyType').value;
const rangeGroup = document.getElementById('mcpPropertyRangeGroup');
if (type === 'integer' || type === 'number') {
rangeGroup.style.display = 'block';
} else {
rangeGroup.style.display = 'none';
}
}
/**
* 处理参数表单提交
*/
function handlePropertySubmit(e) {
e.preventDefault();
const index = parseInt(document.getElementById('mcpPropertyIndex').value);
const name = document.getElementById('mcpPropertyName').value.trim();
const type = document.getElementById('mcpPropertyType').value;
const minimum = document.getElementById('mcpPropertyMinimum').value;
const maximum = document.getElementById('mcpPropertyMaximum').value;
const description = document.getElementById('mcpPropertyDescription').value.trim();
const required = document.getElementById('mcpPropertyRequired').checked;
// 检查名称重复
const isDuplicate = mcpProperties.some((p, i) => i !== index && p.name === name);
if (isDuplicate) {
alert('参数名称已存在,请使用不同的名称');
return;
}
const propData = {
name,
type,
description,
required
};
// 数值类型添加范围限制
if (type === 'integer' || type === 'number') {
if (minimum !== '') {
propData.minimum = parseFloat(minimum);
}
if (maximum !== '') {
propData.maximum = parseFloat(maximum);
}
}
mcpProperties[index][field] = value;
if (field === 'type' && value !== 'integer' && value !== 'number') {
delete mcpProperties[index].minimum;
delete mcpProperties[index].maximum;
renderMcpProperties();
if (index >= 0) {
mcpProperties[index] = propData;
} else {
mcpProperties.push(propData);
}
renderMcpProperties();
closePropertyModal();
}
/**
@@ -215,6 +288,14 @@ function setupMcpEventListeners() {
const cancelBtn = document.getElementById('cancelMcpBtn');
const form = document.getElementById('mcpToolForm');
const addPropertyBtn = document.getElementById('addMcpPropertyBtn');
// 参数编辑模态框相关元素
const propertyModal = document.getElementById('mcpPropertyModal');
const closePropertyBtn = document.getElementById('closeMcpPropertyModalBtn');
const cancelPropertyBtn = document.getElementById('cancelMcpPropertyBtn');
const propertyForm = document.getElementById('mcpPropertyForm');
const propertyTypeSelect = document.getElementById('mcpPropertyType');
// Return early if required elements don't exist (e.g., in test environment)
if (!toggleBtn || !panel || !addBtn || !modal || !closeBtn || !cancelBtn || !form || !addPropertyBtn) {
return;
@@ -234,6 +315,17 @@ function setupMcpEventListeners() {
if (e.target === modal) closeMcpModal();
});
form.addEventListener('submit', handleMcpSubmit);
// 参数编辑模态框事件
if (propertyModal && closePropertyBtn && cancelPropertyBtn && propertyForm && propertyTypeSelect) {
closePropertyBtn.addEventListener('click', closePropertyModal);
cancelPropertyBtn.addEventListener('click', closePropertyModal);
propertyModal.addEventListener('click', (e) => {
if (e.target === propertyModal) closePropertyModal();
});
propertyForm.addEventListener('submit', handlePropertySubmit);
propertyTypeSelect.addEventListener('change', updatePropertyRangeVisibility);
}
}
/**
@@ -275,7 +367,7 @@ function openMcpModal(index = null) {
mcpProperties = [];
}
renderMcpProperties();
document.getElementById('mcpToolModal').style.display = 'block';
document.getElementById('mcpToolModal').style.display = 'flex';
}
/**
@@ -447,4 +539,4 @@ export async function executeMcpTool(toolName, toolArgs) {
}
// 暴露全局方法供 HTML 内联事件调用
window.mcpModule = { updateMcpProperty, deleteMcpProperty, editMcpTool, deleteMcpTool };
window.mcpModule = { addMcpProperty, editMcpProperty, deleteMcpProperty, editMcpTool, deleteMcpTool };
+68 -9
View File
@@ -244,20 +244,23 @@
</div>
<div class="input-group">
<label for="mcpToolDescription">工具描述 *</label>
<textarea id="mcpToolDescription" placeholder="详细描述工具的功能和使用场景..." required></textarea>
<textarea id="mcpToolDescription" placeholder="详细描述工具的功能和使用场景..." required>
</textarea>
</div>
<div class="input-group">
<label>输入参数</label>
<div class="properties-container">
<div id="mcpPropertiesContainer">
<div style="text-align: center; padding: 20px; color: #999; font-size: 14px;">
暂无参数,点击下方按钮添加参数
</div>
</div>
<button type="button" class="btn-secondary" id="addMcpPropertyBtn">
<div class="input-group-header">
<label>输入参数</label>
<button type="button" class="properties-btn-primary" id="addMcpPropertyBtn">
➕ 添加参数
</button>
</div>
<div class="properties-container">
<div class="mcp-empty-state" id="mcpEmptyState">
暂无参数,点击上方按钮添加参数
</div>
<div class="mcp-properties-list" id="mcpPropertiesContainer">
</div>
</div>
</div>
<div class="input-group">
<label for="mcpMockResponse">
@@ -275,6 +278,62 @@
</div>
</div>
<!-- 参数编辑模态框 -->
<div id="mcpPropertyModal" class="modal">
<div class="modal-content property-modal">
<div class="modal-header">
<h2 id="mcpPropertyModalTitle">编辑参数</h2>
<button class="close-btn" id="closeMcpPropertyModalBtn">&times;</button>
</div>
<div class="modal-body">
<form id="mcpPropertyForm">
<input type="hidden" id="mcpPropertyIndex" value="-1">
<div class="input-group">
<label for="mcpPropertyName">参数名称 *</label>
<input type="text" id="mcpPropertyName" placeholder="例如: param_1" required>
</div>
<div class="input-group">
<label for="mcpPropertyType">数据类型 *</label>
<select id="mcpPropertyType" class="model-select" required>
<option value="string">字符串</option>
<option value="integer">整数</option>
<option value="number">数字</option>
<option value="boolean">布尔值</option>
<option value="array">数组</option>
<option value="object">对象</option>
</select>
</div>
<div class="input-group" id="mcpPropertyRangeGroup" style="display: none;">
<div class="config-row">
<div class="config-item">
<label for="mcpPropertyMinimum">最小值</label>
<input type="number" id="mcpPropertyMinimum" placeholder="可选">
</div>
<div class="config-item">
<label for="mcpPropertyMaximum">最大值</label>
<input type="number" id="mcpPropertyMaximum" placeholder="可选">
</div>
</div>
</div>
<div class="input-group">
<label for="mcpPropertyDescription">参数描述</label>
<input type="text" id="mcpPropertyDescription" placeholder="可选">
</div>
<div class="input-group">
<label class="mcp-checkbox-label">
<input type="checkbox" id="mcpPropertyRequired">
必填参数
</label>
</div>
<div class="modal-actions">
<button type="button" class="btn-secondary" id="cancelMcpPropertyBtn">取消</button>
<button type="submit" class="btn-primary">保存</button>
</div>
</form>
</div>
</div>
</div>
<!-- 背景加载 -->
<script src="js/ui/background-load.js?v=0127"></script>