Jelajahi Sumber

[Mod 0000] 修改、调试CAD viewer

houzekong 11 bulan lalu
induk
melakukan
8ceaa0a330

+ 0 - 2
package.json

@@ -60,8 +60,6 @@
     "mky-svg": "^1.0.2",
     "mockjs": "^1.1.0",
     "moment": "^2.29.4",
-    "mxcad": "^1.0.195",
-    "mxdraw": "^0.1.263",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.2.1",
     "pinia": "2.1.6",

+ 1 - 55
pnpm-lock.yaml

@@ -122,12 +122,6 @@ dependencies:
   moment:
     specifier: ^2.29.4
     version: 2.30.1
-  mxcad:
-    specifier: ^1.0.195
-    version: 1.0.195(three@0.162.0)
-  mxdraw:
-    specifier: ^0.1.263
-    version: 0.1.263(three@0.162.0)
   nprogress:
     specifier: ^0.2.0
     version: 0.2.0
@@ -174,7 +168,7 @@ dependencies:
     specifier: ^3.9.5
     version: 3.9.6
   vue:
-    specifier: ^3.3.4
+    specifier: ^3.2.0
     version: 3.3.6(typescript@4.9.5)
   vue-cropper:
     specifier: ^0.6.2
@@ -2282,12 +2276,6 @@ packages:
     engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
     dev: true
 
