Browse Source

安装monaco-editor;安装vue-pdf-embed;python编辑器;查看报告;Three.js基础框架搭建

lichunyang 6 months ago
parent
commit
863153e176

+ 258 - 3
package-lock.json

@@ -27,13 +27,17 @@
         "js-cookie": "2.2.0",
         "mitt": "^3.0.1",
         "moment": "^2.30.1",
+        "monaco-editor": "^0.52.2",
         "normalize.css": "^8.0.1",
         "pinia": "^2.1.6",
         "pinia-plugin-persistedstate": "^3.2.0",
         "sass": "^1.71.1",
         "sass-loader": "^13.3.3",
         "v-scale-screen": "^2.2.0",
+        "vite-plugin-monaco-editor": "^1.1.0",
         "vue": "^3.3.4",
+        "vue-monaco-editor": "^0.0.19",
+        "vue-pdf-embed": "^2.1.2",
         "vue-router": "4.0",
         "webuploader": "^0.1.8"
       },
@@ -2583,6 +2587,177 @@
         "node": "^12.20.0 || >=14"
       }
     },
+    "node_modules/@napi-rs/canvas": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas/-/canvas-0.1.68.tgz",
+      "integrity": "sha512-LQESrePLEBLvhuFkXx9jjBXRC2ClYsO5mqQ1m/puth5z9SOuM3N/B3vDuqnC3RJFktDktyK9khGvo7dTkqO9uQ==",
+      "optional": true,
+      "engines": {
+        "node": ">= 10"
+      },
+      "optionalDependencies": {
+        "@napi-rs/canvas-android-arm64": "0.1.68",
+        "@napi-rs/canvas-darwin-arm64": "0.1.68",
+        "@napi-rs/canvas-darwin-x64": "0.1.68",
+        "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.68",
+        "@napi-rs/canvas-linux-arm64-gnu": "0.1.68",
+        "@napi-rs/canvas-linux-arm64-musl": "0.1.68",
+        "@napi-rs/canvas-linux-riscv64-gnu": "0.1.68",
+        "@napi-rs/canvas-linux-x64-gnu": "0.1.68",
+        "@napi-rs/canvas-linux-x64-musl": "0.1.68",
+        "@napi-rs/canvas-win32-x64-msvc": "0.1.68"
+      }
+    },
+    "node_modules/@napi-rs/canvas-android-arm64": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.68.tgz",
+      "integrity": "sha512-h1KcSR4LKLfRfzeBH65xMxbWOGa1OtMFQbCMVlxPCkN1Zr+2gK+70pXO5ktojIYcUrP6KDcOwoc8clho5ccM/w==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-darwin-arm64": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.68.tgz",
+      "integrity": "sha512-/VURlrAD4gDoxW1GT/b0nP3fRz/fhxmHI/xznTq2FTwkQLPOlLkDLCvTmQ7v6LtGKdc2Ed6rvYpRan+JXThInQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-darwin-x64": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.68.tgz",
+      "integrity": "sha512-tEpvGR6vCLTo1Tx9wmDnoOKROpw57wiCWwCpDOuVlj/7rqEJOUYr9ixW4aRJgmeGBrZHgevI0EURys2ER6whmg==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.68.tgz",
+      "integrity": "sha512-U9xbJsumPOiAYeAFZMlHf62b9dGs2HJ6Q5xt7xTB0uEyPeurwhgYBWGgabdsEidyj38YuzI/c3LGBbSQB3vagw==",
+      "cpu": [
+        "arm"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-arm64-gnu": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.68.tgz",
+      "integrity": "sha512-KFkn8wEm3mPnWD4l8+OUUkxylSJuN5q9PnJRZJgv15RtCA1bgxIwTkBhI/+xuyVMcHqON9sXq7cDkEJtHm35dg==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-arm64-musl": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.68.tgz",
+      "integrity": "sha512-IQzts91rCdOALXBWQxLZRCEDrfFTGDtNRJMNu+2SKZ1uT8cmPQkPwVk5rycvFpvgAcmiFiOSCp1aRrlfU8KPpQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.68.tgz",
+      "integrity": "sha512-e9AS5UttoIKqXSmBzKZdd3NErSVyOEYzJfNOCGtafGk1//gibTwQXGlSXmAKuErqMp09pyk9aqQRSYzm1AQfBw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-x64-gnu": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.68.tgz",
+      "integrity": "sha512-Pa/I36VE3j57I3Obhrr+J48KGFfkZk2cJN/2NmW/vCgmoF7kCP6aTVq5n+cGdGWLd/cN9CJ9JvNwEoMRDghu0g==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-linux-x64-musl": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.68.tgz",
+      "integrity": "sha512-9c6rkc5195wNxuUHJdf4/mmnq433OQey9TNvQ9LspJazvHbfSkTij8wtKjASVQsJyPDva4fkWOeV/OQ7cLw0GQ==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
+    "node_modules/@napi-rs/canvas-win32-x64-msvc": {
+      "version": "0.1.68",
+      "resolved": "https://registry.npmmirror.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.68.tgz",
+      "integrity": "sha512-Fc5Dez23u0FoSATurT6/w1oMytiRnKWEinHivdMvXpge6nG4YvhrASrtqMk8dGJMVQpHr8QJYF45rOrx2YU2Aw==",
+      "cpu": [
+        "x64"
+      ],
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">= 10"
+      }
+    },
     "node_modules/@nodelib/fs.scandir": {
       "version": "2.1.5",
       "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
@@ -5867,8 +6042,7 @@
     "node_modules/lodash.debounce": {
       "version": "4.0.8",
       "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
-      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
-      "peer": true
+      "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
     },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
@@ -5993,6 +6167,11 @@
         "node": "*"
       }
     },
