29 KiB
29 KiB
WebSocket 文件管理API文档
概述
通过WebSocket连接实现CAD文件的列表查询和下载功能。支持Creo、PDMS、Revit三种CAD软件的文件格式。
WebSocket连接
连接地址: ws://localhost:8000/api/v1/ws/connect
连接参数:
client_id: 客户端ID(可选,不提供会自动生成)user_id: 用户ID(可选)
连接示例:
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
支持的文件格式
📐 Creo 文件
.prt- 零件文件(Part).asm- 装配文件(Assembly).drw- 工程图文件(Drawing)- 特别支持版本号后缀:
.prt.1,.prt.2,.asm.1等
🏭 PDMS 文件
.rvm- 3D模型文件.dri- 图纸文件
🏗️ Revit 文件
.rvt- Revit项目文件.rfa- Revit族文件(Family).rte- Revit模板文件
WebSocket消息格式
1. 获取CAD文件列表
发送消息:
{
"type": "get_file_list"
}
响应消息:
{
"type": "info",
"message": "获取文件列表成功",
"data": {
"base_path": "C:\\Users\\Public\\Documents",
"total_count": 15,
"files": [
{
"filename": "part1.prt.1",
"relative_path": "creo/part1.prt.1",
"absolute_path": "C:\\Users\\Public\\Documents\\creo\\part1.prt.1",
"size": 1024000,
"modified_time": 1706950800.0,
"extension": ".prt.1"
},
{
"filename": "building.rvt",
"relative_path": "revit/building.rvt",
"absolute_path": "C:\\Users\\Public\\Documents\\revit\\building.rvt",
"size": 5120000,
"modified_time": 1706951000.0,
"extension": ".rvt"
}
]
},
"timestamp": "2026-02-04T10:30:00"
}
2. 获取单个文件下载链接
发送消息:
{
"type": "download_file",
"file_path": "creo/part1.prt.1"
}
响应消息:
{
"type": "info",
"message": "文件下载链接已生成",
"data": {
"file_path": "creo/part1.prt.1",
"filename": "part1.prt.1",
"download_url": "/api/v1/files/download/creo/part1.prt.1",
"file_size": 1024000
},
"timestamp": "2026-02-04T10:30:00"
}
使用下载链接:
// 获取到download_url后,使用HTTP GET请求下载
const downloadUrl = `http://localhost:8000${data.download_url}`;
window.open(downloadUrl, '_blank');
3. 获取批量下载链接
发送消息:
{
"type": "download_batch",
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm",
"revit/building.rvt"
]
}
响应消息:
{
"type": "info",
"message": "批量下载链接已生成",
"data": {
"file_count": 3,
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm",
"revit/building.rvt"
],
"download_url": "/api/v1/files/download/batch",
"method": "POST",
"note": "请使用POST方法,将file_paths作为JSON数组发送到此URL"
},
"timestamp": "2026-02-04T10:30:00"
}
使用批量下载:
// 使用fetch API进行POST请求
const response = await fetch('http://localhost:8000/api/v1/files/download/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data.file_paths)
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();
完整前端示例
JavaScript/WebSocket
class CADFileManager {
constructor(serverUrl = 'ws://localhost:8000/api/v1/ws/connect') {
this.ws = null;
this.serverUrl = serverUrl;
this.httpBaseUrl = 'http://localhost:8000';
}
// 连接WebSocket
connect(userId = 'user123') {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(`${this.serverUrl}?user_id=${userId}`);
this.ws.onopen = () => {
console.log('WebSocket连接成功');
resolve();
};
this.ws.onerror = (error) => {
console.error('WebSocket错误:', error);
reject(error);
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
});
}
// 处理服务器消息
handleMessage(message) {
console.log('收到消息:', message);
if (message.type === 'info' && message.data) {
// 处理不同类型的响应
if (message.data.files) {
this.onFileListReceived(message.data);
} else if (message.data.download_url) {
this.onDownloadUrlReceived(message.data);
}
} else if (message.type === 'error') {
console.error('错误:', message.message);
}
}
// 获取文件列表
getFileList() {
this.ws.send(JSON.stringify({
type: 'get_file_list'
}));
}
// 文件列表回调(可以被覆盖)
onFileListReceived(data) {
console.log('文件列表:', data.files);
console.log('总数:', data.total_count);
}
// 请求单个文件下载
requestDownload(filePath) {
this.ws.send(JSON.stringify({
type: 'download_file',
file_path: filePath
}));
}
// 下载URL回调
onDownloadUrlReceived(data) {
const fullUrl = `${this.httpBaseUrl}${data.download_url}`;
console.log('下载链接:', fullUrl);
// 自动触发下载
window.open(fullUrl, '_blank');
}
// 请求批量下载
async requestBatchDownload(filePaths) {
// 批量下载直接使用HTTP POST
const response = await fetch(`${this.httpBaseUrl}/api/v1/files/download/batch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(filePaths)
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();
window.URL.revokeObjectURL(url);
}
// 断开连接
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// 使用示例
async function main() {
const manager = new CADFileManager();
// 连接
await manager.connect('user123');
// 自定义文件列表回调
manager.onFileListReceived = (data) => {
console.log(`找到 ${data.total_count} 个文件`);
data.files.forEach(file => {
console.log(`- ${file.filename} (${(file.size / 1024).toFixed(2)} KB)`);
});
};
// 获取文件列表
manager.getFileList();
// 下载单个文件
setTimeout(() => {
manager.requestDownload('creo/part1.prt.1');
}, 2000);
// 批量下载
setTimeout(async () => {
await manager.requestBatchDownload([
'creo/part1.prt.1',
'revit/building.rvt'
]);
}, 4000);
}
main();
React示例
import React, { useState, useEffect, useRef } from 'react';
function CADFileManager() {
const [files, setFiles] = useState([]);
const [selectedFiles, setSelectedFiles] = useState([]);
const [connected, setConnected] = useState(false);
const wsRef = useRef(null);
useEffect(() => {
// 连接WebSocket
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
ws.onopen = () => {
console.log('WebSocket连接成功');
setConnected(true);
// 连接成功后立即获取文件列表
ws.send(JSON.stringify({ type: 'get_file_list' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'info' && message.data?.files) {
setFiles(message.data.files);
} else if (message.type === 'info' && message.data?.download_url) {
// 自动打开下载链接
window.open(`http://localhost:8000${message.data.download_url}`, '_blank');
}
};
wsRef.current = ws;
return () => {
ws.close();
};
}, []);
// 刷新文件列表
const refreshFileList = () => {
if (wsRef.current && connected) {
wsRef.current.send(JSON.stringify({ type: 'get_file_list' }));
}
};
// 下载单个文件
const downloadFile = (filePath) => {
if (wsRef.current && connected) {
wsRef.current.send(JSON.stringify({
type: 'download_file',
file_path: filePath
}));
}
};
// 批量下载
const downloadBatch = async () => {
const response = await fetch('http://localhost:8000/api/v1/files/download/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(selectedFiles)
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();
window.URL.revokeObjectURL(url);
};
// 切换文件选择
const toggleFileSelection = (filePath) => {
setSelectedFiles(prev =>
prev.includes(filePath)
? prev.filter(f => f !== filePath)
: [...prev, filePath]
);
};
return (
<div style={{ padding: '20px' }}>
<h1>CAD文件管理器</h1>
<div style={{ marginBottom: '20px' }}>
<span>连接状态: {connected ? '✅ 已连接' : '❌ 未连接'}</span>
<button onClick={refreshFileList} style={{ marginLeft: '10px' }}>
刷新列表
</button>
<button
onClick={downloadBatch}
disabled={selectedFiles.length === 0}
style={{ marginLeft: '10px' }}
>
批量下载 ({selectedFiles.length})
</button>
</div>
<div>
<h2>文件列表 (共 {files.length} 个)</h2>
<table border="1" cellPadding="8" style={{ width: '100%' }}>
<thead>
<tr>
<th>选择</th>
<th>文件名</th>
<th>路径</th>
<th>大小</th>
<th>扩展名</th>
<th>操作</th>
</tr>
</thead>
<tbody>
{files.map(file => (
<tr key={file.absolute_path}>
<td>
<input
type="checkbox"
checked={selectedFiles.includes(file.relative_path)}
onChange={() => toggleFileSelection(file.relative_path)}
/>
</td>
<td>{file.filename}</td>
<td>{file.relative_path}</td>
<td>{(file.size / 1024).toFixed(2)} KB</td>
<td>{file.extension}</td>
<td>
<button onClick={() => downloadFile(file.relative_path)}>
下载
</button>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
);
}
export default CADFileManager;
错误处理
错误响应格式:
{
"type": "error",
"message": "错误描述",
"timestamp": "2026-02-04T10:30:00"
}
常见错误:
"缺少参数: file_path"- 未提供文件路径"缺少参数: file_paths (必须是数组)"- 批量下载参数格式错误"访问被拒绝"- 文件路径不安全"文件不存在"- 请求的文件不存在"只能下载CAD文件"- 尝试下载非CAD格式文件"获取文件列表失败"- 扫描文件系统失败
注意事项
-
WebSocket用于控制,HTTP用于下载
- 文件列表通过WebSocket返回
- 文件下载使用HTTP接口(因为WebSocket不适合传输大文件)
-
路径安全
- 所有文件路径都会进行安全检查
- 只能访问配置目录内的文件
-
文件格式限制
- 只能下载Creo、PDMS、Revit格式的文件
- 自动识别带版本号的Creo文件
-
批量下载
- 文件打包为ZIP格式
- 保持原有目录结构
- 自动跳过不存在或不安全的文件
-
配置路径
- 默认路径:
C:\Users\Public\Documents - 可在
configs/software_config.yaml中修改
- 默认路径:
文件批量重命名功能
4. 获取重命名策略列表
发送消息:
{
"type": "get_rename_strategies"
}
响应消息:
{
"type": "info",
"message": "获取重命名策略成功",
"data": {
"strategies": [
{
"name": "add_prefix",
"description": "在文件名前添加前缀"
},
{
"name": "add_suffix",
"description": "在扩展名前添加后缀"
},
{
"name": "sequence",
"description": "使用序号重命名文件"
},
{
"name": "replace_text",
"description": "替换文件名中的文本"
},
{
"name": "add_datetime",
"description": "添加日期时间戳"
},
{
"name": "change_case",
"description": "转换文件名大小写"
}
],
"total_count": 6
},
"timestamp": "2026-02-04T11:10:00"
}
5. 预览重命名结果
发送消息:
{
"type": "preview_rename",
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm",
"revit/building.rvt"
],
"strategy": "add_prefix",
"params": {
"prefix": "NEW_"
}
}
响应消息:
{
"type": "info",
"message": "重命名预览生成成功",
"data": {
"strategy": "add_prefix",
"params": {
"prefix": "NEW_"
},
"preview": [
{
"original": "part1.prt.1",
"new": "NEW_part1.prt.1",
"success": true
},
{
"original": "assembly.asm",
"new": "NEW_assembly.asm",
"success": true
},
{
"original": "building.rvt",
"new": "NEW_building.rvt",
"success": true
}
],
"total_count": 3
},
"timestamp": "2026-02-04T11:10:00"
}
6. 执行批量重命名
发送消息:
{
"type": "rename_files",
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm"
],
"strategy": "sequence",
"params": {
"base_name": "component",
"start_number": 1,
"digits": 3,
"separator": "_"
}
}
响应消息:
{
"type": "info",
"message": "批量重命名完成: 成功 2 个, 失败 0 个",
"data": {
"strategy": "sequence",
"params": {
"base_name": "component",
"start_number": 1,
"digits": 3,
"separator": "_"
},
"success_count": 2,
"failed_count": 0,
"results": [
{
"original_path": "creo/part1.prt.1",
"original_name": "part1.prt.1",
"new_name": "component_001.prt.1",
"new_path": "creo/component_001.prt.1",
"success": true
},
{
"original_path": "creo/assembly.asm",
"original_name": "assembly.asm",
"new_name": "component_002.asm",
"new_path": "creo/component_002.asm",
"success": true
}
]
},
"timestamp": "2026-02-04T11:10:00"
}
重命名策略参数说明
1. add_prefix(添加前缀)
{
"strategy": "add_prefix",
"params": {
"prefix": "NEW_"
}
}
2. add_suffix(添加后缀)
{
"strategy": "add_suffix",
"params": {
"suffix": "_backup"
}
}
3. sequence(序号重命名)
{
"strategy": "sequence",
"params": {
"base_name": "file",
"start_number": 1,
"digits": 3,
"separator": "_"
}
}
base_name: 基础文件名start_number: 起始序号(默认1)digits: 序号位数(默认3,如001)separator: 分隔符(默认下划线)
4. replace_text(文本替换)
{
"strategy": "replace_text",
"params": {
"search": "old",
"replace": "new",
"case_sensitive": true
}
}
search: 要搜索的文本replace: 替换为的文本case_sensitive: 是否区分大小写(默认true)
5. add_datetime(添加日期时间)
{
"strategy": "add_datetime",
"params": {
"format": "%Y%m%d_%H%M%S",
"position": "suffix",
"separator": "_"
}
}
format: 日期时间格式(默认:%Y%m%d_%H%M%S)position: 位置,prefix或suffix(默认suffix)separator: 分隔符(默认下划线)
6. change_case(大小写转换)
{
"strategy": "change_case",
"params": {
"case_type": "lower"
}
}
case_type: 转换类型,可选值:lower(小写)、upper(大写)、title(标题格式)
重命名功能使用示例
JavaScript示例
// 扩展之前的 CADFileManager 类
class CADFileManager {
// ... 之前的代码 ...
// 获取重命名策略列表
getRenameStrategies() {
this.ws.send(JSON.stringify({
type: 'get_rename_strategies'
}));
}
// 预览重命名
previewRename(filePaths, strategy, params) {
this.ws.send(JSON.stringify({
type: 'preview_rename',
file_paths: filePaths,
strategy: strategy,
params: params
}));
}
// 执行批量重命名
renameFiles(filePaths, strategy, params) {
this.ws.send(JSON.stringify({
type: 'rename_files',
file_paths: filePaths,
strategy: strategy,
params: params
}));
}
}
// 使用示例
async function renameExample() {
const manager = new CADFileManager();
await manager.connect('user123');
// 1. 获取策略列表
manager.getRenameStrategies();
// 2. 预览重命名(添加前缀)
setTimeout(() => {
manager.previewRename(
['creo/part1.prt.1', 'creo/assembly.asm'],
'add_prefix',
{ prefix: 'V2_' }
);
}, 1000);
// 3. 执行重命名(序号重命名)
setTimeout(() => {
manager.renameFiles(
['creo/part1.prt.1', 'creo/part2.prt.1'],
'sequence',
{
base_name: 'component',
start_number: 1,
digits: 3,
separator: '_'
}
);
}, 3000);
}
React示例
function CADFileRenamer() {
const [files, setFiles] = useState([]);
const [selectedFiles, setSelectedFiles] = useState([]);
const [strategies, setStrategies] = useState([]);
const [selectedStrategy, setSelectedStrategy] = useState('');
const [renameParams, setRenameParams] = useState({});
const [previewResults, setPreviewResults] = useState([]);
const wsRef = useRef(null);
useEffect(() => {
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
ws.onopen = () => {
// 获取文件列表和策略列表
ws.send(JSON.stringify({ type: 'get_file_list' }));
ws.send(JSON.stringify({ type: 'get_rename_strategies' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'info') {
if (message.data?.files) {
setFiles(message.data.files);
} else if (message.data?.strategies) {
setStrategies(message.data.strategies);
} else if (message.data?.preview) {
setPreviewResults(message.data.preview);
} else if (message.data?.results) {
// 重命名完成,刷新文件列表
ws.send(JSON.stringify({ type: 'get_file_list' }));
setPreviewResults([]);
setSelectedFiles([]);
}
}
};
wsRef.current = ws;
return () => ws.close();
}, []);
// 预览重命名
const handlePreview = () => {
if (wsRef.current && selectedFiles.length > 0 && selectedStrategy) {
wsRef.current.send(JSON.stringify({
type: 'preview_rename',
file_paths: selectedFiles,
strategy: selectedStrategy,
params: renameParams
}));
}
};
// 执行重命名
const handleRename = () => {
if (wsRef.current && selectedFiles.length > 0 && selectedStrategy) {
if (confirm(`确定要重命名 ${selectedFiles.length} 个文件吗?`)) {
wsRef.current.send(JSON.stringify({
type: 'rename_files',
file_paths: selectedFiles,
strategy: selectedStrategy,
params: renameParams
}));
}
}
};
return (
<div style={{ padding: '20px' }}>
<h1>CAD文件批量重命名</h1>
{/* 策略选择 */}
<div style={{ marginBottom: '20px' }}>
<label>重命名策略: </label>
<select
value={selectedStrategy}
onChange={(e) => setSelectedStrategy(e.target.value)}
>
<option value="">请选择策略</option>
{strategies.map(s => (
<option key={s.name} value={s.name}>{s.description}</option>
))}
</select>
</div>
{/* 参数输入(根据策略动态显示) */}
{selectedStrategy === 'add_prefix' && (
<div>
<label>前缀: </label>
<input
type="text"
value={renameParams.prefix || ''}
onChange={(e) => setRenameParams({...renameParams, prefix: e.target.value})}
/>
</div>
)}
{/* 操作按钮 */}
<div style={{ marginBottom: '20px' }}>
<button onClick={handlePreview} disabled={selectedFiles.length === 0}>
预览重命名
</button>
<button onClick={handleRename} disabled={selectedFiles.length === 0} style={{ marginLeft: '10px' }}>
执行重命名
</button>
</div>
{/* 预览结果 */}
{previewResults.length > 0 && (
<div style={{ marginBottom: '20px', padding: '10px', background: '#f0f0f0' }}>
<h3>预览结果</h3>
{previewResults.map((result, idx) => (
<div key={idx}>
{result.original} → {result.new}
</div>
))}
</div>
)}
{/* 文件列表 */}
<h2>文件列表</h2>
<table border="1" cellPadding="8">
<thead>
<tr>
<th>选择</th>
<th>文件名</th>
<th>路径</th>
</tr>
</thead>
<tbody>
{files.map(file => (
<tr key={file.absolute_path}>
<td>
<input
type="checkbox"
checked={selectedFiles.includes(file.relative_path)}
onChange={(e) => {
if (e.target.checked) {
setSelectedFiles([...selectedFiles, file.relative_path]);
} else {
setSelectedFiles(selectedFiles.filter(f => f !== file.relative_path));
}
}}
/>
</td>
<td>{file.filename}</td>
<td>{file.relative_path}</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
重命名功能注意事项
-
文件名冲突处理
- 如果重命名后的文件名已存在,系统会自动添加数字后缀(如
file_1,file_2) - 最多尝试1000次,避免无限循环
- 如果重命名后的文件名已存在,系统会自动添加数字后缀(如
-
扩展名保留
- 所有重命名策略都会保留原有的文件扩展名
- 支持带版本号的扩展名(如
.prt.1,.prt.2)
-
预览功能
- 建议在执行重命名前先使用预览功能
- 预览不会修改实际文件,只返回重命名结果
-
错误处理
- 批量重命名时,单个文件失败不会影响其他文件
- 返回结果中会详细说明每个文件的成功/失败状态
-
安全性
- 只能重命名配置目录内的CAD文件
- 路径安全检查防止目录遍历攻击
配置管理功能
7. 获取文件配置
发送消息:
{
"type": "get_file_config"
}
响应消息:
{
"type": "info",
"message": "获取文件配置成功",
"data": {
"base_path": "C:\\Users\\Public\\Documents",
"file_extensions": {
"creo": [".prt", ".asm", ".drw"],
"pdms": [".rvm", ".dri"],
"revit": [".rvt", ".rfa", ".rte"]
}
},
"timestamp": "2026-02-04T14:30:00"
}
8. 设置文件基础路径
发送消息:
{
"type": "set_base_path",
"base_path": "D:\\CAD_Files"
}
响应消息(成功):
{
"type": "info",
"message": "基础路径设置成功",
"data": {
"base_path": "D:\\CAD_Files"
},
"timestamp": "2026-02-04T14:30:00"
}
响应消息(失败):
{
"type": "error",
"message": "设置基础路径失败: 路径不存在: D:\\Invalid_Path",
"timestamp": "2026-02-04T14:30:00"
}
9. 设置文件扩展名
发送消息:
{
"type": "set_file_extensions",
"file_extensions": {
"creo": [".prt", ".asm", ".drw", ".sec"],
"pdms": [".rvm", ".dri"],
"revit": [".rvt", ".rfa", ".rte"],
"autocad": [".dwg", ".dxf"]
}
}
响应消息(成功):
{
"type": "info",
"message": "文件扩展名配置已更新",
"data": {
"file_extensions": {
"creo": [".prt", ".asm", ".drw", ".sec"],
"pdms": [".rvm", ".dri"],
"revit": [".rvt", ".rfa", ".rte"],
"autocad": [".dwg", ".dxf"]
}
},
"timestamp": "2026-02-04T14:30:00"
}
响应消息(失败):
{
"type": "error",
"message": "设置文件扩展名失败: 扩展名格式错误: dwg,必须以'.'开头",
"timestamp": "2026-02-04T14:30:00"
}
配置管理功能使用示例
JavaScript示例
// 扩展之前的 CADFileManager 类
class CADFileManager {
// ... 之前的代码 ...
// 获取文件配置
getFileConfig() {
this.ws.send(JSON.stringify({
type: 'get_file_config'
}));
}
// 设置基础路径
setBasePath(basePath) {
this.ws.send(JSON.stringify({
type: 'set_base_path',
base_path: basePath
}));
}
// 设置文件扩展名
setFileExtensions(extensions) {
this.ws.send(JSON.stringify({
type: 'set_file_extensions',
file_extensions: extensions
}));
}
}
// 使用示例
async function configExample() {
const manager = new CADFileManager();
await manager.connect('user123');
// 1. 获取当前配置
manager.getFileConfig();
// 2. 修改基础路径
setTimeout(() => {
manager.setBasePath('D:\\CAD_Files');
}, 1000);
// 3. 修改文件扩展名(添加AutoCAD支持)
setTimeout(() => {
manager.setFileExtensions({
creo: ['.prt', '.asm', '.drw'],
pdms: ['.rvm', '.dri'],
revit: ['.rvt', '.rfa', '.rte'],
autocad: ['.dwg', '.dxf']
});
}, 2000);
}
React示例
function CADConfigManager() {
const [basePath, setBasePath] = useState('');
const [fileExtensions, setFileExtensions] = useState({});
const [newBasePath, setNewBasePath] = useState('');
const wsRef = useRef(null);
useEffect(() => {
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
ws.onopen = () => {
// 连接成功后获取当前配置
ws.send(JSON.stringify({ type: 'get_file_config' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'info' && message.data) {
if (message.data.base_path) {
setBasePath(message.data.base_path);
}
if (message.data.file_extensions) {
setFileExtensions(message.data.file_extensions);
}
}
};
wsRef.current = ws;
return () => ws.close();
}, []);
// 修改基础路径
const handleSetBasePath = () => {
if (wsRef.current && newBasePath) {
wsRef.current.send(JSON.stringify({
type: 'set_base_path',
base_path: newBasePath
}));
}
};
// 修改文件扩展名
const handleSetExtensions = (software, extensions) => {
const newExtensions = {
...fileExtensions,
[software]: extensions
};
if (wsRef.current) {
wsRef.current.send(JSON.stringify({
type: 'set_file_extensions',
file_extensions: newExtensions
}));
}
};
return (
<div style={{ padding: '20px' }}>
<h1>CAD配置管理器</h1>
<div style={{ marginBottom: '20px' }}>
<h2>当前基础路径</h2>
<p>{basePath}</p>
<input
type="text"
placeholder="输入新路径"
value={newBasePath}
onChange={(e) => setNewBasePath(e.target.value)}
/>
<button onClick={handleSetBasePath}>修改路径</button>
</div>
<div>
<h2>当前文件扩展名配置</h2>
<pre>{JSON.stringify(fileExtensions, null, 2)}</pre>
</div>
</div>
);
}
配置管理功能注意事项
-
路径验证
- 设置基础路径前会验证路径是否存在
- 路径必须是有效的目录,不能是文件
-
扩展名格式
- 所有扩展名必须以
.开头 - 扩展名配置必须是字典格式,值必须是数组
- 所有扩展名必须以
-
配置持久化
- 所有配置修改会立即写入
configs/software_config.yaml文件 - 配置修改后会立即生效,影响文件列表和下载功能
- 所有配置修改会立即写入
-
权限要求
- 修改配置需要对配置文件有写入权限
- 建议在管理员权限下运行
-
配置同步
- 修改配置后,建议重新获取文件列表以查看变更效果
- 多个客户端同时修改配置时,以最后一次修改为准