Files
Uni-Lab-OS/unilabos/app/web/templates/registry_editor.html
2025-09-07 12:53:00 +08:00

1086 lines
29 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "base.html" %} {% block title %}注册表编辑器 - UniLab{% endblock %}
{% block header %}注册表编辑器{% endblock %} {% block nav %}
{% endblock %} {% block scripts %}
<style>
.editor-container {
max-width: 100%;
margin: 0;
padding: 10px;
position: relative;
}
.form-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #333;
}
.form-control {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 14px;
box-sizing: border-box;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
margin-right: 10px;
transition: background-color 0.2s;
}
.btn-primary {
background-color: #2196f3;
color: white;
}
.btn-primary:hover {
background-color: #0b7dda;
}
.btn-secondary {
background-color: #6c757d;
color: white;
}
.btn-secondary:hover {
background-color: #545b62;
}
.radio-group {
display: flex;
gap: 15px;
margin-top: 8px;
}
.radio-option {
display: flex;
align-items: center;
gap: 5px;
}
.log-container {
background: #1e1e1e;
color: #ffffff;
border-radius: 4px;
padding: 15px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 13px;
max-height: 400px;
overflow-y: auto;
white-space: pre-wrap;
word-wrap: break-word;
border: 1px solid #444;
}
.status-indicator {
display: inline-block;
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 8px;
}
.status-disconnected {
background-color: #f44336;
}
.status-connected {
background-color: #4caf50;
}
.status-processing {
background-color: #ff9800;
}
.results-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.results-tabs {
display: flex;
margin-bottom: 15px;
border-bottom: 1px solid #ddd;
}
.result-tab {
padding: 10px 20px;
cursor: pointer;
border-bottom: 2px solid transparent;
transition: all 0.2s;
}
.result-tab.active {
border-bottom-color: #2196f3;
color: #2196f3;
font-weight: bold;
}
.result-tab:hover {
background-color: #f5f5f5;
}
.result-content {
display: none;
}
.result-content.active {
display: block;
}
.schema-viewer {
background: #f8f9fa;
border: 1px solid #dee2e6;
border-radius: 4px;
padding: 15px;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 13px;
max-height: 500px;
overflow: auto;
white-space: pre-wrap;
}
.error-message {
background-color: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
}
.success-message {
background-color: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
padding: 10px;
border-radius: 4px;
margin-bottom: 15px;
}
.file-info {
background: #e9f7fe;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
font-size: 13px;
color: #555;
}
.file-input-group {
display: flex;
gap: 10px;
align-items: center;
}
.file-browse-btn {
white-space: nowrap;
min-width: 100px;
}
.form-text {
display: block;
margin-top: 5px;
font-size: 12px;
color: #6c757d;
}
.file-analysis-status {
background: #fff3cd;
border: 1px solid #ffeaa7;
color: #856404;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
font-size: 13px;
}
.class-info {
background: #d1ecf1;
border: 1px solid #bee5eb;
color: #0c5460;
padding: 10px;
border-radius: 4px;
margin-top: 10px;
font-size: 13px;
}
.file-browser-container {
border: 1px solid #ddd;
border-radius: 4px;
background: white;
}
.current-path {
padding: 8px 12px;
background: #f8f9fa;
border-bottom: 1px solid #ddd;
font-size: 13px;
color: #555;
}
.file-browser {
max-height: 300px;
overflow-y: auto;
padding: 0;
}
.file-item {
padding: 8px 12px;
border-bottom: 1px solid #eee;
cursor: pointer;
display: flex;
align-items: center;
transition: background-color 0.2s;
}
.file-item:hover {
background-color: #f8f9fa;
}
.file-item.selected {
background-color: #e3f2fd;
border-left: 3px solid #2196f3;
}
.file-icon {
margin-right: 8px;
font-size: 16px;
width: 20px;
text-align: center;
}
.file-name {
flex: 1;
font-size: 14px;
}
.file-size {
font-size: 12px;
color: #666;
margin-left: 8px;
}
.directory-icon {
color: #ff9800;
}
.python-file-icon {
color: #4caf50;
}
.other-file-icon {
color: #666;
}
.selected-file {
padding: 8px 12px;
background: #e8f5e8;
border-top: 1px solid #ddd;
font-size: 13px;
}
.loading-indicator {
padding: 20px;
text-align: center;
color: #666;
font-style: italic;
}
.yaml-container {
border: 1px solid #ddd;
border-radius: 4px;
background: white;
}
.yaml-header {
padding: 10px 12px;
background: #f8f9fa;
border-bottom: 1px solid #ddd;
display: flex;
justify-content: space-between;
align-items: center;
}
.copy-yaml-btn {
font-size: 12px;
padding: 4px 12px;
}
/* 连接状态右上角定位 */
.connection-status-fixed {
position: fixed;
top: 80px;
right: 20px;
background: white;
padding: 10px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
border: 1px solid #ddd;
}
/* 主要内容区域两栏布局 */
.main-content {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.left-column {
flex: 1;
min-width: 0;
}
.right-column {
flex: 1;
min-width: 0;
}
/* 日志区域在底部 */
.log-section-bottom {
background: white;
border-top: 2px solid #e0e0e0;
padding: 15px;
margin-top: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
/* 响应式设计 */
@media (max-width: 1024px) {
.main-content {
flex-direction: column;
}
.connection-status-fixed {
position: relative;
top: auto;
right: auto;
margin-bottom: 20px;
}
}
</style>
{% endblock %} {% block content %}
<div class="editor-container">
<!-- 连接状态 - 右上角固定 -->
<div class="connection-status-fixed">
<div id="connection-status">
<span class="status-indicator status-disconnected" id="status-dot"></span>
<span id="status-text">未连接</span>
</div>
</div>
<!-- 主要内容区域 -->
<div class="main-content">
<!-- 左栏:文件导入配置 -->
<div class="left-column">
<div class="form-section">
<h3>文件导入配置</h3>
<div class="form-group">
<label for="registry-type">注册表类型</label>
<div class="radio-group">
<div class="radio-option">
<input
type="radio"
id="type-device"
name="registry-type"
value="device"
checked
/>
<label for="type-device">设备 (Device)</label>
</div>
<div class="radio-option">
<input
type="radio"
id="type-resource"
name="registry-type"
value="resource"
/>
<label for="type-resource">资源 (Resource)</label>
</div>
</div>
</div>
<div class="form-group">
<label for="file-browser">选择Python文件</label>
<div class="file-browser-container">
<!-- 当前路径显示 -->
<div class="current-path">
<strong>当前路径:</strong>
<span id="current-path-display">加载中...</span>
</div>
<!-- 文件浏览器 -->
<div class="file-browser" id="file-browser">
<div class="loading-indicator">正在加载文件列表...</div>
</div>
<!-- 选择的文件显示 -->
<div class="selected-file" id="selected-file" style="display: none">
<strong>已选择:</strong>
<span id="selected-file-name"></span>
<span id="selected-file-size" class="file-size"></span>
</div>
</div>
<small class="form-text">只显示 .py 文件和文件夹</small>
</div>
<div class="form-group">
<button
class="btn btn-secondary"
onclick="analyzeFile()"
id="analyze-btn"
disabled
>
分析文件
</button>
<small class="form-text">首先分析文件以获取可用的类列表</small>
</div>
<div
class="form-group"
id="class-selection-group"
style="display: none"
>
<label for="class-name" id="class-selection-label">选择类名</label>
<select id="class-name" class="form-control">
<option value="" id="class-selection-option">请选择类...</option>
</select>
</div>
<div class="form-group">
<button
class="btn btn-primary"
onclick="importFile()"
id="import-btn"
disabled
>
生成注册表
</button>
<button class="btn btn-secondary" onclick="clearResults()">
重置
</button>
</div>
<div id="workflow-tips" class="class-info">
<strong>使用流程:</strong><br />
1. 选择注册表类型(设备/资源)<br />
2. 在文件浏览器中导航并选择Python文件<br />
3. 点击"分析文件"获取可用类列表<br />
4. 从下拉列表中选择要注册的类<br />
5. 点击"生成注册表"获得YAML配置文件
</div>
<div id="file-info" class="file-info" style="display: none">
<strong>文件信息:</strong>
<div id="file-details"></div>
</div>
</div>
</div>
<!-- 右栏:注册表配置结果 -->
<div class="right-column">
<div id="results-section" class="form-section" style="display: none">
<h3>注册表配置结果</h3>
<div class="yaml-container">
<div class="yaml-header">
<strong>YAML格式注册表配置</strong>
<button
class="btn btn-secondary copy-yaml-btn"
onclick="copyYamlToClipboard()"
>
复制YAML
</button>
</div>
<div class="schema-viewer" id="yaml-registry-viewer">等待数据...</div>
</div>
</div>
</div>
</div>
<!-- 处理日志 - 底部 -->
<div class="log-section-bottom">
<h3>处理日志</h3>
<div id="log-output" class="log-container">等待操作...</div>
</div>
</div>
<script>
let ws = null;
let isConnected = false;
let currentPath = '';
let selectedFilePath = null;
// 页面加载完成后初始化
document.addEventListener('DOMContentLoaded', function () {
initWebSocket();
loadFileBrowser(''); // 加载根目录
initRegistryTypeListener();
});
// 初始化注册表类型监听器
function initRegistryTypeListener() {
const radioButtons = document.querySelectorAll(
'input[name="registry-type"]'
);
radioButtons.forEach((radio) => {
radio.addEventListener('change', function () {
updateClassSelectionLabel(this.value);
});
});
// 初始化标签文字
const checkedRadio = document.querySelector(
'input[name="registry-type"]:checked'
);
if (checkedRadio) {
updateClassSelectionLabel(checkedRadio.value);
}
}
// 更新类选择标签文字
function updateClassSelectionLabel(registryType) {
const label = document.getElementById('class-selection-label');
const option = document.getElementById('class-selection-option');
if (registryType === 'resource') {
label.textContent = '选择类名/可执行函数';
option.textContent = '请选择类或函数...';
} else {
label.textContent = '选择类名';
option.textContent = '请选择类...';
}
}
// 初始化WebSocket连接
function initWebSocket() {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${protocol}//${window.location.host}/api/v1/ws/registry_editor`;
ws = new WebSocket(wsUrl);
ws.onopen = function (event) {
isConnected = true;
updateConnectionStatus('connected', '已连接');
addLog('WebSocket连接已建立', 'info');
};
ws.onmessage = function (event) {
try {
const data = JSON.parse(event.data);
handleWebSocketMessage(data);
} catch (e) {
addLog(`解析消息失败: ${e.message}`, 'error');
}
};
ws.onclose = function (event) {
isConnected = false;
updateConnectionStatus('disconnected', '连接已断开');
addLog('WebSocket连接已断开', 'warning');
// 5秒后尝试重连
setTimeout(initWebSocket, 5000);
};
ws.onerror = function (error) {
addLog('WebSocket连接错误', 'error');
updateConnectionStatus('disconnected', '连接错误');
};
}
// 更新连接状态
function updateConnectionStatus(status, text) {
const statusDot = document.getElementById('status-dot');
const statusText = document.getElementById('status-text');
statusDot.className = `status-indicator status-${status}`;
statusText.textContent = text;
}
// 处理WebSocket消息
function handleWebSocketMessage(data) {
switch (data.type) {
case 'log':
addLog(data.message, data.level || 'info');
break;
case 'progress':
updateConnectionStatus('processing', data.message);
break;
case 'result':
handleAnalysisResult(data.data);
break;
case 'file_analysis_result':
handleFileAnalysisResult(data.data);
break;
case 'error':
addLog(data.message, 'error');
updateConnectionStatus('connected', '已连接');
break;
default:
addLog(`收到未知消息类型: ${data.type}`, 'warning');
}
}
// 处理分析结果
function handleAnalysisResult(result) {
updateConnectionStatus('connected', '已连接');
// 显示结果区域
document.getElementById('results-section').style.display = 'block';
// 显示YAML格式的注册表配置
if (result.registry_schema) {
// 直接显示后端返回的YAML字符串
const yamlViewer = document.getElementById('yaml-registry-viewer');
yamlViewer.textContent = result.registry_schema;
// 保存YAML内容供复制使用
window.registryYaml = result.registry_schema;
addLog(`生成的设备ID: ${result.device_id}`, 'info');
}
addLog('注册表生成完成!', 'success');
}
// 复制YAML到剪贴板
function copyYamlToClipboard() {
if (!window.registryYaml) {
addLog('没有可复制的YAML内容', 'error');
return;
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard
.writeText(window.registryYaml)
.then(() => {
addLog('YAML配置已复制到剪贴板', 'success');
// 视觉反馈
const copyBtn = document.querySelector('.copy-yaml-btn');
const originalText = copyBtn.textContent;
copyBtn.textContent = '已复制!';
copyBtn.style.backgroundColor = '#4caf50';
setTimeout(() => {
copyBtn.textContent = originalText;
copyBtn.style.backgroundColor = '';
}, 2000);
})
.catch((err) => {
addLog('复制失败,请手动复制', 'error');
});
} else {
// 后备方案:选择文本
const yamlViewer = document.getElementById('yaml-registry-viewer');
const range = document.createRange();
range.selectNodeContents(yamlViewer);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
addLog('已选择YAML文本请按Ctrl+C复制', 'info');
}
}
// 添加日志
function addLog(message, level = 'info') {
const logOutput = document.getElementById('log-output');
const timestamp = new Date().toLocaleTimeString();
let prefix = '';
switch (level) {
case 'error':
prefix = '[ERROR]';
break;
case 'warning':
prefix = '[WARN]';
break;
case 'success':
prefix = '[SUCCESS]';
break;
case 'info':
default:
prefix = '[INFO]';
break;
}
const logLine = `${timestamp} ${prefix} ${message}\n`;
logOutput.textContent += logLine;
logOutput.scrollTop = logOutput.scrollHeight;
}
// 加载文件浏览器
async function loadFileBrowser(path) {
try {
const response = await fetch(
`/api/v1/file-browser?path=${encodeURIComponent(path)}`
);
const result = await response.json();
if (result.code === 0) {
const data = result.data;
currentPath = data.current_path;
renderFileBrowser(data);
} else {
addLog(`加载目录失败: ${result.message}`, 'error');
}
} catch (error) {
addLog(`加载目录时出错: ${error}`, 'error');
}
}
// 渲染文件浏览器
function renderFileBrowser(data) {
const fileBrowser = document.getElementById('file-browser');
const currentPathDisplay = document.getElementById('current-path-display');
// 更新当前路径显示
currentPathDisplay.textContent = data.current_path || data.working_dir;
// 清空文件浏览器
fileBrowser.innerHTML = '';
// 添加返回上级目录选项(除了根路径)
const currentPath = data.current_path || data.working_dir;
if (currentPath && currentPath !== '' && !isRootPath(currentPath)) {
const parentItem = document.createElement('div');
parentItem.className = 'file-item';
parentItem.innerHTML = `
<span class="file-icon directory-icon">⬆️</span>
<span class="file-name">..</span>
<span class="file-size">(返回上级目录)</span>
`;
// 计算上级目录路径
const parentPath = currentPath.split(/[/\\]/).slice(0, -1).join('/');
parentItem.onclick = () => loadFileBrowser(parentPath);
fileBrowser.appendChild(parentItem);
}
if (data.items.length === 0) {
if (fileBrowser.children.length === 0) {
fileBrowser.innerHTML =
'<div class="loading-indicator">此目录为空</div>';
}
return;
}
// 渲染文件和目录
data.items.forEach((item) => {
const fileItem = document.createElement('div');
fileItem.className = 'file-item';
fileItem.dataset.path = item.path;
fileItem.dataset.type = item.type;
let icon = '';
let iconClass = '';
if (item.type === 'directory') {
icon = '📁';
iconClass = 'directory-icon';
} else if (item.is_python) {
icon = '🐍';
iconClass = 'python-file-icon';
} else {
return; // 只显示Python文件和目录
}
let sizeText = '';
if (item.type === 'file') {
sizeText = `<span class="file-size">(${formatFileSize(
item.size
)})</span>`;
}
fileItem.innerHTML = `
<span class="file-icon ${iconClass}">${icon}</span>
<span class="file-name">${item.name}</span>
${sizeText}
`;
// 添加点击事件
if (item.type === 'directory') {
fileItem.onclick = () => loadFileBrowser(item.path);
} else if (item.is_python) {
fileItem.onclick = () => selectFile(item);
}
fileBrowser.appendChild(fileItem);
});
}
// 选择文件
function selectFile(fileInfo) {
// 清除之前的选择
document.querySelectorAll('.file-item').forEach((item) => {
item.classList.remove('selected');
});
// 标记当前选择
event.target.closest('.file-item').classList.add('selected');
// 保存选择的文件信息
selectedFilePath = fileInfo.path;
window.selectedFileInfo = fileInfo;
// 显示选择的文件信息
const selectedFileDiv = document.getElementById('selected-file');
const selectedFileName = document.getElementById('selected-file-name');
const selectedFileSize = document.getElementById('selected-file-size');
selectedFileName.textContent = fileInfo.name;
selectedFileSize.textContent = `(${formatFileSize(fileInfo.size)})`;
selectedFileDiv.style.display = 'block';
// 启用分析按钮
const analyzeBtn = document.getElementById('analyze-btn');
analyzeBtn.disabled = false;
addLog(`已选择文件: ${fileInfo.name}`, 'info');
addLog('点击"分析文件"按钮开始分析', 'success');
}
// 判断是否为根路径
function isRootPath(path) {
if (!path || path === '') return true;
// Windows根路径如 C:, D:, C:\, D:\
if (/^[A-Za-z]:?\\?$/.test(path)) return true;
// Unix根路径
if (path === '/') return true;
// 网络路径根(\\server 或 //server
if (/^[\\\/]{2}[^\\\/]+$/.test(path)) return true;
return false;
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
}
// 分析文件
function analyzeFile() {
if (!isConnected) {
addLog('WebSocket未连接请等待连接建立', 'error');
return;
}
if (!selectedFilePath) {
addLog('请先选择文件', 'error');
return;
}
const analyzeBtn = document.getElementById('analyze-btn');
analyzeBtn.disabled = true;
analyzeBtn.textContent = '分析中...';
const request = {
type: 'analyze_file',
data: {
file_path: selectedFilePath,
},
};
ws.send(JSON.stringify(request));
addLog(`开始分析文件: ${selectedFilePath}`, 'info');
}
// 处理文件分析结果
function handleFileAnalysisResult(analysisResult) {
const analyzeBtn = document.getElementById('analyze-btn');
const classSelectionGroup = document.getElementById(
'class-selection-group'
);
const classSelect = document.getElementById('class-name');
analyzeBtn.disabled = false;
analyzeBtn.textContent = '重新分析';
if (analysisResult.success) {
const classes = analysisResult.classes || [];
const registryType = document.querySelector(
'input[name="registry-type"]:checked'
).value;
// 清空现有选项并设置默认选项
const defaultText =
registryType === 'resource' ? '请选择类或函数...' : '请选择类...';
classSelect.innerHTML = `<option value="">${defaultText}</option>`;
// 添加找到的类
classes.forEach((cls) => {
const option = document.createElement('option');
option.value = cls.name;
option.textContent = `${cls.name} - ${cls.docstring || '无描述'}`;
classSelect.appendChild(option);
});
if (classes.length > 0) {
classSelectionGroup.style.display = 'block';
addLog(
`找到 ${classes.length} 个类: ${classes
.map((c) => c.name)
.join(', ')}`,
'success'
);
// 显示分析状态
showFileAnalysisStatus(analysisResult);
// 监听类选择变化,启用导入按钮
classSelect.onchange = function () {
const importBtn = document.getElementById('import-btn');
if (this.value) {
importBtn.disabled = false;
addLog(`选择了类: ${this.value}`, 'info');
} else {
importBtn.disabled = true;
}
};
} else {
addLog('文件中未找到任何类定义', 'warning');
classSelectionGroup.style.display = 'none';
}
} else {
addLog(`文件分析失败: ${analysisResult.error}`, 'error');
classSelectionGroup.style.display = 'none';
}
}
// 显示文件分析状态
function showFileAnalysisStatus(analysisResult) {
// 移除现有的分析状态显示
const existingStatus = document.getElementById('file-analysis-status');
if (existingStatus) {
existingStatus.remove();
}
// 创建新的状态显示
const statusDiv = document.createElement('div');
statusDiv.id = 'file-analysis-status';
statusDiv.className = 'file-analysis-status';
let statusContent = `
<strong>文件分析结果:</strong><br>
文件路径: ${analysisResult.file_path}<br>
模块名: ${analysisResult.module_name}<br>
找到类数量: ${analysisResult.classes.length}
`;
if (analysisResult.classes.length > 0) {
statusContent += '<br><strong>可用类:</strong>';
analysisResult.classes.forEach((cls) => {
statusContent += `<br>• ${cls.name}: ${cls.docstring || '无描述'}`;
});
}
statusDiv.innerHTML = statusContent;
// 插入到类选择组前面
const classSelectionGroup = document.getElementById(
'class-selection-group'
);
classSelectionGroup.parentNode.insertBefore(statusDiv, classSelectionGroup);
}
// 导入文件
function importFile() {
if (!isConnected) {
addLog('WebSocket未连接请等待连接建立', 'error');
return;
}
if (!selectedFilePath) {
addLog('请先选择文件', 'error');
return;
}
const classSelect = document.getElementById('class-name');
const className = classSelect.value.trim();
if (!className) {
addLog('请先分析文件并选择要导入的类', 'error');
return;
}
const registryType = document.querySelector(
'input[name="registry-type"]:checked'
).value;
updateConnectionStatus('processing', '正在生成注册表...');
const request = {
type: 'import_file',
data: {
file_path: selectedFilePath,
registry_type: registryType,
class_name: className,
module_name: null, // 模块名自动生成
},
};
ws.send(JSON.stringify(request));
addLog(`开始导入文件: ${selectedFilePath}`, 'info');
addLog(`注册表类型: ${registryType}`, 'info');
addLog(`选择的类: ${className}`, 'info');
// 显示文件信息
showFileInfo(selectedFilePath, registryType, className);
}
// 显示文件信息
function showFileInfo(filePath, registryType, className) {
const fileInfo = document.getElementById('file-info');
const fileDetails = document.getElementById('file-details');
let details = `
<div><strong>文件路径:</strong> ${filePath}</div>
<div><strong>注册表类型:</strong> ${registryType}</div>
<div><strong>选择的类:</strong> ${className}</div>
`;
fileDetails.innerHTML = details;
fileInfo.style.display = 'block';
}
// 清空结果
function clearResults() {
// 重置文件选择
selectedFilePath = null;
window.selectedFileInfo = null;
// 清除文件选择状态
document.querySelectorAll('.file-item').forEach((item) => {
item.classList.remove('selected');
});
// 隐藏选择的文件显示
document.getElementById('selected-file').style.display = 'none';
// 重置分析按钮
const analyzeBtn = document.getElementById('analyze-btn');
analyzeBtn.disabled = true;
analyzeBtn.textContent = '分析文件';
// 重置导入按钮
const importBtn = document.getElementById('import-btn');
importBtn.disabled = true;
// 隐藏类选择
document.getElementById('class-selection-group').style.display = 'none';
// 根据当前注册表类型设置默认选项文字
const registryType = document.querySelector(
'input[name="registry-type"]:checked'
).value;
const defaultText =
registryType === 'resource' ? '请选择类或函数...' : '请选择类...';
document.getElementById(
'class-name'
).innerHTML = `<option value="">${defaultText}</option>`;
// 移除分析状态显示
const analysisStatus = document.getElementById('file-analysis-status');
if (analysisStatus) {
analysisStatus.remove();
}
// 清空其他显示区域
document.getElementById('results-section').style.display = 'none';
document.getElementById('file-info').style.display = 'none';
document.getElementById('log-output').textContent = '等待操作...';
// 清空YAML查看器
document.getElementById('yaml-registry-viewer').textContent = '等待数据...';
window.registryYaml = null;
addLog('已重置所有设置', 'info');
}
</script>
{% endblock %}