+    "node_modules/monaco-editor": {
+      "version": "0.52.2",
+      "resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.52.2.tgz",
+      "integrity": "sha512-GEQWEZmfkOGLdd3XK8ryrfWz3AIP8YymVXiPHEdewrUq7mh0qrKrfHLNCXcbB6sTnMLnOZ3ztSiKcciFUkIJwQ=="
+    },
     "node_modules/ms": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz",
@@ -6253,6 +6432,17 @@
       "integrity": "sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==",
       "dev": true
     },
+    "node_modules/pdfjs-dist": {
+      "version": "4.10.38",
+      "resolved": "https://registry.npmmirror.com/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz",
+      "integrity": "sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ==",
+      "engines": {
+        "node": ">=20"
+      },
+      "optionalDependencies": {
+        "@napi-rs/canvas": "^0.1.65"
+      }
+    },
     "node_modules/performance-now": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
@@ -6847,7 +7037,6 @@
       "version": "0.6.1",
       "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
       "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
-      "peer": true,
       "engines": {
         "node": ">=0.10.0"
       }
@@ -7493,6 +7682,14 @@
         }
       }
     },
+    "node_modules/vite-plugin-monaco-editor": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/vite-plugin-monaco-editor/-/vite-plugin-monaco-editor-1.1.0.tgz",
+      "integrity": "sha512-IvtUqZotrRoVqwT0PBBDIZPNraya3BxN/bfcNfnxZ5rkJiGcNtO5eAOWWSgT7zullIAEqQwxMU83yL9J5k7gww==",
+      "peerDependencies": {
+        "monaco-editor": ">=0.33.0"
+      }
+    },
     "node_modules/vite-plugin-vue-setup-extend": {
       "version": "0.4.0",
       "resolved": "https://registry.npmmirror.com/vite-plugin-vue-setup-extend/-/vite-plugin-vue-setup-extend-0.4.0.tgz",
@@ -7548,6 +7745,64 @@
         "eslint": ">=6.0.0"
       }
     },
+    "node_modules/vue-monaco-editor": {
+      "version": "0.0.19",
+      "resolved": "https://registry.npmmirror.com/vue-monaco-editor/-/vue-monaco-editor-0.0.19.tgz",
+      "integrity": "sha512-6tgCWkC1WDPp8K1xgZ9Fp7U4ww0DIQPZC/x9Ih8hEuKPfpekzcHo2r9DaWa1lvzFMeJPv5e4mFCYhmR9+SAl8A==",
+      "dependencies": {
+        "lodash.debounce": "^4.0.8",
+        "vue": "^2.1.0"
+      }
+    },
+    "node_modules/vue-monaco-editor/node_modules/@vue/compiler-sfc": {
+      "version": "2.7.16",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz",
+      "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==",
+      "dependencies": {
+        "@babel/parser": "^7.23.5",
+        "postcss": "^8.4.14",
+        "source-map": "^0.6.1"
+      },
+      "optionalDependencies": {
+        "prettier": "^1.18.2 || ^2.0.0"
+      }
+    },
+    "node_modules/vue-monaco-editor/node_modules/prettier": {
+      "version": "2.8.8",
+      "resolved": "https://registry.npmmirror.com/prettier/-/prettier-2.8.8.tgz",
+      "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
+      "optional": true,
+      "bin": {
+        "prettier": "bin-prettier.js"
+      },
+      "engines": {
+        "node": ">=10.13.0"
+      },
+      "funding": {
+        "url": "https://github.com/prettier/prettier?sponsor=1"
+      }
+    },
+    "node_modules/vue-monaco-editor/node_modules/vue": {
+      "version": "2.7.16",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-2.7.16.tgz",
+      "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==",
+      "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
+      "dependencies": {
+        "@vue/compiler-sfc": "2.7.16",
+        "csstype": "^3.1.0"
+      }
+    },
+    "node_modules/vue-pdf-embed": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmmirror.com/vue-pdf-embed/-/vue-pdf-embed-2.1.2.tgz",
+      "integrity": "sha512-/j++oknFBY9x/MgEFBo9tSuOXS0Z9COlywwLhMREhiGfmuQqpnGy5T+SwVIXxR1tmdzM/lHog8JL7HOAgXT1aw==",
+      "dependencies": {
+        "pdfjs-dist": "^4.10.38"
+      },
+      "peerDependencies": {
+        "vue": "^3.3.0"
+      }
+    },
     "node_modules/vue-router": {
       "version": "4.0.16",
       "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.0.16.tgz",

+ 4 - 1
package.json

@@ -2,7 +2,6 @@
   "name": "disater",
   "private": true,
   "version": "0.0.0",
-  "type": "module",
   "scripts": {
     "dev": "vite --mode development",
     "build": "vite build --mode production",
@@ -30,13 +29,17 @@
     "js-cookie": "2.2.0",
     "mitt": "^3.0.1",
     "moment": "^2.30.1",
+    "monaco-editor": "^0.52.2",
     "normalize.css": "^8.0.1",
     "pinia": "^2.1.6",
     "pinia-plugin-persistedstate": "^3.2.0",
     "sass": "^1.71.1",
     "sass-loader": "^13.3.3",
     "v-scale-screen": "^2.2.0",
+    "vite-plugin-monaco-editor": "^1.1.0",
     "vue": "^3.3.4",
+    "vue-monaco-editor": "^0.0.19",
+    "vue-pdf-embed": "^2.1.2",
     "vue-router": "4.0",
     "webuploader": "^0.1.8"
   },

BIN
src/assets/pdf/sample.pdf


+ 3 - 0
src/components.d.ts

@@ -9,6 +9,9 @@ declare module 'vue' {
   export interface GlobalComponents {
     Header: typeof import('./components/header.vue')['default']
     NavigateBar: typeof import('./components/layout/NavigateBar.vue')['default']
+    Pagination: typeof import('./components/Pagination/index.vue')['default']
+    PdfReportView: typeof import('./components/pdfReportView/index.vue')['default']
+    PythonEditor: typeof import('./components/PythonEditor/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
   }

+ 50 - 0
src/components/PythonEditor/index.vue

@@ -0,0 +1,50 @@
+<template>
+  <div id="editorContainer" ></div>
+</template>
+
+<script setup>
+import { toRaw } from 'vue';
+import * as monaco from 'monaco-editor'; // 全部导入
+
+const props = defineProps({
+  value: String,
+  language: String,
+});
+
+const getVal = () => {
+	return toRaw(editor.value).getValue(); //获取编辑器中的文本
+}
+
+const emit = defineEmits(['change']);
+
+const editor = ref(null);
+
+onMounted(() => {
+  editor.value = monaco.editor.create(document.getElementById('editorContainer'), {
+    value: props.value || '',
+    language: props.language || 'python',
+    minimap: {
+      enabled: true,
+    },
+    colorDecorators: true, // 颜色装饰器
+    readOnly: false, // 是否开启已读功能
+    theme: "vs-dark", // 主题 vs hc-black 
+    automaticLayout: true,
+  });
+
+  // 监听编辑器内容变化
+  editor.value.onDidChangeModelContent(() => {
+    // 触发父组件的 change 事件,通知编辑器内容变化
+    emit('change', getVal());
+  });
+
+   // 启用代码检查
+  // enableCodeValidation();
+});
+</script>
+
+<style>
+#editorContainer{
+  height: 300px !important;
+}
+</style>

+ 28 - 0
src/components/pdfReportView/index.vue

@@ -0,0 +1,28 @@
+<template>
+  <div class="pdf-container">
+    <vue-pdf-embed
+      :source="pdfUrl"
+      annotation-layer
+      text-layer
+      :scale="scale"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref } from 'vue';
+import VuePdfEmbed from 'vue-pdf-embed';
+
+const pdfUrl = ref(new URL('@/assets/pdf/sample.pdf', import.meta.url).href);
+const scale = ref(1.5); // 提高 scale 值
+</script>
+
+<style scoped>
+.pdf-container {
+  height: 580px;
+  width: 100%;
+  overflow-y: auto; /* 启用垂直滚动条 */
+  transform: scale(1); /* 缩小容器 */
+  transform-origin: top left; /* 确保缩放从左上角开始 */
+}
+</style>

+ 20 - 16
src/views/home.vue

@@ -923,8 +923,8 @@
           </el-dialog>
           <!-- 查看报告 -->
           <el-dialog v-model="dialog.lookover" align-center :modal="false" :close-on-click-modal="false"
-            :append-to-body="true" draggable :fullscreen="false" :modal-append-to-body="false" modal-class="summary-dlg"
-            :before-close="handleClose" width="500" class="dialog_class bgcolor tianjia sel" style="max-height: 70%;">
+            :append-to-body="true" draggable :fullscreen="false" :modal-append-to-body="false" modal-class="summary-dlgPdf"
+            :before-close="handleClose" width="580px" height="800px">
 
             <template #header="{ titleId, titleClass }">
               <div class="my-header ">
@@ -932,11 +932,12 @@
                 <h4 :id="titleId" :class="titleClass">查看报告:</h4>
               </div>
             </template>
-            <div class="numberinput lefttext">
+            <pdfReportView/>
+            <!-- <div class="numberinput lefttext">
               <el-form>
                 <el-image :src="getImgPath('baog.png')" fit="contain"></el-image>
               </el-form>
-            </div>
+            </div> -->
             <template #footer>
               <div class="dialog-footer">
                 <el-button @click="dialog.lookover = false">取消</el-button>
@@ -1228,23 +1229,16 @@
               </div>
             </template>
             <div class="footerp" style="height:500px;padding:15px">
-              <!-- <el-input
-            v-model="textarea1"
-            style="width: 100%;height:400px"
-            :autosize="{ minRows: 2, maxRows: 19}"
-            
-            type="textarea"
-            placeholder="Please input"
-          /> -->
-          <div class="text">
-          <el-input
+          <div>
+          <!-- <el-input
           v-model="textarea1"
        
           style="width: 100%"
           :autosize="{ minRows: 2, maxRows: 4 }"
           type="textarea"
           placeholder="Please input"
-        />
+        /> -->
+            <PythonEdit :value="textarea1" language="python" @change="handleEditorChange"/>
           </div>
         <div class="pythfoter">
            <div class="span active" >
@@ -1668,7 +1662,8 @@ import TACS from "./titlecomponent/TACS.vue";
 
 import fileUploads from './components/fileuploads.vue'
 import Exreport from "./titlecomponent/Exreport.vue";
-
+import PythonEdit from '@/components/PythonEditor/index.vue'; // python编辑器
+import pdfReportView from '@/components/pdfReportView/index.vue'; //报告查看
 
 let Sidebarref = ref();
 let resource=ref(0);
@@ -2573,6 +2568,11 @@ const pythonSubmit = () => {
   });
 }
 