-  /@fingerprintjs/fingerprintjs@4.2.2:
-    resolution: {integrity: sha512-scD+pDgNZW78LuFAr7ms2yxmDx2NWC4+K5iiOjPT2ZlTlHFbLsORUzLJI2rcKicxxLtHbvf3A7BU1drVr4iHGg==}
-    dependencies:
-      tslib: 2.6.2
-    dev: false
-
   /@fullcalendar/common@5.10.1:
     resolution: {integrity: sha512-EumKIJcQTvQdTs75/9dmeREFgjcRVWzqHJS1Xvlz5mNsmB+w9EINCHETRjChtAQg1WD/lTQyVj4sHsKO7vCMSw==, tarball: http://registry.npm.taobao.org/@fullcalendar/common/-/common-5.10.1.tgz}
     dependencies:
@@ -6827,12 +6815,6 @@ packages:
       batch-processor: 1.0.0
     dev: false
 
-  /element-resize-event-polyfill@1.0.5:
-    resolution: {integrity: sha512-HWjhWQIi8Ilw8YWyX5O4b7L2AIQE9SMP+LFoHrr6b1ZLIUIpqybA5eU3v2uWAnE0ptCxyLTT92IgLTehWldIfQ==}
-    dependencies:
-      resize-observer-polyfill: 1.5.1
-    dev: false
-
   /emittery@0.13.1:
     resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==, tarball: http://registry.npm.taobao.org/emittery/-/emittery-0.13.1.tgz}
     engines: {node: '>=12'}
@@ -9924,10 +9906,6 @@ packages:
     hasBin: true
     dev: true
 
-  /jquery@3.7.1:
-    resolution: {integrity: sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==}
-    dev: false
-
   /js-base64@2.6.4:
     resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==, tarball: http://registry.npm.taobao.org/js-base64/-/js-base64-2.6.4.tgz}
     dev: true
@@ -10735,30 +10713,6 @@ packages:
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
     dev: true
 
-  /mxcad@1.0.195(three@0.162.0):
-    resolution: {integrity: sha512-bMQPRMt4jIHvHK4ixWUflHdZYri8u4zIYihA2UDyraLA1IYUeExgRNDtOocOsE/NfuH39L+xSDUtnpEIiS3zHQ==}
-    dependencies:
-      '@fingerprintjs/fingerprintjs': 4.2.2
-      iconv-lite: 0.6.3
-      mxdraw: 0.1.263(three@0.162.0)
-      threebox-plugin: 2.2.7
-    transitivePeerDependencies:
-      - three
-    dev: false
-
-  /mxdraw@0.1.263(three@0.162.0):
-    resolution: {integrity: sha512-pE25TfSc4y3dOCbpyPFHkfFz+UyZHV59yEqMZzYlPkYCUrpHjvbeeWz6A8j3rxzifLMMX2maPT8ocyzlZwuqHA==}
-    engines: {node: '>=6.0.0'}
-    peerDependencies:
-      three: ^0.113.2
-    dependencies:
-      element-resize-event-polyfill: 1.0.5
-      jquery: 3.7.1
-      lodash: 4.17.21
-      three: 0.162.0
-      three-gif-texture: 1.0.15
-    dev: false
-
   /nanoid@3.3.6:
     resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
     engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
@@ -12853,18 +12807,10 @@ packages:
     resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, tarball: http://registry.npm.taobao.org/text-table/-/text-table-0.2.0.tgz}
     dev: true
 
-  /three-gif-texture@1.0.15:
-    resolution: {integrity: sha512-GzTF6OtgdjqKOF+AtA0WgLguJ8/iqE5u9KQQadmgWLusM2ML82XvQJuoGD6MqQalC33prUeaf2zqTWCP8fR4rw==}
-    dev: false
-
   /three@0.162.0:
     resolution: {integrity: sha512-xfCYj4RnlozReCmUd+XQzj6/5OjDNHBy5nT6rVwrOKGENAvpXe2z1jL+DZYaMu4/9pNsjH/4Os/VvS9IrH7IOQ==}
     dev: false
 
-  /threebox-plugin@2.2.7:
-    resolution: {integrity: sha512-H87Nm4w1PfisHPHzavTGXlwIoJpx2+QU57GooQYIhF51lsg+U5A0KGf3Jrv/HWsLCGOwV2BTnv7UTLfpO1EccQ==}
-    dev: false
-
   /throat@6.0.2:
     resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==, tarball: http://registry.npm.taobao.org/throat/-/throat-6.0.2.tgz}
     dev: false

+ 2 - 1
public/mxcad/mxServerConfig.json

@@ -1,4 +1,5 @@
 {
+  "?serverUrlBackup": "http://182.92.126.35:1337",
   "?uploadFileConfig": "上传文件相关配置",
   "uploadFileConfig": {
     "?baseUrl": "上传服务地址",
@@ -23,7 +24,7 @@
 	"?saveUrl": "保存文件服务地址",
 	"saveUrl": "http://localhost:1337/mxcad/savemxweb",
 	"?saveDwgUrl": "保存DWG文件服务地址",
-	"saveDwgUrl": "http://localhost:1337/mxcad/savedwg",
+	"saveDwgUrl": "http://182.92.126.35:8092/modelreq/safety/cadFile/transformCadFileType",
 	"?printPdfUrl": "把指定范围输出到pdf的服务地址",
 	"printPdfUrl": "http://localhost:1337/mxcad/print_to_pdf"
   },

File diff ditekan karena terlalu besar
+ 0 - 2
public/mxcad/plugins/test.js


File diff ditekan karena terlalu besar
+ 0 - 0
public/mxcad/plugins/test.js.map


+ 1 - 3
src/App.vue

@@ -2,8 +2,7 @@
   <AdaptiveContainer :options="{ width: width, height: height }" style="overflow-y: hidden">
     <ConfigProvider :locale="getAntdLocale" prefixCls="zxm">
       <AppProvider>
-        <!-- <RouterView v-if="!isReload" /> -->
-        <CADViewer show-operations class="w-1000px h-900px" />
+        <RouterView v-if="!isReload" />
       </AppProvider>
     </ConfigProvider>
   </AdaptiveContainer>
@@ -17,7 +16,6 @@
   import { useLocale } from '/@/locales/useLocale';
   import AdaptiveContainer from '/@/components/Container/src/Adaptive.vue';
   import { useAppStore } from '/@/store/modules/app';
-  import { CADViewer } from '/@/components/CADViewer/index';
 
   // 解决日期时间国际化问题
   import 'dayjs/locale/zh-cn';

+ 82 - 0
src/components/CADViewer/README.md

@@ -0,0 +1,82 @@
+## CAD Viewer 组件说明
+
+该组件旨在提供一个Viewer以预览、下载、编辑、打开DWG等通用格式的CAD文件。
+
+### 快速开始
+
+```vue
+<template>
+  <CADViewer style="width: 1920px" :height="1080" />
+</template>
+
+<script lang="ts" setup>
+  import { CADViewer } from '/@/components/CADViewer';
+</script>
+`
+```
+
+### Prop解读
+
+详见`/@/components/CADViewer/src/CADViewer.vue`
+
+### 实现原理
+
+通过iframe嵌入即成的主体应用,编写客制化的操作栏并下发指令对其进行操作。可以通过[前期文档](https://mxcadx.gitee.io/mxcad_docs/zh/7.%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/1.MxCAD%20APP%E5%BA%94%E7%94%A8%E9%9B%86%E6%88%90.html)了解更多。
+
+有了主体应用(mxcad app)后,使用 postMessage 技术与其通信以下达指令即可完成操作。需要额外说明的,预设的某些指令可能不足以满足需求,此时需要在 mxcad app 中修改源码以添加客制化指令。本组件中的 MKY_Download_DWG、MKY_Open_DWG 即是客制化指令,具体细节参考[实现细节](#关于操作配置及其实现细节)。
+
+对于操作栏,使用时首先应该向`/@/components/CADViewer/src/operationMap.ts`里注册操作配置。具体说明见`/@/components/CADViewer/src/types`和`/@/components/CADViewer/src/operationMap.ts`
+
+### 关于操作配置及其实现细节
+
+阅读完[实现原理](#实现原理)里操作相关的内容后,我们了解了如何注册操作配置,但对于如何完整的实现还不清楚,那么它的细节如下:
+
+1. 所支持的mxcad app指令在`/public/mxcad/mxQuickCommand.json`中可以查阅。
+2. 如果没有发现可用的指令,需要自行在 mxcad app 的源码中实现指令。
+3. 实现指令后,使用`MxFun.addCommand`添加指令到 mxcad app 中。
+4. 配合着该指令的实现,在对应操作里实现业务。
+
+这里以 MKY_Open_DWG 举例,这个指令不在 mxcad app 的默认指令集中,我们需要手动添加它,步骤如下:
+
+1. 在 mxcad app 的源码中,找到添加 cadfile 相关指令的文件 `mxcad_app/2d/MxCAD/src/cadfile.ts`。
+2. 添加指令,该指令将打开一个 .mxweb 文件,需要提供文件的网络路径作为参数,代码就像这样:
+
+```ts
+/** 打开指定的文件 */
+export async function MKY_Open_Mxweb({ param }: { param: string }) {
+  MxCpp.getCurrentMxCAD().openWebFile(param);
+}
+```
+
+3. 为 mxcad app 注册该指令:
+
+```ts
+MxFun.addCommand('MKY_Open_Mxweb', MKY_Open_Mxweb);
+```
+
+4. 在项目中更新 mxcad app,即将其打包后把新的文件`mxcad_app/2d/dist/plugins/test.js`、`mxcad_app/2d/dist/plugins/test.js.map`替换到项目的`public/mxcad/plugins`中。
+
+5. 注册操作配置,使用该指令:
+
+```ts
+operationMap.set('MKY_Open_Mxweb', {
+  name: 'MKY_Open_Mxweb',
+  alias: '打开DWG文件',
+  component: OpenFile,
+  on: {
+    select: (file: File) => {
+      processFile(file).then((path) => {
+        postMessage('MKY_Open_Mxweb', path);
+      });
+    },
+  },
+});
+```
+
+### 其他
+
+上文中提到的 mxcad app 在[前期文档](https://mxcadx.gitee.io/mxcad_docs/zh/7.%E6%9C%80%E4%BD%B3%E5%AE%9E%E8%B7%B5/1.MxCAD%20APP%E5%BA%94%E7%94%A8%E9%9B%86%E6%88%90.html)中下载(不会读到这还没看这个文档吧?)。
+
+关于客制化指令的实现未在git上同步。需要添加了客制化指令的项目源码的可以联系@houzekong。
+
+全文到此结束。

+ 28 - 14
src/components/CADViewer/src/CADViewer.vue

@@ -1,21 +1,17 @@
 <template>
   <div>
     <OperationBar v-if="showOperations" :allowed-operations="allowedOperations" :denied-operations="deniedOperations" />
-    <iframe
-      v-if="iframeMode"
-      :src="iframeSrc"
-      :id="CAD_VIEWER_IFRAME_ID"
-      class="w-100% cad-viewer__viewer"
-      :style="{ height: `${height}px` }"
-    ></iframe>
-    <BasicViewer v-else class="w-100% cad-viewer__viewer" :style="{ height: `${height}px` }" />
+    <iframe :src="iframeSrc" :id="CAD_VIEWER_IFRAME_ID" class="w-100% cad-viewer__viewer" :style="{ height: `${height}px` }"></iframe>
+    <!-- <BasicViewer v-else class="w-100% cad-viewer__viewer" :style="{ height: `${height}px` }" /> -->
   </div>
 </template>
 <script setup lang="ts">
   import OperationBar from './components/OperationBar.vue';
-  import { SupportedOperationName } from './types';
-  import BasicViewer from './components/BasicViewer.vue';
-  import { CAD_VIEWER_IFRAME_ID } from './const';
+  import { SupportedOperationName, SupportedOperation } from './types';
+  // import BasicViewer from './components/BasicViewer.vue';
+  import { CAD_VIEWER_IFRAME_ID } from './viewer.data';
+  import { onMounted, onUnmounted } from 'vue';
+  import { operationMap } from './operationMap';
   // import url from '/mxcad/index.html?url';
 
   withDefaults(
@@ -23,8 +19,8 @@
       /** CAD viewer 的高度,注意不是整个组件的高度 */
       height?: number;
       showOperations?: boolean;
-      /** 是否使用 iframe 代替客制化 CAD viewer */
-      iframeMode?: boolean;
+      // /** 是否使用 iframe 代替客制化 CAD viewer */
+      // iframeMode?: boolean;
       /** CAD viewer 嵌入的 iframe src 描述 */
       iframeSrc?: string;
       /** 设置允许展示的操作按钮,优先级高于 deniedOperations */
@@ -34,11 +30,29 @@
     }>(),
     {
       height: 980,
-      iframeMode: true,
+      // iframeMode: true,
       showOperations: false,
       iframeSrc: '/mxcad/index.html',
     }
   );
+
+  const onMessage = (message: MessageEvent) => {
+    try {
+      if (!message.isTrusted || !operationMap.has(message.data.cmd)) return;
+      const op = operationMap.get(message.data.cmd) as SupportedOperation;
+      if (!op.messageHandler) return;
+      op.messageHandler(message.data);
+    } catch (e) {}
+  };
+
+  onMounted(() => {
+    window.addEventListener('message', onMessage);
+  });
+
+  onUnmounted(() => {
+    window.removeEventListener('message', onMessage);
+  });
 </script>
 
 <style scoped></style>
+./viewer.data

+ 16 - 15
src/components/CADViewer/src/components/BasicViewer.vue

@@ -1,26 +1,27 @@
 <template>
+  <!-- !!!!!!!!!!!!!!!!!!!!已弃用 -->
   <!-- Viewer的宽/高等其他样式由使用者定义 -->
   <div class="overflow-hidden">
     <canvas id="cad_base_viewer"></canvas>
   </div>
 </template>
 <script setup lang="ts">
-  import { onMounted } from 'vue';
-  import { createMxCad } from 'mxcad';
+  // import { onMounted } from 'vue';
+  // import { createMxCad } from 'mxcad';
 
-  onMounted(() => {
-    // SharedArrayBuffer可以提供多个Worker共享一个内存块的功能,以增强性能
-    const mode = 'SharedArrayBuffer' in window ? '2d' : '2d-st';
-    createMxCad({
-      canvas: '#cad_base_viewer',
-      locateFile: (fileName) => {
-        console.log('debug fileName', fileName);
-        return new URL(`../../../../../node_modules/mxcad/dist/wasm/${mode}/${fileName}`, import.meta.url).href;
-      },
-      fontspath: new URL('../../../../../node_modules/mxcad/dist/fonts', import.meta.url).href,
-      fileUrl: new URL('/model/mxweb/demo.mxweb', import.meta.url).href,
-    });
-  });
+  // onMounted(() => {
+  //   // SharedArrayBuffer可以提供多个Worker共享一个内存块的功能,以增强性能
+  //   const mode = 'SharedArrayBuffer' in window ? '2d' : '2d-st';
+  //   createMxCad({
+  //     canvas: '#cad_base_viewer',
+  //     locateFile: (fileName) => {
+  //       console.log('debug fileName', fileName);
+  //       return new URL(`../../../../../node_modules/mxcad/dist/wasm/${mode}/${fileName}`, import.meta.url).href;
+  //     },
+  //     fontspath: new URL('../../../../../node_modules/mxcad/dist/fonts', import.meta.url).href,
+  //     fileUrl: new URL('/model/mxweb/demo.mxweb', import.meta.url).href,
+  //   });
+  // });
 </script>
 
 <style scoped></style>

+ 4 - 4
src/components/CADViewer/src/components/OpenFile.vue

@@ -7,19 +7,19 @@
     :show-upload-list="false"
     :custom-request="customRequest"
   >
-    <Button> 选择文件 </Button>
+    <Button>打开DWG文件</Button>
   </Upload>
 </template>
 <script lang="ts" setup>
   import { Upload, Button, message } from 'ant-design-vue';
   import { ref } from 'vue';
-  import { MxCpp } from 'mxcad';
   import type { UploadProps } from 'ant-design-vue';
 
+  const emit = defineEmits<{ (event: 'select', file: Blob): void }>();
+
   const customRequest: UploadProps['customRequest'] = ({ file }) => {
     if (typeof file === 'string') return message.warn('不支持所选择的文件', 2000);
-    const filePath = URL.createObjectURL(file);
-    MxCpp.getCurrentMxCAD().openWebFile(filePath);
+    emit('select', file);
   };
 
   const fileList = ref<UploadProps['fileList']>([]);

+ 44 - 15
src/components/CADViewer/src/operationMap.ts

@@ -1,35 +1,45 @@
 import type { SupportedOperation, SupportedOperationName } from './types';
-// import { MxCpp } from 'mxcad';
-// import OpenFile from './components/OpenFile.vue';
-import { CAD_VIEWER_IFRAME_ID } from './const';
+import OpenFile from './components/OpenFile.vue';
+import { CAD_VIEWER_IFRAME_ID } from './viewer.data';
 import { message } from 'ant-design-vue';
+import { downloadByUrl } from '/@/utils/file/download';
+import { transformCadFile } from './viewer.api';
+
+// MxFun.sendStringToExecute('')
 
 /** CAD Viewer 所支持的全部操作映射表 */
 const operationMap = new Map<SupportedOperationName, SupportedOperation>();
 
 // 打开 dwg 格式的 CAD 文件
-operationMap.set('open-file', {
-  name: 'open-file',
-  alias: '打开文件',
-  component: 'a-button',
+operationMap.set('MKY_Open_Mxweb', {
+  name: 'MKY_Open_Mxweb',
+  alias: '打开DWG文件',
+  component: OpenFile,
   on: {
-    click() {
-      postMessage('OpenDwg');
+    select: (file: File) => {
+      processFile(file).then((path) => {
+        postMessage('MKY_Open_Mxweb', path);
+      });
     },
   },
-  // component: OpenFile,
 });
 
-operationMap.set('download-file', {
-  name: 'download-file',
-  alias: '导出文件',
+// 下载 dwg 格式的 CAD 文件
+operationMap.set('MKY_Download_Mxweb', {
+  name: 'MKY_Download_Mxweb',
+  alias: '下载DWG文件',
   component: 'a-button',
   on: {
     click() {
-      // MxCpp.getCurrentMxCAD().saveFile();
-      postMessage('Mx_Export_DWG');
+      postMessage('MKY_Download_Mxweb');
     },
   },
+  messageHandler(context: { file: Uint8Array }) {
+    const file = new File([new Blob([context.file])], 'file.mxweb');
+    processFile(file).then((url) => {
+      downloadByUrl({ url });
+    });
+  },
 });
 
 export function add(opName: SupportedOperationName, operation: SupportedOperation) {
@@ -53,4 +63,23 @@ function postMessage(cmd: string, param?: unknown) {
   );
 }
 
+/** 调用 api 转换文件格式,并返回转换后文件的网络地址 */
+function processFile(file: Blob) {
+  const data = new FormData();
+  data.append('file', file);
+  const close = message.loading('正在转换文件格式,请稍等', 0);
+  return transformCadFile(data)
+    .then((result) => {
+      const filepath = result.replace('/data/file/', '');
+      if (import.meta.env.PROD) {
+        return window.location.origin + `/sys/common/static/${filepath}`.replace(/\/+/g, '/');
+      } else {
+        return import.meta.env.VITE_GLOB_DOMAIN_URL + `/sys/common/static/${filepath}`.replace(/\/+/g, '/');
+      }
+    })
+    .finally(() => {
+      close();
+    });
+}
+
 export { operationMap };

+ 4 - 1
src/components/CADViewer/src/types/index.ts

@@ -1,6 +1,7 @@
 import { Component } from 'vue';
 
-export type SupportedOperationName = 'open-file' | 'download-file';
+/** 支持的操作名称,即mxcad app支持的命令 */
+export type SupportedOperationName = 'MKY_Open_Mxweb' | 'MKY_Download_Mxweb';
 export type SupportedOperation = {
   name: SupportedOperationName;
   alias: string;
@@ -10,4 +11,6 @@ export type SupportedOperation = {
   prop?: any;
   /** 操作项组件对应的事件,譬如组件为 AButton,on 可以是 { click: () => {} } */
   on?: any;
+  /** 执行命令后如果子页面发送了消息会在这里接收 */
+  messageHandler?: (args: any) => void;
 };

+ 18 - 0
src/components/CADViewer/src/viewer.api.ts

@@ -0,0 +1,18 @@
+import { ContentTypeEnum } from '/@/enums/httpEnum';
+import { defHttp } from '/@/utils/http/axios';
+
+enum Api {
+  transformCadFile = '/safety/cadFile/transformCadFileType',
+}
+/**
+ * 列表接口
+ * @param data
+ */
+export const transformCadFile = (data) =>
+  defHttp.post({
+    url: Api.transformCadFile,
+    params: data,
+    headers: {
+      'Content-Type': ContentTypeEnum.FORM_DATA,
+    },
+  });

+ 0 - 0
src/components/CADViewer/src/const.ts → src/components/CADViewer/src/viewer.data.ts


+ 5 - 0
types/global.d.ts

@@ -48,6 +48,9 @@ declare global {
   interface ImportMetaEnv extends ViteEnv {
     __: unknown;
   }
+  interface ImportMeta {
+    readonly env: ImportMetaEnv;
+  }
 
   declare interface ViteEnv {
     VITE_PORT: number;
@@ -59,6 +62,8 @@ declare global {
     VITE_USE_CDN: boolean;
     VITE_BUILD_COMPRESS: 'gzip' | 'brotli' | 'none';
     VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE: boolean;
+    VITE_GLOB_DOMAIN_URL: string;
+    PROD: boolean;
   }
 
   declare function parseInt(s: string | number, radix?: number): number;

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini