1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
| <template> <div class="fixed inset-0 z-50 flex items-center justify-center bg-black/70" @click.self="closeViewer" > <div class="relative w-[90vw] h-[90vh] bg-white rounded-lg shadow-2xl" > <div class="flex flex-col w-full h-full"> <!-- 关闭按钮 --> <button @click="closeViewer" class="absolute top-2 right-4 z-10 w-8 h-8 flex items-center justify-center bg-white/90 hover:bg-white rounded-full shadow-lg transition-colors" > <span class="text-gray-600 text-xl font-bold">×</span> </button> <div class="p-3 bg-gray-100 border-b border-gray-300 flex items-center gap-3" > <span v-if="isLoadingLib" class="text-sm text-blue-600" >正在加载 CAD 引擎...</span > <span v-else-if="libLoadError" class="text-sm text-red-600">{{ libLoadError }}</span> <span v-else-if="fileName" class="text-sm text-gray-700" >当前文件: {{ fileName }}</span > </div>
<div class="flex flex-1 min-h-0"> <div class="w-64 shrink-0 bg-white border-r border-gray-300 p-3 flex flex-col overflow-hidden gap-1" > <h3 class="text-base font-semibold">图层管理</h3> <!-- 图层操作区域:包含全选等 --> <div class="shrink-0"> <ElButton size="small" @click="setAllLayersVisibility(true)"> 全部显示 </ElButton> <ElButton size="small" @click="setAllLayersVisibility(false)"> 全部隐藏 </ElButton> </div> <ElScrollbar class="flex-1 min-h-0" > <div v-if="layers.length === 0" class="text-gray-500 text-sm shrink-0">暂无图层信息</div> <li v-else v-for="layer in layers" :key="layer.name" class="flex items-center py-1.5 gap-2" > <label class="flex items-center gap-2 flex-1 min-w-0 cursor-pointer" > <input type="checkbox" :checked="layer.visible" @change="toggleLayer(layer)" class="cursor-pointer shrink-0" /> <span class="select-none hover:text-blue-500 transition-colors truncate" @click="selectLayerName(layer.name)" :title="layer.name" > {{ layer.name }} </span> </label> <span class="w-3 h-3 shrink-0 rounded-sm border border-gray-400" :style="{ backgroundColor: getLayerColor(layer.color) }" ></span> </li> </ElScrollbar> <ElButton type="primary" @click="handleConfirm">确认</ElButton> </div>
<!-- CAD显示区域 --> <div class="flex-1 overflow-hidden" @mousedown.middle.stop.prevent @mouseup.middle.stop.prevent @auxclick.stop.prevent @contextmenu.middle.stop.prevent > <canvas ref="canvasRef" class="w-full h-full relative bg-gray-800" ></canvas> </div> </div> </div> </div> </div> </template>
<script setup lang="ts"> import { ref, onMounted, watch, nextTick } from "vue"; import type { AcDbOpenDatabaseOptions, AcDbObjectId, } from "@mlightcad/data-model"; import { ElButton, ElScrollbar } from "element-plus"; import { AcApDocManager } from "@mlightcad/cad-simple-viewer";
// 定义 Props 和 Emits interface Props { file?: File | null; }
const props = defineProps<Props>();
const emit = defineEmits<{ (e: "layersUpdated", layerList: typeof layers.value): void; (e: "layer-selected", layerName: string[]): void; (e: "close"): void; }>();
// 状态定义 const canvasRef = ref<HTMLCanvasElement | null>(null); const fileName = ref<string>(""); const isViewerReady = ref(false); // 新增:标记 Viewer 是否已准备好 const isLoadingLib = ref(false); // CAD 库加载状态 const libLoadError = ref<string | null>(null); // 库加载错误信息 // 图层信息 const layers = ref< Array<{ name: string; visible: boolean; id: AcDbObjectId; color: number }> >([]);
const closeViewer = () => { emit("close"); };
// 初始化 Viewer onMounted(async () => { await nextTick();
if (!canvasRef.value) { console.error("Canvas element not found during mount"); return; } // 保存原始标题 const originalTitle = document.title;
try { isLoadingLib.value = true;
// 初始化 DocManager,绑定 Canvas AcApDocManager.createInstance({ canvas: canvasRef.value }); console.log("CAD viewer initialized successfully");
// 标记初始化完成 isViewerReady.value = true;
document.title = originalTitle;
// 如果 Props 中已有文件,初始化完成后立即加载 if (props.file) { loadDwgFile(props.file); } isLoadingLib.value = false; } catch (error) { console.error("Failed to initialize CAD viewer:", error); libLoadError.value = "初始化 CAD 查看器失败"; } });
// 加载DWG文件 const loadDwgFile = async (file: File) => { // 双重保险:如果 Viewer 没准备好,绝对不执行 if (!isViewerReady.value || !AcApDocManager) { console.warn("Viewer is not ready yet."); return; } // 保存原始标题 const originalTitle = document.title;
fileName.value = file.name;
try { const arrayBuffer = await file.arrayBuffer();
const options: AcDbOpenDatabaseOptions = { minimumChunkSize: 1000, readOnly: false, };
// 此时访问 instance 是安全的 const success = await AcApDocManager.instance.openDocument( file.name, arrayBuffer, options );
if (success) { console.log("DWG 文件加载成功"); fetchLayers(); } else { console.error("DWG 文件加载失败"); } } catch (error) { console.error("读取文件出错:", error); } finally { // 恢复原始标题 document.title = originalTitle; } };
/** * 获取图层信息 */ const fetchLayers = () => { if (!AcApDocManager) return; const doc = AcApDocManager.instance.curDocument; if (!doc) return;
const db = doc.database; const layerTable = db.tables.layerTable; const layerList: typeof layers.value = [];
const layerIterator = layerTable.newIterator(); for (const layer of layerIterator) { layerList.push({ id: layer.objectId, name: layer.name, visible: !layer.isOff, color: layer.color.colorIndex || 7, }); }
layers.value = layerList; emit("layersUpdated", layerList); };
// 监听file prop变化 watch( () => props.file, (newFile) => { // 只有当有新文件 且 Viewer 已经准备好时才加载 if (newFile && isViewerReady.value) { loadDwgFile(newFile); } } );
// 切换图层可见性 const toggleLayer = (layerItem: (typeof layers.value)[0]) => { if (!AcApDocManager) return; const doc = AcApDocManager.instance.curDocument; if (!doc) return;
const db = doc.database; const layer = db.tables.layerTable.getAt(layerItem.name);
if (layer) { layer.isOff = layerItem.visible; layerItem.visible = !layerItem.visible; // 更新本地响应式状态 } };
// 全部显示或全部隐藏图层 const setAllLayersVisibility = (visible: boolean) => { if (!AcApDocManager) return; const doc = AcApDocManager.instance.curDocument; if (!doc) return;
layers.value.forEach((layer) => { layer.visible = visible; const dbLayer = doc.database.tables.layerTable.getAt(layer.name); if (dbLayer) { dbLayer.isOff = !visible; } }); };
// 发送图层名称 const selectLayerName = (name: string) => { console.log("Selected Layer:", name); };
// 将可见图层名字列表作为字符串返回 const handleConfirm = () => { const visibleLayerNames = layers.value .filter((layer) => layer.visible) .map((layer) => layer.name) console.log("Visible Layers:", visibleLayerNames); emit("layer-selected", visibleLayerNames); closeViewer(); };
// 辅助函数:将 AutoCAD 颜色索引转换为 CSS 颜色 const getLayerColor = (colorIndex: number) => { const colors: Record<number, string> = { 1: "red", 2: "yellow", 3: "green", 4: "cyan", 5: "blue", 6: "magenta", 7: "white", }; return colors[colorIndex] || "#ccc"; }; </script>
|