+// python处理编辑器内容变化的方法
+const handleEditorChange = (value) => {
+  console.log('Editor content changed in parent component:', value);
+};
+
 // 模块选择
 const clickgeometry = (e, index, key, name) => {
   console.log(key);
@@ -4129,6 +4129,9 @@ color: #2267B1;
 .summary-dlg .el-dialog {
   pointer-events: auto;
 }
+.summary-dlgPdf .el-dialog__body{
+  padding: 5px;
+}
 
 .el-table--enable-row-hover .el-table__body tr:hover>td.el-table__cell {
   background-color: #fff;
@@ -4171,4 +4174,5 @@ color: #2267B1;
   font-size: 16px;
   font-family: 'Source Sans Pro-Regular';
 }
+
 </style>

+ 4 - 1
src/views/home/querylist.vue

@@ -19,7 +19,10 @@
             <el-table-column prop="image" label="模板" width="230">
                 <template #default="scope">
                     <div class="elimagetab">
-                        <el-image :src="scope.row.image" fit="contain" style="height: 100%; width: auto;"></el-image>
+                        <el-image 
+                        :src="scope.row.image" 
+                        fit="contain" 
+                        style="height: auto; max-width: 100%"></el-image>
                     </div>     
                 </template>
             </el-table-column>

+ 193 - 0
src/views/testThreejsView/components/ThreeScene.vue

@@ -0,0 +1,193 @@
+<template>
+  <div ref="threeContainer" class="three-container"></div>
+</template>
+
+<script setup>
+import { ref, onMounted, onUnmounted, watch } from "vue";
+import * as THREE from "three";
+import { VTKParserFactory } from "../utils/parsers/VTKParserFactory";
+import { UnstructuredGridRenderer } from "../utils/renderers/UnstructuredGridRenderer";
+import { PolyDataRenderer } from "../utils/renderers/PolyDataRenderer";
+import {
+  initScene,
+  initCamera,
+  initRenderer,
+  animateScene,
+  cleanupScene,
+  initControls,
+  initAxesHelper
+} from "../utils/threeUtils";
+
+const props = defineProps({
+  vtkData: {
+    type: Object,
+    required: true,
+  },
+});
+
+const threeContainer = ref(null);
+
+let scene, camera, renderer, controls, axesHelper;
+
+// 创建坐标系指示器
+const createAxesHelper = () => {
+  const axesHelper = new THREE.AxesHelper(20); // 50 是坐标轴的长度
+  axesHelper.position.set(-100, -100, 0); // 将坐标系指示器放置在左下角
+  console.log("Axes helper added to scene:", axesHelper); // 调试日志
+  // 添加标签
+  const labels = ['X', 'Y', 'Z'];
+  const colors = [0xff0000, 0x00ff00, 0x0000ff]; // 红、绿、蓝
+  labels.forEach((label, index) => {
+    const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ color: colors[index] }));
+    sprite.position.set(60 * (index === 0 ? 1 : 0), 60 * (index === 1 ? 1 : 0), 60 * (index === 2 ? 1 : 0));
+    axesHelper.add(sprite);
+  });
+  return axesHelper;
+};
+
+// 初始化场景
+const init = () => {
+  scene = initScene();
+  camera = initCamera();
+  renderer = initRenderer(threeContainer.value);
+  controls = initControls(camera, renderer);
+   // 初始化 AxesHelper
+  const { axesHelper, updateAxesHelperPosition } = initAxesHelper(scene, camera, renderer.domElement);
+  return { updateAxesHelperPosition };
+};
+
+const adjustCameraToFit = (scene, camera) => {
+  const box = new THREE.Box3().setFromObject(scene);
+  const size = new THREE.Vector3();
+  box.getSize(size);
+
+  const maxDim = Math.max(size.x, size.y, size.z);
+  const fov = camera.fov * (Math.PI / 180);
+  let cameraZ = Math.abs(maxDim / Math.sin(fov / 2));
+
+  const center = new THREE.Vector3();
+  box.getCenter(center);
+  camera.position.set(center.x, center.y, cameraZ);
+  camera.lookAt(center);
+};
+
+// 渲染 VTK 数据
+const renderVTK = (vtkData) => {
+  if (!vtkData) return;
+  // 根据文件格式选择解析器
+  const parser = VTKParserFactory.createParser(vtkData.datasetType);
+  const parsedData = parser.parse(vtkData);
+
+  // 根据文件格式选择渲染器
+  let dataRenderer;
+  switch (vtkData.datasetType) {
+    case "UNSTRUCTURED_GRID":
+      dataRenderer = new UnstructuredGridRenderer();
+      break;
+    case "POLYDATA":
+      dataRenderer = new PolyDataRenderer();
+      break;
+    default:
+      throw new Error(`Unsupported dataset type: ${vtkData.datasetType}`);
+  }
+
+  // 渲染数据
+  dataRenderer.render(parsedData, scene);
+
+  // 根据数据类型调整相机位置
+  if (vtkData.datasetType === "UNSTRUCTURED_GRID") {
+    adjustCameraForUnstructuredGrid(scene, camera);
+  } else if (vtkData.datasetType === "POLYDATA") {
+    adjustCameraForPolydata(scene, camera);
+  }
+
+  // adjustCameraToFit(scene, camera);
+};
+
+// 监听 vtkData 变化
+watch(
+  () => props.vtkData,
+  (newData) => {
+    if (newData) {
+      // 清空场景
+      while (scene.children.length > 0) {
+        scene.remove(scene.children[0]);
+      }
+
+      // 重新渲染 VTK 数据
+      renderVTK(newData);
+    }
+  },
+  { immediate: true }
+);
+
+// VTK 数据
+onMounted(() => {
+  const { updateAxesHelperPosition } = init();
+  animateScene(scene, camera, renderer, controls, () => {
+    updateAxesHelperPosition(); // 更新 AxesHelper 的位置和方向
+  });
+  // 监听窗口大小变化
+  window.addEventListener("resize", onWindowResize);
+});
+onUnmounted(() => {
+  // 清理场景
+  cleanupScene(renderer);
+  // 移除窗口大小变化监听器
+  window.removeEventListener("resize", onWindowResize);
+});
+
+const onWindowResize = () => {
+  const width = threeContainer.value.clientWidth;
+  const height = threeContainer.value.clientHeight;
+  camera.aspect = width / height;
+  camera.updateProjectionMatrix();
+  renderer.setSize(width, height);
+};
+
+const adjustCameraForUnstructuredGrid = (scene, camera) => {
+  const box = new THREE.Box3().setFromObject(scene);
+  const size = new THREE.Vector3();
+  box.getSize(size);
+
+  const maxDim = Math.max(size.x, size.y, size.z);
+  const fov = camera.fov * (Math.PI / 180);
+  let cameraZ = Math.abs(maxDim / Math.sin(fov / 2));
+
+  const center = new THREE.Vector3();
+  box.getCenter(center);
+  camera.position.set(center.x, center.y, cameraZ);
+  camera.lookAt(center);
+};
+
+const adjustCameraForPolydata = (scene, camera) => {
+  const box = new THREE.Box3().setFromObject(scene);
+  // 手动设置边界框
+  box.set(
+    new THREE.Vector3(-100, -100, -100),
+    new THREE.Vector3(100, 100, 100)
+  );
+  const size = new THREE.Vector3();
+  box.getSize(size);
+
+  console.log("Polydata bounding box:", box);
+  console.log("Polydata size:", size);
+
+  const maxDim = Math.max(size.x, size.y, size.z);
+  const fov = camera.fov * (Math.PI / 180);
+  let cameraZ = Math.abs(maxDim / Math.sin(fov / 2));
+
+  const center = new THREE.Vector3();
+  box.getCenter(center);
+  camera.position.set(center.x, center.y, cameraZ * 0.8); // 调整相机距离
+  camera.lookAt(center);
+};
+</script>
+
+<style>
+.three-container {
+  width: 100%;
+  height: calc(89vh - 8px); /* 给容器设置固定高度 */
+  border: 1px solid #ccc; /* 可选:添加边框以便查看容器范围 */
+}
+</style>

