支持从主程序导入场景模型
This commit is contained in:
@@ -22,11 +22,31 @@ type UploadModelPayload = {
|
||||
};
|
||||
|
||||
const defaultOperationTree = JSON.stringify({ OperationTree: "[]" });
|
||||
const requestSceneModelMessageType = "DMT_MODEL_LIBRARY_REQUEST_SCENE_MODEL";
|
||||
const sceneModelMessageType = "DMT_MODEL_LIBRARY_SCENE_MODEL";
|
||||
const sceneModelErrorMessageType = "DMT_MODEL_LIBRARY_SCENE_MODEL_ERROR";
|
||||
|
||||
type SceneModelMessagePayload = {
|
||||
modelName?: string;
|
||||
fileName?: string;
|
||||
modelBuffer?: ArrayBuffer;
|
||||
operationTreeJson?: string;
|
||||
};
|
||||
|
||||
type SceneModelMessage = {
|
||||
type?: string;
|
||||
payload?: SceneModelMessagePayload;
|
||||
message?: string;
|
||||
};
|
||||
|
||||
let dictionariesLoaded = false;
|
||||
let dictionariesLoading: Promise<void> | null = null;
|
||||
let sceneModelBridgeBound = false;
|
||||
|
||||
export function bindModelActions() {
|
||||
const isAdmin = getCurrentUser()?.role === "admin";
|
||||
bindSceneModelBridge();
|
||||
ensureImportSceneModelButton();
|
||||
if (isAdmin) {
|
||||
document.querySelector("#manageUsersBtn")?.addEventListener("click", async () => {
|
||||
try {
|
||||
@@ -242,8 +262,90 @@ function syncSelectedFolderActions() {
|
||||
const canWrite = Boolean(currentFolder?.permissions.write);
|
||||
const addModelBtn = document.querySelector<HTMLButtonElement>("#addModelBtn");
|
||||
const addProcessModelBtn = document.querySelector<HTMLButtonElement>("#addProcessModelBtn");
|
||||
const importSceneModelBtn = document.querySelector<HTMLButtonElement>("#importSceneModelBtn");
|
||||
if (addModelBtn) addModelBtn.hidden = !canWrite;
|
||||
if (addProcessModelBtn) addProcessModelBtn.hidden = !canWrite;
|
||||
if (importSceneModelBtn) importSceneModelBtn.hidden = !canWrite;
|
||||
}
|
||||
|
||||
function ensureImportSceneModelButton() {
|
||||
if (document.querySelector("#importSceneModelBtn")) return;
|
||||
const addModelBtn = document.querySelector<HTMLButtonElement>("#addModelBtn");
|
||||
if (!addModelBtn) return;
|
||||
const button = document.createElement("button");
|
||||
button.id = "importSceneModelBtn";
|
||||
button.type = "button";
|
||||
button.textContent = "导入场景模型";
|
||||
button.addEventListener("click", requestSceneModelImport);
|
||||
addModelBtn.insertAdjacentElement("afterend", button);
|
||||
}
|
||||
|
||||
function bindSceneModelBridge() {
|
||||
if (sceneModelBridgeBound) return;
|
||||
sceneModelBridgeBound = true;
|
||||
window.addEventListener("message", (event) => {
|
||||
const data = event.data as SceneModelMessage;
|
||||
if (data?.type !== sceneModelMessageType && data?.type !== sceneModelErrorMessageType) return;
|
||||
if (!isAllowedParentOrigin(event.origin)) return;
|
||||
void handleSceneModelMessage(data);
|
||||
});
|
||||
}
|
||||
|
||||
function requestSceneModelImport() {
|
||||
if (!appState.selectedFolderId) {
|
||||
notify("请先选择目录");
|
||||
return;
|
||||
}
|
||||
if (!window.parent || window.parent === window) {
|
||||
notify("请在 DMT 主程序中使用导入场景模型");
|
||||
return;
|
||||
}
|
||||
window.parent.postMessage({
|
||||
type: requestSceneModelMessageType
|
||||
}, resolveParentOrigin());
|
||||
notify("已请求主程序导出当前选中模型");
|
||||
}
|
||||
|
||||
async function handleSceneModelMessage(data: SceneModelMessage) {
|
||||
if (data.type === sceneModelErrorMessageType) {
|
||||
notifyError(data.message || "主程序导出模型失败");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = data.payload;
|
||||
if (!payload?.modelBuffer) {
|
||||
notifyError("主程序返回的模型数据为空");
|
||||
return;
|
||||
}
|
||||
|
||||
const fileName = ensureGlbFileName(payload.fileName || payload.modelName || "scene-model.glb");
|
||||
const file = new File([payload.modelBuffer], fileName, { type: "model/gltf-binary" });
|
||||
await openProcessModelUploadDialog({
|
||||
file,
|
||||
modelName: payload.modelName || modelNameFromFile(fileName),
|
||||
operationTreeJson: payload.operationTreeJson || defaultOperationTree
|
||||
});
|
||||
}
|
||||
|
||||
function isAllowedParentOrigin(origin: string) {
|
||||
const expectedOrigin = resolveParentOrigin();
|
||||
return expectedOrigin === "*" || origin === expectedOrigin;
|
||||
}
|
||||
|
||||
function resolveParentOrigin() {
|
||||
try {
|
||||
const meta = import.meta as ImportMeta & { env?: Record<string, string | undefined> };
|
||||
const configuredOrigin = meta.env?.VITE_DMT_PARENT_ORIGIN;
|
||||
if (configuredOrigin) return configuredOrigin;
|
||||
if (document.referrer) return new URL(document.referrer).origin;
|
||||
} catch {
|
||||
return "*";
|
||||
}
|
||||
return "*";
|
||||
}
|
||||
|
||||
function ensureGlbFileName(fileName: string) {
|
||||
return fileName.toLowerCase().endsWith(".glb") ? fileName : `${fileName}.glb`;
|
||||
}
|
||||
|
||||
export async function uploadModelToBackend(payload: UploadModelPayload) {
|
||||
|
||||
Reference in New Issue
Block a user