feat: 前端七段数码管添加数字孪生功能
This commit is contained in:
@@ -1,3 +1,6 @@
|
||||
import { ResourcePurpose } from "@/APIClient";
|
||||
import { AuthManager } from "@/utils/AuthManager";
|
||||
|
||||
// 定义 diagram.json 的类型结构
|
||||
export interface DiagramData {
|
||||
version: number;
|
||||
@@ -26,40 +29,43 @@ export interface DiagramPart {
|
||||
// 连接类型定义 - 使用元组类型表示四元素数组
|
||||
export type ConnectionArray = [string, string, number, string[]];
|
||||
|
||||
import { AuthManager } from '@/utils/AuthManager';
|
||||
|
||||
// 解析连接字符串为组件ID和引脚ID
|
||||
export function parseConnectionPin(connectionPin: string): { componentId: string; pinId: string } {
|
||||
const [componentId, pinId] = connectionPin.split(':');
|
||||
export function parseConnectionPin(connectionPin: string): {
|
||||
componentId: string;
|
||||
pinId: string;
|
||||
} {
|
||||
const [componentId, pinId] = connectionPin.split(":");
|
||||
return { componentId, pinId };
|
||||
}
|
||||
|
||||
// 将连接数组转换为适用于渲染的格式
|
||||
export function connectionArrayToWireItem(
|
||||
connection: ConnectionArray,
|
||||
index: number,
|
||||
startPos = { x: 0, y: 0 },
|
||||
endPos = { x: 0, y: 0 }
|
||||
connection: ConnectionArray,
|
||||
index: number,
|
||||
startPos = { x: 0, y: 0 },
|
||||
endPos = { x: 0, y: 0 },
|
||||
): WireItem {
|
||||
const [startPinStr, endPinStr, width, path] = connection;
|
||||
const { componentId: startComponentId, pinId: startPinId } = parseConnectionPin(startPinStr);
|
||||
const { componentId: endComponentId, pinId: endPinId } = parseConnectionPin(endPinStr);
|
||||
|
||||
const { componentId: startComponentId, pinId: startPinId } =
|
||||
parseConnectionPin(startPinStr);
|
||||
const { componentId: endComponentId, pinId: endPinId } =
|
||||
parseConnectionPin(endPinStr);
|
||||
|
||||
return {
|
||||
id: `wire-${index}`,
|
||||
startX: startPos.x,
|
||||
startY: startPos.y,
|
||||
endX: endPos.x,
|
||||
endX: endPos.x,
|
||||
endY: endPos.y,
|
||||
startComponentId,
|
||||
startPinId,
|
||||
endComponentId,
|
||||
endPinId,
|
||||
strokeWidth: width,
|
||||
color: '#4a5568', // 默认颜色
|
||||
routingMode: 'path',
|
||||
color: "#4a5568", // 默认颜色
|
||||
routingMode: "path",
|
||||
pathCommands: path,
|
||||
showLabel: false
|
||||
showLabel: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -76,7 +82,7 @@ export interface WireItem {
|
||||
endPinId?: string;
|
||||
strokeWidth: number;
|
||||
color: string;
|
||||
routingMode: 'orthogonal' | 'path';
|
||||
routingMode: "orthogonal" | "path";
|
||||
constraint?: string;
|
||||
pathCommands?: string[];
|
||||
showLabel: boolean;
|
||||
@@ -89,57 +95,63 @@ export async function loadDiagramData(examId?: string): Promise<DiagramData> {
|
||||
if (examId) {
|
||||
try {
|
||||
const resourceClient = AuthManager.createAuthenticatedResourceClient();
|
||||
|
||||
|
||||
// 获取diagram类型的资源列表
|
||||
const resources = await resourceClient.getResourceList(examId, 'canvas', 'template');
|
||||
|
||||
const resources = await resourceClient.getResourceList(
|
||||
examId,
|
||||
"canvas",
|
||||
ResourcePurpose.Template,
|
||||
);
|
||||
|
||||
if (resources && resources.length > 0) {
|
||||
// 获取第一个diagram资源
|
||||
const diagramResource = resources[0];
|
||||
|
||||
|
||||
// 使用动态API获取资源文件内容
|
||||
const response = await resourceClient.getResourceById(diagramResource.id);
|
||||
|
||||
const response = await resourceClient.getResourceById(
|
||||
diagramResource.id,
|
||||
);
|
||||
|
||||
if (response && response.data) {
|
||||
const text = await response.data.text();
|
||||
const data = JSON.parse(text);
|
||||
|
||||
|
||||
// 验证数据格式
|
||||
const validation = validateDiagramData(data);
|
||||
if (validation.isValid) {
|
||||
console.log('成功从API加载实验diagram:', examId);
|
||||
console.log("成功从API加载实验diagram:", examId);
|
||||
return data;
|
||||
} else {
|
||||
console.warn('API返回的diagram数据格式无效:', validation.errors);
|
||||
console.warn("API返回的diagram数据格式无效:", validation.errors);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log('未找到实验diagram资源,使用默认加载方式');
|
||||
console.log("未找到实验diagram资源,使用默认加载方式");
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('从API加载实验diagram失败,使用默认加载方式:', error);
|
||||
console.warn("从API加载实验diagram失败,使用默认加载方式:", error);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 如果没有examId或API加载失败,尝试从静态文件加载(不再使用本地存储)
|
||||
|
||||
|
||||
// 从静态文件加载(作为备选方案)
|
||||
const response = await fetch('/src/components/diagram.json');
|
||||
const response = await fetch("/src/components/diagram.json");
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load diagram.json: ${response.statusText}`);
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
|
||||
// 验证静态文件数据
|
||||
const validation = validateDiagramData(data);
|
||||
if (validation.isValid) {
|
||||
return data;
|
||||
} else {
|
||||
console.warn('静态diagram文件数据格式无效:', validation.errors);
|
||||
throw new Error('所有diagram数据源都无效');
|
||||
console.warn("静态diagram文件数据格式无效:", validation.errors);
|
||||
throw new Error("所有diagram数据源都无效");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading diagram data:', error);
|
||||
console.error("Error loading diagram data:", error);
|
||||
// 返回空的默认数据结构
|
||||
return createEmptyDiagram();
|
||||
}
|
||||
@@ -149,33 +161,31 @@ export async function loadDiagramData(examId?: string): Promise<DiagramData> {
|
||||
export function createEmptyDiagram(): DiagramData {
|
||||
return {
|
||||
version: 1,
|
||||
author: 'user',
|
||||
editor: 'user',
|
||||
author: "user",
|
||||
editor: "user",
|
||||
parts: [],
|
||||
connections: []
|
||||
connections: [],
|
||||
};
|
||||
}
|
||||
|
||||
// 保存图表数据(已禁用本地存储)
|
||||
export function saveDiagramData(data: DiagramData): void {
|
||||
// 本地存储功能已禁用 - 不再保存到localStorage
|
||||
console.debug('saveDiagramData called but localStorage saving is disabled');
|
||||
console.debug("saveDiagramData called but localStorage saving is disabled");
|
||||
}
|
||||
|
||||
// 更新组件位置
|
||||
export function updatePartPosition(
|
||||
data: DiagramData,
|
||||
partId: string,
|
||||
x: number,
|
||||
y: number
|
||||
data: DiagramData,
|
||||
partId: string,
|
||||
x: number,
|
||||
y: number,
|
||||
): DiagramData {
|
||||
return {
|
||||
...data,
|
||||
parts: data.parts.map(part =>
|
||||
part.id === partId
|
||||
? { ...part, x, y }
|
||||
: part
|
||||
)
|
||||
parts: data.parts.map((part) =>
|
||||
part.id === partId ? { ...part, x, y } : part,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -184,21 +194,21 @@ export function updatePartAttribute(
|
||||
data: DiagramData,
|
||||
partId: string,
|
||||
attrName: string,
|
||||
value: any
|
||||
value: any,
|
||||
): DiagramData {
|
||||
return {
|
||||
...data,
|
||||
parts: data.parts.map(part =>
|
||||
part.id === partId
|
||||
? {
|
||||
...part,
|
||||
attrs: {
|
||||
...part.attrs,
|
||||
[attrName]: value
|
||||
}
|
||||
}
|
||||
: part
|
||||
)
|
||||
parts: data.parts.map((part) =>
|
||||
part.id === partId
|
||||
? {
|
||||
...part,
|
||||
attrs: {
|
||||
...part.attrs,
|
||||
[attrName]: value,
|
||||
},
|
||||
}
|
||||
: part,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -210,72 +220,79 @@ export function addConnection(
|
||||
endComponentId: string,
|
||||
endPinId: string,
|
||||
width: number = 2,
|
||||
path: string[] = []
|
||||
path: string[] = [],
|
||||
): DiagramData {
|
||||
const newConnection: ConnectionArray = [
|
||||
`${startComponentId}:${startPinId}`,
|
||||
`${endComponentId}:${endPinId}`,
|
||||
width,
|
||||
path
|
||||
path,
|
||||
];
|
||||
|
||||
|
||||
return {
|
||||
...data,
|
||||
connections: [...data.connections, newConnection]
|
||||
connections: [...data.connections, newConnection],
|
||||
};
|
||||
}
|
||||
|
||||
// 删除连接
|
||||
export function deleteConnection(
|
||||
data: DiagramData,
|
||||
connectionIndex: number
|
||||
connectionIndex: number,
|
||||
): DiagramData {
|
||||
return {
|
||||
...data,
|
||||
connections: data.connections.filter((_, index) => index !== connectionIndex)
|
||||
connections: data.connections.filter(
|
||||
(_, index) => index !== connectionIndex,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
// 查找与组件关联的所有连接
|
||||
export function findConnectionsByPart(
|
||||
data: DiagramData,
|
||||
partId: string
|
||||
partId: string,
|
||||
): { connection: ConnectionArray; index: number }[] {
|
||||
return data.connections
|
||||
.map((connection, index) => ({ connection, index }))
|
||||
.filter(({ connection }) => {
|
||||
const [startPin, endPin] = connection;
|
||||
const startCompId = startPin.split(':')[0];
|
||||
const endCompId = endPin.split(':')[0];
|
||||
const startCompId = startPin.split(":")[0];
|
||||
const endCompId = endPin.split(":")[0];
|
||||
return startCompId === partId || endCompId === partId;
|
||||
});
|
||||
}
|
||||
|
||||
// 添加验证diagram.json文件的函数
|
||||
export function validateDiagramData(data: any): { isValid: boolean; errors: string[] } {
|
||||
export function validateDiagramData(data: any): {
|
||||
isValid: boolean;
|
||||
errors: string[];
|
||||
} {
|
||||
const errors: string[] = [];
|
||||
|
||||
|
||||
// 检查版本号
|
||||
if (!data.version) {
|
||||
errors.push('缺少version字段');
|
||||
errors.push("缺少version字段");
|
||||
}
|
||||
|
||||
|
||||
// 检查parts数组
|
||||
if (!Array.isArray(data.parts)) {
|
||||
errors.push('parts字段不是数组');
|
||||
errors.push("parts字段不是数组");
|
||||
} else {
|
||||
// 验证parts中的每个对象
|
||||
data.parts.forEach((part: any, index: number) => {
|
||||
if (!part.id) errors.push(`parts[${index}]缺少id`);
|
||||
if (!part.type) errors.push(`parts[${index}]缺少type`);
|
||||
if (typeof part.x !== 'number') errors.push(`parts[${index}]缺少有效的x坐标`);
|
||||
if (typeof part.y !== 'number') errors.push(`parts[${index}]缺少有效的y坐标`);
|
||||
if (typeof part.x !== "number")
|
||||
errors.push(`parts[${index}]缺少有效的x坐标`);
|
||||
if (typeof part.y !== "number")
|
||||
errors.push(`parts[${index}]缺少有效的y坐标`);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 检查connections数组
|
||||
if (!Array.isArray(data.connections)) {
|
||||
errors.push('connections字段不是数组');
|
||||
errors.push("connections字段不是数组");
|
||||
} else {
|
||||
// 验证connections中的每个数组
|
||||
data.connections.forEach((conn: any, index: number) => {
|
||||
@@ -283,25 +300,25 @@ export function validateDiagramData(data: any): { isValid: boolean; errors: stri
|
||||
errors.push(`connections[${index}]不是有效的连接数组`);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const [startPin, endPin, width] = conn;
|
||||
|
||||
if (typeof startPin !== 'string' || !startPin.includes(':')) {
|
||||
|
||||
if (typeof startPin !== "string" || !startPin.includes(":")) {
|
||||
errors.push(`connections[${index}]的起始针脚格式无效`);
|
||||
}
|
||||
|
||||
if (typeof endPin !== 'string' || !endPin.includes(':')) {
|
||||
|
||||
if (typeof endPin !== "string" || !endPin.includes(":")) {
|
||||
errors.push(`connections[${index}]的结束针脚格式无效`);
|
||||
}
|
||||
|
||||
if (typeof width !== 'number') {
|
||||
|
||||
if (typeof width !== "number") {
|
||||
errors.push(`connections[${index}]的宽度不是有效的数字`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
isValid: errors.length === 0,
|
||||
errors
|
||||
errors,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user