+ 31 - 0
src/views/testThreejsView/index.vue

@@ -0,0 +1,31 @@
+<template>
+  <div>
+    <ThreeScene :vtkData="vtkData"/>
+  </div>
+</template>
+
+<script setup>
+import ThreeScene from "./components/ThreeScene.vue";
+import useVtkStore  from '@/store/modules/vtkData';
+const vtkStore = useVtkStore(); // 使用 useVtkStore
+const route = useRoute();
+const vtkData = ref(null);
+onMounted(() => {
+
+  // 从 VtkStore 中获取数据
+  vtkData.value = vtkStore.getVTKData;
+  
+  if (!vtkData.value) {
+    console.error("未接收到 VTK 数据");
+    return;
+  }
+});
+
+onUnmounted(() => {
+  // 清除 VtkStore 中的数据
+  // vtkStore.clearVTKData();
+});
+</script>
+
+<style>
+</style> 

+ 15 - 0
src/views/testThreejsView/utils/parsers/PolyDataParser.js

@@ -0,0 +1,15 @@
+export class PolyDataParser {
+    parse(vtkData) {
+      const points = vtkData.points.map(p => [p.x, p.y, p.z]).flat();
+      const polygons = vtkData.polygons || [];
+      const pointData = vtkData.pointData || {};
+      const normals = vtkData.normals || [];
+  
+      return {
+        points,
+        polygons,
+        pointData,
+        normals,
+      };
+    }
+  }

+ 13 - 0
src/views/testThreejsView/utils/parsers/UnstructuredGridParser.js

@@ -0,0 +1,13 @@
+export class UnstructuredGridParser {
+    parse(vtkData) {
+      const points = vtkData.points.map(p => [p.x, p.y, p.z]).flat();
+      const cells = vtkData.cells || [];
+      const pointData = vtkData.pointData || {};
+  
+      return {
+        points,
+        cells,
+        pointData,
+      };
+    }
+  }

+ 15 - 0
src/views/testThreejsView/utils/parsers/VTKParserFactory.js

@@ -0,0 +1,15 @@
+import { UnstructuredGridParser } from './UnstructuredGridParser';
+import { PolyDataParser } from './PolyDataParser';
+
+export class VTKParserFactory {
+  static createParser(datasetType) {
+    switch (datasetType) {
+      case "UNSTRUCTURED_GRID":
+        return new UnstructuredGridParser();
+      case "POLYDATA":
+        return new PolyDataParser();
+      default:
+        throw new Error(`Unsupported dataset type: ${datasetType}`);
+    }
+  }
+}

+ 97 - 0
src/views/testThreejsView/utils/renderers/PolyDataRenderer.js

@@ -0,0 +1,97 @@
+import * as THREE from 'three';
+
+export class PolyDataRenderer {
+  render(vtkData, scene) {
+    const { points, polygons, pointData, normals } = vtkData;
+    // 创建点
+    const pointsMesh = this.createPoints(points);
+    scene.add(pointsMesh);
+
+    // 创建多边形
+    const polygonsMesh = this.createPolygons(points, polygons);
+    scene.add(polygonsMesh);
+
+    // 创建带颜色映射的点
+    if (pointData && pointData['Displacement-magnitude']) {
+      const displacementMagnitude = pointData['Displacement-magnitude'];
+      const minDisplacement = Math.min(...displacementMagnitude);
+      const maxDisplacement = Math.max(...displacementMagnitude);
+      const coloredPoints = this.createColoredPoints(points, displacementMagnitude, minDisplacement, maxDisplacement);
+      scene.add(coloredPoints);
+    }
+
+    // 创建法向量箭头
+    if (normals && normals.length > 0) {
+      const normalVectors = normals.map(n => [n.x, n.y, n.z]).flat();
+      const normalArrows = this.createVectorArrows(points, normalVectors);
+      scene.add(normalArrows);
+    }
+  }
+
+  createPoints(pointsData) {
+    const geometry = new THREE.BufferGeometry();
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsData, 3));
+    const material = new THREE.PointsMaterial({ color: 0xff0000, size: 10 });
+    return new THREE.Points(geometry, material);
+  }
+
+  createPolygons(pointsData, polygonsData) {
+    const geometry = new THREE.BufferGeometry();
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsData, 3));
+
+    const indices = [];
+    for (const polygon of polygonsData) {
+      for (let i = 0; i < polygon.length - 1; i++) {
+        indices.push(polygon[i], polygon[i + 1]);
+      }
+      indices.push(polygon[polygon.length - 1], polygon[0]); // 闭合多边形
+    }
+    geometry.setIndex(indices);
+
+    const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
+    return new THREE.LineSegments(geometry, material);
+  }
+
+  createColoredPoints(pointsData, scalars, minValue, maxValue) {
+    const geometry = new THREE.BufferGeometry();
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsData, 3));
+
+    const colors = [];
+    for (let i = 0; i < scalars.length; i++) {
+      const normalizedValue = (scalars[i] - minValue) / (maxValue - minValue);
+      const color = new THREE.Color();
+      color.setHSL(normalizedValue * 0.7, 1.0, 0.5);
+      colors.push(color.r, color.g, color.b);
+    }
+    geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
+
+    const material = new THREE.PointsMaterial({
+      size: 10,
+      vertexColors: true,
+    });
+
+    return new THREE.Points(geometry, material);
+  }
+
+  createVectorArrows(pointsData, vectors) {
+    const arrows = new THREE.Group();
+    for (let i = 0; i < pointsData.length; i += 3) {
+      const x = pointsData[i];
+      const y = pointsData[i + 1];
+      const z = pointsData[i + 2];
+      const vx = vectors[i];
+      const vy = vectors[i + 1];
+      const vz = vectors[i + 2];
+
+      const direction = new THREE.Vector3(vx, vy, vz).normalize();
+      const arrow = new THREE.ArrowHelper(
+        direction,
+        new THREE.Vector3(x, y, z),
+        Math.sqrt(vx * vx + vy * vy + vz * vz) * 10,
+        0xff0000
+      );
+      arrows.add(arrow);
+    }
+    return arrows;
+  }
+}

+ 106 - 0
src/views/testThreejsView/utils/renderers/UnstructuredGridRenderer.js

@@ -0,0 +1,106 @@
+import * as THREE from 'three';
+
+export class UnstructuredGridRenderer {
+  render(vtkData, scene) {
+    const { points, cells, pointData } = vtkData;
+
+    // 创建点
+    const pointsMesh = this.createPoints(points);
+    scene.add(pointsMesh);
+
+    // 创建线
+    const linesMesh = this.createLines(points, cells);
+    scene.add(linesMesh);
+
+    // 创建带颜色映射的点
+    if (pointData && pointData['Displacement-magnitude']) {
+      const displacementMagnitude = pointData['Displacement-magnitude'];
+      const minDisplacement = Math.min(...displacementMagnitude);
+      const maxDisplacement = Math.max(...displacementMagnitude);
+      const coloredPoints = this.createColoredPoints(points, displacementMagnitude, minDisplacement, maxDisplacement);
+      scene.add(coloredPoints);
+    }
+
+    // 创建向量箭头
+    if (pointData && pointData['translation']) {
+      const translationVectors = pointData['translation'];
+      const arrows = this.createVectorArrows(points, translationVectors);
+      scene.add(arrows);
+    }
+  }
+
+  // 创建点
+  createPoints(pointsData) {
+    const geometry = new THREE.BufferGeometry();
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsData, 3));
+    // 创建材质
+    const material = new THREE.PointsMaterial({ color: 0xff0000, size: 10 });
+    // 创建点对象
+    return new THREE.Points(geometry, material);
+  }
+
+  // 创建线
+  createLines(pointsData, cellsData) {
+    const geometry = new THREE.BufferGeometry();
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsData, 3));
+    // 将 CELLS 数据转换为线的索引
+    const indices = [];
+
+    for (const cell of cellsData) {
+      
+      for (let i = 0; i < cell.length - 1; i++) {
+        indices.push(cell[i], cell[i + 1]);
+      }
+    }
+    geometry.setIndex(indices);
+    // 创建材质
+    const material = new THREE.LineBasicMaterial({ color: 0x0000ff });
+    // 创建线对象
+    return new THREE.LineSegments(geometry, material);
+  }
+
+  // 创建带颜色映射的点
+  createColoredPoints(pointsData, scalars, minValue, maxValue) {
+    const geometry = new THREE.BufferGeometry();
+    geometry.setAttribute('position', new THREE.Float32BufferAttribute(pointsData, 3));
+
+    const colors = [];
+    for (let i = 0; i < scalars.length; i++) {
+      const normalizedValue = (scalars[i] - minValue) / (maxValue - minValue);
+      const color = new THREE.Color();
+      color.setHSL(normalizedValue * 0.7, 1.0, 0.5);
+      colors.push(color.r, color.g, color.b);
+    }
+    geometry.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));
+
+    const material = new THREE.PointsMaterial({
+      size: 10,
+      vertexColors: true,
+    });
+
+    return new THREE.Points(geometry, material);
+  }
+
+  // 创建向量箭头
+  createVectorArrows(pointsData, vectors) {
+    const arrows = new THREE.Group();
+    for (let i = 0; i < pointsData.length; i += 3) {
+      const x = pointsData[i];
+      const y = pointsData[i + 1];
+      const z = pointsData[i + 2];
+      const vx = vectors[i];
+      const vy = vectors[i + 1];
+      const vz = vectors[i + 2];
+
+      const direction = new THREE.Vector3(vx, vy, vz).normalize();
+      const arrow = new THREE.ArrowHelper(
+        direction,
+        new THREE.Vector3(x, y, z),
+        Math.sqrt(vx * vx + vy * vy + vz * vz) * 10,
+        0xff0000
+      );
+      arrows.add(arrow);
+    }
+    return arrows;
+  }
+}

+ 89 - 0
src/views/testThreejsView/utils/threeUtils.js

@@ -0,0 +1,89 @@
+import * as THREE from 'three';
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
+
+// 初始化场景
+export function initScene() {
+  const scene = new THREE.Scene();
+  scene.background = new THREE.Color(230 / 255, 231 / 255, 233 / 255); // 设置背景颜色
+  return scene;
+}
+
+// 初始化相机
+export function initCamera() {
+  const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
+  camera.position.set(100, 100, 300); // 调整相机位置
+  camera.lookAt(0, 0, 0); // 让相机看向点数据的中心
+  return camera;
+}
+
+// 初始化渲染器
+export function initRenderer(container) {
+  const renderer = new THREE.WebGLRenderer();
+  renderer.setSize(window.innerWidth, window.innerHeight);
+  container.appendChild(renderer.domElement);
+  return renderer;
+}
+
+// 初始化轨道控制器
+export function initControls(camera, renderer) {
+  const controls = new OrbitControls(camera, renderer.domElement);
+  controls.enableDamping = true; // 启用阻尼效果,使操作更平滑
+  controls.dampingFactor = 0.05; // 阻尼系数
+  controls.screenSpacePanning = true; // 允许平移
+  controls.minDistance = 10; // 最小缩放距离
+  controls.maxDistance = 8000; // 最大缩放距离
+  return controls;
+}
+
+// 添加 AxesHelper 到场景
+export function initAxesHelper(scene, camera, container) {
+  if (!scene || !camera || !container) {
+    throw new Error("initAxesHelper: scene, camera, or container is undefined");
+  }
+  // 创建 AxesHelper
+  const axesHelper = new THREE.AxesHelper(10); // 设置 AxesHelper 的大小为 5
+  // 将 AxesHelper 添加到场景
+  scene.add(axesHelper);
+  // 更新 AxesHelper 的位置和方向
+  const updateAxesHelperPosition = () => {
+    // 将 AxesHelper 放置在屏幕左下角
+    const width = container.clientWidth;
+    const height = container.clientHeight;
+    const offset = 50; // 距离左下角的偏移量
+    // 计算屏幕左下角的坐标
+    const screenPosition = new THREE.Vector3(-width / 2 + offset, -height / 2 + offset, 0);
+    // 将屏幕坐标转换为世界坐标
+    const worldPosition = screenPosition.unproject(camera);
+    // 设置 AxesHelper 的位置
+    axesHelper.position.copy(worldPosition);
+    // 确保 AxesHelper 的方向与相机保持一致
+    axesHelper.quaternion.copy(camera.quaternion);
+    // 打印调试信息
+    console.log("AxesHelper Position:", worldPosition);
+    console.log("AxesHelper Quaternion:", axesHelper.quaternion);
+  };
+
+  // 返回 AxesHelper 对象和更新函数
+  return {
+    axesHelper,
+    updateAxesHelperPosition,
+  };
+}
+
+// 动画循环
+export const animateScene = (scene, camera, renderer, controls, updateCallback) => {
+  const animate = () => {
+    requestAnimationFrame(animate);
+    controls.update(); // 更新控制器
+    if (updateCallback) updateCallback(); // 调用回调函数
+    renderer.render(scene, camera);
+  };
+  animate();
+};
+
+// 清理场景
+export const cleanupScene = (renderer) => {
+  if (renderer) {
+    renderer.dispose();
+  }
+};

+ 1 - 1
src/views/titlecomponent/ADflow.vue

@@ -37,7 +37,7 @@
               <fileUploads
                 :projectId="124" 
                 solverType="exampleSolver" 
-                accept=".cgns" 
+                accept=".cgns"
                 upId="uniqueId1" 
                 name="点击选择文件"
                 :imgSrc="meshFileImgSrc"

+ 2 - 2
src/views/vuetree/index.vue

@@ -587,8 +587,8 @@ async  function logToObject1() {
   mergedObj.value=JSON.stringify(obj);
   try {
         const container = vueFlowRef.value.$el;
-        const canvas = await html2canvas(container);
-        const img = canvas.toDataURL('image/png');
+        const canvas = await html2canvas(container, { scale: 3 });
+        const img = canvas.toDataURL('image/jpeg', 1.0);
  
         // 创建一个图片元素并设置src属性为转换后的图片数据
        vueflowimg.value=img

+ 3 - 1
vite.config.ts

@@ -1,5 +1,6 @@
 import { defineConfig, loadEnv } from 'vite'
 import vue from '@vitejs/plugin-vue'
+import monacoEditorPlugin from 'vite-plugin-monaco-editor';
 
 //1、 导入 path 模块,帮助我们解析路径
 import { resolve } from 'path'
@@ -20,7 +21,8 @@ export default defineConfig(({ mode }) => {
     return {
         base: "./",
         plugins: [
-            vue(),
+            vue(), 
+            monacoEditorPlugin({}), // 启用 Monaco Editor 插件
             AutoImport({
                 //安装两行后你会发现在组件中不用再导入ref,reactive等
                 imports: ['vue', 'vue-router'],