Browse Source

添加风门、调整echarts

hrx 2 years ago
parent
commit
e5f18c16a9

+ 1 - 1
.env.development

@@ -26,4 +26,4 @@ VITE_GLOB_API_URL_PREFIX=
 #微前端qiankun应用,命名必须以VITE_APP_SUB_开头,jeecg-app-1为子应用的项目名称,也是子应用的路由父路径
 VITE_APP_SUB_micro-need-air = '//localhost:8099/'
 
-VITE_3D_MODAL_ARR = ['fm/fm-n-processed.glb']
+VITE_3D_MODAL_ARR = fm/fm-4.glb

+ 2 - 2
.env.production

@@ -5,7 +5,7 @@ VITE_USE_MOCK = true
 VITE_PUBLIC_PATH = /
 
 # 控制台不输出
-VITE_DROP_CONSOLE = true
+VITE_DROP_CONSOLE = false
 
 # 是否启用gzip或brotli压缩
 # 选项值: gzip | brotli | none
@@ -36,4 +36,4 @@ VITE_USE_PWA = false
 # 是否兼容旧浏览器
 VITE_LEGACY = false
 
-VITE_3D_MODAL_ARR = ['fm/fm-n-processed.glb']
+VITE_3D_MODAL_ARR = fm/amendakai.glb,fm/fm-4.glb,9f/9f-processed

+ 1 - 7
.eslintrc.js

@@ -17,13 +17,7 @@ module.exports = defineConfig({
       jsx: true,
     },
   },
-  extends: [
-    'plugin:vue/vue3-recommended',
-    'plugin:@typescript-eslint/recommended',
-    'prettier',
-    'plugin:prettier/recommended',
-    'plugin:jest/recommended',
-  ],
+  extends: ['plugin:vue/vue3-recommended', 'plugin:@typescript-eslint/recommended', 'prettier', 'plugin:prettier/recommended', 'plugin:jest/recommended'],
   rules: {
     'vue/script-setup-uses-vars': 'error',
     '@typescript-eslint/ban-ts-ignore': 'off',

+ 11 - 1
build/vite/plugin/index.ts

@@ -17,6 +17,7 @@ import { configSvgIconsPlugin } from './svgSprite';
 import { configHmrPlugin } from './hmr';
 import OptimizationPersist from 'vite-plugin-optimize-persist';
 import PkgConfig from 'vite-plugin-package-config';
+import glsl from "rollup-plugin-glsl";
 
 export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
   const { VITE_USE_IMAGEMIN, VITE_USE_MOCK, VITE_LEGACY, VITE_BUILD_COMPRESS, VITE_BUILD_COMPRESS_DELETE_ORIGIN_FILE } = viteEnv;
@@ -75,6 +76,15 @@ export function createVitePlugins(viteEnv: ViteEnv, isBuild: boolean) {
   //vite-plugin-theme【解决vite首次打开界面加载慢问题】
   vitePlugins.push(PkgConfig());
   vitePlugins.push(OptimizationPersist());
-
+  vitePlugins.push(
+    glsl({
+      // By default, everything gets included
+      include: "**/*.glsl",
+      // Undefined by default
+      exclude: ["**/index.html"],
+      // Source maps are on by default
+      // sourceMap: false,
+    })
+  )
   return vitePlugins;
 }

+ 2 - 2
mock/sys/user.ts

@@ -4,11 +4,11 @@ export function createFakeUserList() {
   return [
     {
       userId: '1',
-      username: 'admin',
+      username: '',
       realname: '管理员',
       avatar: 'https://q1.qlogo.cn/g?b=qq&nk=190848757&s=640',
       desc: 'manager',
-      password: '123456',
+      password: '',
       token: 'fakeToken1',
       homePath: '/dashboard/analysis',
       roles: [

+ 13 - 1
package.json

@@ -66,7 +66,7 @@
     "resize-observer-polyfill": "^1.5.1",
     "showdown": "^1.9.1",
     "sortablejs": "^1.14.0",
-    "three": "0.121.1",
+    "three": "^0.144.0",
     "tinymce": "^5.10.3",
     "vditor": "^3.8.13",
     "vue": "^3.2.20",
@@ -137,6 +137,7 @@
     "prettier": "^2.4.1",
     "pretty-quick": "^3.1.1",
     "rimraf": "^3.0.2",
+    "rollup-plugin-glsl": "^1.3.0",
     "rollup-plugin-visualizer": "5.5.2",
     "stylelint": "^13.13.1",
     "stylelint-config-prettier": "^9.0.3",
@@ -245,11 +246,22 @@
         "sortablejs",
         "three",
         "three/examples/jsm/controls/OrbitControls",
+        "three/examples/jsm/controls/OrbitControls.js",
         "three/examples/jsm/controls/TransformControls",
+        "three/examples/jsm/libs/stats.module.js",
         "three/examples/jsm/loaders/DRACOLoader",
+        "three/examples/jsm/loaders/DRACOLoader.js",
         "three/examples/jsm/loaders/GLTFLoader",
+        "three/examples/jsm/loaders/GLTFLoader.js",
         "three/examples/jsm/loaders/RGBELoader.js",
+        "three/examples/jsm/postprocessing/EffectComposer.js",
+        "three/examples/jsm/postprocessing/OutlinePass.js",
+        "three/examples/jsm/postprocessing/RenderPass.js",
+        "three/examples/jsm/postprocessing/ShaderPass.js",
+        "three/examples/jsm/postprocessing/UnrealBloomPass.js",
         "three/examples/jsm/renderers/CSS3DRenderer",
+        "three/examples/jsm/renderers/CSS3DRenderer.js",
+        "three/examples/jsm/shaders/FXAAShader.js",
         "tinymce/icons/default/icons",
         "tinymce/plugins/advlist",
         "tinymce/plugins/anchor",

+ 251 - 7
src/assets/less/modal.less

@@ -1,13 +1,16 @@
-.bg{
+.bg {
   width: 100%;
   height: 100%;
   background: url('/@/assets/images/vent/bg.png');
+  position: relative;
+  background-repeat: no-repeat;
+  background-size: 100%;
   // z-index: 1;
-  #damper3DCSS{
-    .elementTag{
+  #damper3DCSS {
+    .elementTag {
       position: relative;
       left: -18px;
-      top:  -2px;
+      top: -2px;
       // padding: 5px 20px;
       // color: #fff;
       // box-shadow: 0 0 18px #dbecff22;
@@ -38,16 +41,257 @@
       //   right: -85px;
       //   border-radius: 50%;
       // }
-      .elementContent{
+      .elementContent {
         background-color: rgb(20 143 221 / 68%);
         box-shadow: 0px 0px 12px rgb(0 128 255 / 75%);
         border: 1px solid rgb(127 177 255 / 75%);
         padding: 10px 20px 0px 20px;
         color: #efefef;
-        p{
+        p {
           line-height: 1rem;
         }
       }
     }
   }
-}
+}
+.scene-box {
+  width: 100%;
+  height: 100%;
+  position: absolute;
+  z-index: 2;
+  left: 0;
+  top: 0;
+  pointer-events: none;
+  display: flex;
+  flex-direction: column;
+  justify-items: center;
+  overflow: hidden;
+  .top-box {
+    position: absolute;
+    top: 0;
+    width: 100%;
+    height: 50px;
+    background-image: linear-gradient(to right, #046ad500, #006effe7, #046ad500);
+    display: flex;
+    align-items: center;
+    pointer-events: auto;
+    .row {
+      color: #fff;
+      display: flex;
+    }
+    .top-left {
+      width: 400px;
+      height: 100%;
+      font-size: 16px;
+      display: flex;
+      align-items: center;
+      padding-left: 20px;
+      position: relative;
+      background: url('/@/assets/images/vent/tj.png') no-repeat;
+      background-position: center right;
+      background-size: auto 100%;
+      cursor: pointer;
+      padding-right: 80px;
+      color: #ffffff;
+      letter-spacing: 0.15em;
+      text-shadow: -1px -1px 1px #0084ff, 0px 1px 0 #28282822, 0px 2px 0 #28282822, 0px 3px 0 #28282822, 0px 4px 0 #28282822, 0px 5px 0 #28282822, 0px 6px 0 #28282822, 0px 7px 0 #28282822,
+        0px 8px 0 #28282822, 0px 9px 0 #28282822,
+        // 0px 10px 0 #28282822,
+        // 0px 11px 0 #28282822,
+        // 0px 12px 0 #181818,
+        // 0px 13px 0 #161616,
+        // 0px 14px 0 #141414,
+        // 0px 15px 0 #121212,
+        2px 20px 5px rgba(0, 0, 0, 0.3),
+        5px 23px 5px rgba(0, 0, 0, 0.1), 8px 27px 8px rgba(0, 0, 0, 0.2);
+      &::after {
+        width: calc(100% - 105px);
+        height: 100%;
+        content: '';
+        position: absolute;
+        display: block;
+        bottom: 1px;
+        border-bottom: 3px solid #5595ff;
+      }
+    }
+    .top-center {
+      flex: 2.2;
+      // justify-content: space-around;
+      padding-left: 10px;
+    }
+    .top-right {
+      flex: 1.8;
+      justify-content: center;
+      .run-type {
+        margin: 0 10px;
+      }
+      .control-title {
+        color: rgb(0, 255, 242);
+      }
+    }
+    .button-box {
+      position: relative;
+      padding: 5px;
+      border: 1px transparent solid;
+      border-radius: 5px;
+      margin-left: 8px;
+      margin-right: 8px;
+      width: auto;
+      height: 40px;
+      border: 1px solid #65dbea;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      color: #fff;
+      padding: 0 15px;
+      cursor: pointer;
+      &:hover {
+        background: linear-gradient(#3eb2ff55, #00c3ff55);
+      }
+      &::before {
+        width: calc(100% - 6px);
+        height: 32px;
+        content: '';
+        position: absolute;
+        top: 3px;
+        right: 0;
+        left: 3px;
+        bottom: 0;
+        z-index: -1;
+        border-radius: inherit; /*important*/
+        background: linear-gradient(#014978, #3bf3fc);
+      }
+      &::after {
+        width: calc(100% + 32px);
+        height: 10px;
+        content: '';
+        position: absolute;
+        top: 40px;
+        right: 0;
+        left: -16px;
+        bottom: 0;
+        z-index: -1;
+        border-radius: inherit; /*important*/
+        background: url('/@/assets/images/vent/short-light.png') no-repeat;
+        background-position: center;
+        background-size: 100%;
+        z-index: 999;
+      }
+    }
+    .button-disable {
+      border: 1px solid #66989e;
+
+      &:hover {
+        background: none;
+      }
+      &::before {
+        background: linear-gradient(#78c8fd55, #49d2fc55);
+      }
+      // &::after{
+      //   background: linear-gradient( #75c8ff55, #45d1fc55);
+      // }
+    }
+  }
+  .title-box {
+    font-family: Geneva, sans-serif;
+    width: 100%;
+    text-align: center;
+    position: absolute;
+    top: 50px;
+    font-size: 28px;
+    color: #ff9900;
+    letter-spacing: 0.15em;
+    text-shadow: -1px -1px 1px #efede3, 0px 1px 0 #28282822, 0px 2px 0 #28282822, 0px 3px 0 #28282822, 0px 4px 0 #28282822, 0px 5px 0 #28282822, 0px 6px 0 #28282822, 0px 7px 0 #28282822,
+      0px 8px 0 #28282822, 0px 9px 0 #28282822, 0px 10px 0 #28282822, 0px 11px 0 #28282822,
+      // 0px 12px 0 #181818,
+      // 0px 13px 0 #161616,
+      // 0px 14px 0 #141414,
+      // 0px 15px 0 #121212,
+      2px 20px 5px rgba(0, 0, 0, 0.3),
+      5px 23px 5px rgba(0, 0, 0, 0.1), 8px 27px 8px rgba(0, 0, 0, 0.2);
+    // 8px 28px 35px rgba(0, 0, 0, 0.9);
+    &:before {
+      content: attr(text);
+      position: absolute;
+      z-index: 10;
+      color: pink;
+      -webkit-mask: linear-gradient(to left, red, transparent);
+    }
+  }
+  .tabs-box {
+    position: fixed;
+    bottom: 0;
+    height: 30vh;
+    pointer-events: auto;
+    background-color: #ffffff11;
+    backdrop-filter: blur(10px);
+    .tab-item {
+      height: calc(30vh - 50px);
+      color: #fff;
+    }
+  }
+  .ant-input-number {
+    background-color: #ffffff00;
+    border: 1px solid #65dbea;
+    border-radius: 5px;
+    align-items: center;
+    display: flex;
+    color: #fff;
+    // line-height: 30px;
+  }
+}
+
+.ant-radio-group {
+  color: #fff !important;
+  .ant-radio-wrapper {
+    color: #fff !important;
+  }
+  .ant-radio {
+    color: #fff !important;
+  }
+}
+
+.elementContent {
+  background-color: rgb(20 143 221 / 40%);
+  box-shadow: 0px 0px 12px rgb(0 128 255 / 75%);
+  border: 1px solid rgb(127 177 255 / 75%);
+  padding: 10px 20px 15px 20px;
+  color: #efefef;
+  p {
+    // line-height: 1rem;
+  }
+}
+
+.state-icon {
+  display: inline-block;
+  width: 11px;
+  height: 11px;
+  border-radius: 6px;
+  margin-right: 6px;
+}
+.open {
+  border: 2px solid #133a56;
+  background: #4ecb73;
+}
+.close {
+  border: 2px solid #ff1818;
+  background: #ff8888;
+  animation: close 1s infinite;
+}
+@keyframes close {
+  0% {
+    border: 2px solid #ff8888;
+    background: #ff3538;
+  }
+  50% {
+    border: 2px solid #ff0000;
+    background: #c90000;
+  }
+  100% {
+    border: 2px solid #ff8888;
+    background: #ff3538;
+  }
+}
+.data-title {
+  color: #eee;
+}

+ 0 - 1
src/components/Page/index.ts

@@ -2,7 +2,6 @@ import { withInstall } from '/@/utils';
 
 import pageFooter from './src/PageFooter.vue';
 import pageWrapper from './src/PageWrapper.vue';
-
 export const PageFooter = withInstall(pageFooter);
 export const PageWrapper = withInstall(pageWrapper);
 

+ 1 - 0
src/components/Page/src/PageWrapper.vue

@@ -58,6 +58,7 @@
       upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
     },
     setup(props, { slots, attrs }) {
+      // debugger
       const wrapperRef = ref(null);
       const headerRef = ref(null);
       const contentRef = ref(null);

+ 5 - 8
src/components/Table/src/BasicTable.vue

@@ -12,8 +12,6 @@
       <template #[replaceFormSlotKey(item)]="data" v-for="item in getFormSlotKeys">
         <slot :name="item" v-bind="data || {}"></slot>
       </template>
-      
-
     </BasicForm>
 
     <Table ref="tableElRef" v-bind="getBindValues" :rowClassName="getRowClassName" v-show="getEmptyDataIsShowTable" @change="handleTableChange">
@@ -22,7 +20,6 @@
       </template>
 
       <template #[`header-${column.dataIndex}`] v-for="column in columns" :key="column.dataIndex">
-
         <HeaderCell :column="column" />
       </template>
     </Table>
@@ -98,7 +95,6 @@
         return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
       });
 
-      
       const isFixedHeightPage = inject(PageWrapperFixedHeightKey, false);
       watchEffect(() => {
         unref(isFixedHeightPage) && props.canResize && warn("'canResize' of BasicTable may not work in PageWrapper with 'fixedHeight' (especially in hot updates)");
@@ -174,7 +170,7 @@
       const { getFooterProps } = useTableFooter(getProps, getScrollRef, tableElRef, getDataSourceRef);
 
       const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm(getProps, slots, fetch, getLoading);
-      
+
       const getBindValues = computed(() => {
         const dataSource = unref(getDataSourceRef);
         let propsData: Recordable = {
@@ -204,8 +200,7 @@
         return propsData;
       });
 
-      console.log(777,getBindValues);
-      
+      console.log(777, getBindValues);
 
       const getWrapperClass = computed(() => {
         const values = unref(getBindValues);
@@ -390,7 +385,9 @@
 
       .ant-table-body {
         overflow-x: hidden !important;
-        //  overflow-y: scroll !important;
+        // height: 100px;
+        // max-height: calc(100% - 20px) !important;
+        overflow-y: scroll !important;
       }
 
       td {

+ 28 - 9
src/components/chart/BarAndLine.vue

@@ -6,7 +6,7 @@
   import { useECharts } from '/@/hooks/web/useECharts';
 
   export default defineComponent({
-    name: 'barAndLine',
+    name: 'BarAndLine',
     props: {
       chartData: {
         type: Array,
@@ -16,6 +16,15 @@
         type: Object,
         default: () => ({}),
       },
+      xAxisPropType: {
+        type: String,
+        required: true,
+      },
+      propTypeArr: {
+        type: Array,
+        default: () => [],
+        required: true,
+      },
       width: {
         type: String as PropType<string>,
         default: '100%',
@@ -39,6 +48,12 @@
             },
           },
         },
+        legend: {
+          top: 10,
+          textStyle: {
+            color: '#ffffffee',
+          },
+        },
         xAxis: {
           type: 'category',
           data: [],
@@ -64,21 +79,25 @@
           Object.assign(option, props.option);
         }
         //图例类型
-        let typeArr = Array.from(new Set(props.chartData.map((item) => item.type)));
+        // let typeArr = Array.from(new Set(props.chartData.map((item) => item.type)));
         //轴数据
-        let xAxisData = Array.from(new Set(props.chartData.map((item) => item.name)));
+        let xAxisData = Array.from(new Set(props.chartData.map((item) => item[props.xAxisPropType])));
+        // let xAxisData = Array.from(new Set(props.chartData.map((item) => item.name)));
         let seriesData = [];
-        typeArr.forEach((type) => {
-          let obj = { name: type };
-          let chartArr = props.chartData.filter((item) => type === item.type);
+        [...props.propTypeArr].forEach((propType: any) => {
+          let obj = { name: propType.name, type: propType.type };
+          // let obj = { name: type };
+          // let chartArr = props.chartData.filter((item) => item === propType.name);
           //data数据
-          obj['data'] = chartArr.map((item) => item.value);
-          obj['type'] = chartArr[0].seriesType;
+          obj['data'] = props.chartData.map((item) => item[propType.filed]);
+          // obj['type'] = chartArr[0].seriesType;
           seriesData.push(obj);
         });
+        console.log('seriesData------------>', seriesData);
+
         option.series = seriesData;
         option.xAxis.data = xAxisData;
-        setOptions(option);
+        setOptions(option, false);
       }
       return { chartRef };
     },

+ 6 - 3
src/components/chart/BarMulti.vue

@@ -6,7 +6,7 @@
   import { useECharts } from '/@/hooks/web/useECharts';
 
   export default defineComponent({
-    name: 'barMulti',
+    name: 'BarMulti',
     props: {
       chartData: {
         type: Array,
@@ -21,7 +21,7 @@
         type: String as PropType<string>,
         default: 'bar',
       },
-	  xAxisPropType: {
+      xAxisPropType: {
         type: String,
         required: true,
       },
@@ -56,6 +56,9 @@
         },
         legend: {
           top: 10,
+          textStyle: {
+            color: '#ffffffee',
+          },
         },
         grid: {
           left: 60,
@@ -68,7 +71,7 @@
         },
         yAxis: {
           type: 'value',
-		  nameTextStyle: {
+          nameTextStyle: {
             fontSize: 14,
           },
         },

+ 8 - 6
src/components/chart/LineMulti.vue

@@ -6,7 +6,7 @@
   import { useECharts } from '/@/hooks/web/useECharts';
 
   export default defineComponent({
-    name: 'lineMulti',
+    name: 'LineMulti',
     props: {
       chartData: {
         type: Array,
@@ -17,7 +17,7 @@
         type: Object,
         default: () => ({}),
       },
-	  xAxisPropType: {
+      xAxisPropType: {
         type: String,
         required: true,
       },
@@ -78,16 +78,18 @@
         if (props.option) {
           Object.assign(option, props.option);
         }
+        console.log(props.chartData);
+
         //图例类型
         // let typeArr = Array.from(new Set(props.chartData.map((item) => item.type)));
         //轴数据
         let xAxisData = Array.from(new Set(props.chartData.map((item) => item[props.xAxisPropType])));
         let seriesData = [];
-        [...props.propTypeArr.keys()].forEach((type) => {
-          let obj = { name: props.propTypeArr.get(type), type: props.type };
-          let chartArr = props.chartData.filter((item) => type === item.type);
+        [...props.propTypeArr.keys()].forEach((filed) => {
+          let obj = { name: props.propTypeArr.get(filed), type: props.type };
+          // let chartArr = props.chartData.filter((item) => filed === item.type);
           //data数据
-          obj['data'] = props.chartData.map((item) => item[type]);
+          obj['data'] = props.chartData.map((item) => item[filed]);
           seriesData.push(obj);
         });
         option.series = seriesData;

+ 1 - 1
src/hooks/core/useThree copy.ts

@@ -326,7 +326,7 @@ class UseThree {
 
   /* 场景环境背景 */
   setEnvMap(hdr) {
-    new RGBELoader().setPath('/public/3D/hdr/').load(hdr + '.hdr', (texture) => {
+    new RGBELoader().setPath('/public/model/hdr/').load(hdr + '.hdr', (texture) => {
       texture.mapping = THREE.EquirectangularReflectionMapping;
       (this.scene as THREE.Scene).background = texture;
       (this.scene as THREE.Scene).environment = texture;

+ 229 - 290
src/hooks/core/useThree.ts

@@ -1,44 +1,51 @@
 import * as THREE from 'three';
 import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
 // 导入轨道控制器
-import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls';
-import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
+import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
+import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
 // import gsap from 'gsap';
-import ResourceTracker from '/@/utils/threejs/ResourceTracker';
-import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
-import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader'
-
-
+import ResourceTracker from '/@/utils/threejs/ResourceTracker.js';
+import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
+import { DRACOLoader } from 'three/examples/jsm/loaders/DRACOLoader.js';
+import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
+import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
+import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
+import { setModalCenter } from '/@/utils/threejs/util';
+import Stats from 'three/examples/jsm/libs/stats.module.js';
+import { useModelStore } from '/@/store/modules/threejs';
 
 class UseThree {
-  canvasContainer: HTMLCanvasElement;
-  CSSCanvasContainer: HTMLCanvasElement | null = null;
-  camera: THREE.PerspectiveCamera | null = null;
+  stats: Stats | null = null; // 帧
+  canvasContainer: HTMLCanvasElement | null; //canvas 容器
+  CSSCanvasContainer: HTMLCanvasElement | null = null; // css canvas 容器
+  camera: THREE.PerspectiveCamera | null = null; // 相机
   scene: THREE.Scene | null = null;
   renderer: THREE.WebGLRenderer | null = null;
   css3dRender: CSS3DRenderer | null = null;
   orbitControls: OrbitControls | null = null;
-  giftLoader: THREE.Object3D | null = null;
-  animationMixer: THREE.AnimationMixer | null = null;
   animationAction: THREE.AnimationAction | null = null;
-  clock: THREE.Clock = new THREE.Clock(); // 计时器
+  clock: THREE.Clock | null = new THREE.Clock(); // 计时器
   timeoutId: NodeJS.Timeout | null = null;
-  animationId: number = 0
-  resourceTracker:ResourceTracker | null = null
-  track: any = null
-  spriteText: THREE.Sprite | null = null
+  animationId = 0;
+  spriteText: THREE.Sprite | null = null;
+  mouse = new THREE.Vector2();
+  rayCaster: THREE.Raycaster | null = null;
+  animations: THREE.AnimationClip[] = [];
+  mixers: THREE.AnimationMixer[] = [];
+  FPS = 40;
+  timeS = 0;
+  resourceTracker: ResourceTracker | null = null;
+  track: any = null;
+  composer; //后期
 
   constructor(canvasSelector, cssCanvas?) {
- 
-
-    this.resourceTracker = new ResourceTracker()
-    this.track = this.resourceTracker.track.bind(this.resourceTracker)
-    this.animationId = 0
+    this.resourceTracker = new ResourceTracker();
+    this.track = this.resourceTracker.track.bind(this.resourceTracker);
+    this.animationId = 0;
     this.canvasContainer = document.querySelector(canvasSelector);
-
     //初始化
     this.init(cssCanvas);
-    this.animate();
+    // this.animate();
     window.addEventListener('resize', this.resizeRenderer.bind(this));
     // 添加滚动事件,鼠标滚动模型执行动画
     // window.addEventListener('wheel', this.wheelRenderer.bind(this));
@@ -47,31 +54,39 @@ class UseThree {
     // 初始化场景
     this.initScene();
     // 初始化环境光
-    this.initLight();
+    // this.initLight();
     // 初始化相机
     this.initCamera();
     //初始化渲染器
     this.initRenderer();
-
     // 初始化控制器
     this.initControles();
-
-    if(cssCanvas){
-      this.initCSSRenderer(cssCanvas)
-      // this.addTextSprite()
+    if (cssCanvas) {
+      this.initCSSRenderer(cssCanvas);
     }
-    // this.setTestPlane()
+    // this.setTestPlane();
+    this.rayCaster = this.track(new THREE.Raycaster());
+    this.createStats();
+    // this.removeSawtooth();
+  }
+
+  createStats() {
+    this.stats = Stats();
+    this.stats?.setMode(0);
+    this.stats.domElement.style.position = 'absolute';
+    this.stats.domElement.style.left = '200px';
+    this.stats.domElement.style.top = '100px';
+    document.body.appendChild(this.stats.domElement);
   }
 
   initScene() {
     this.scene = this.track(new THREE.Scene());
     // const axesHelper = new THREE.AxesHelper(100);
     // this.scene?.add(axesHelper);
-
     // const size = 1000;
     // const divisions = 10;
-    // const gridHelper = new THREE.GridHelper( size, divisions );
-    // this.scene?.add( gridHelper )
+    // const gridHelper = new THREE.GridHelper(size, divisions);
+    // this.scene?.add(gridHelper);
   }
 
   initLight() {
@@ -81,192 +96,192 @@ class UseThree {
   }
 
   initCamera() {
-    this.camera = this.track(new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 0.01, 1000));
-    this.camera?.position.set(0, 0.2, 0.3);
-    // const helper = new THREE.CameraHelper( this.camera);
-    // this.scene?.add( helper );
-    
+    this.camera = this.track(new THREE.PerspectiveCamera(50, this.canvasContainer.clientWidth / this.canvasContainer.clientHeight, 0.000001, 10000));
+
+    // const helper = new THREE.CameraHelper(this.camera);
+    // this.scene?.add(helper);
   }
 
   initRenderer() {
-
-    this.renderer = this.track(new THREE.WebGLRenderer({ antialias: true, alpha:true })) as THREE.WebGLRenderer;
+    this.renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true }) as THREE.WebGLRenderer;
     // 设置屏幕像素比
     this.renderer?.setPixelRatio(window.devicePixelRatio);
     // 设置渲染的尺寸
-    this.renderer?.setSize(window.innerWidth, window.innerHeight);
+    this.renderer?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
     // 色调映射
     this.renderer.toneMapping = THREE.ACESFilmicToneMapping;
 
     this.renderer.outputEncoding = THREE.sRGBEncoding;
 
-    this.renderer.shadowMap.enabled = true
+    this.renderer.shadowMap.enabled = true;
 
-    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap
+    this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;
 
     // 曝光程度
     this.renderer.toneMappingExposure = 1;
 
-    this.canvasContainer.appendChild(this.renderer.domElement);
-    
+    this.canvasContainer?.appendChild(this.renderer.domElement);
   }
 
   initCSSRenderer(cssCanvas) {
     this.CSSCanvasContainer = document.querySelector(cssCanvas);
-    this.css3dRender = this.track(new CSS3DRenderer()) as CSS3DRenderer
-    this.css3dRender.setSize((window.innerWidth), (window.innerHeight))
-    this.CSSCanvasContainer?.appendChild(this.css3dRender.domElement)
-    this.css3dRender.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera)
+    this.css3dRender = this.track(new CSS3DRenderer()) as CSS3DRenderer;
+    this.css3dRender.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+    this.CSSCanvasContainer?.appendChild(this.css3dRender.domElement);
+    this.css3dRender.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera);
     this.orbitControls = this.track(new OrbitControls(this.camera as THREE.Camera, this.css3dRender?.domElement)) as OrbitControls;
-    this.orbitControls.update()
-    // this.orbitControls.enableZoom = false;
-    // // to disable rotation
-    // this.orbitControls.enableRotate = false;
-    // // to disable pan
-    // this.orbitControls.enablePan = false;
+    this.orbitControls.update();
   }
 
   initControles() {
     this.orbitControls = this.track(new OrbitControls(this.camera as THREE.Camera, this.renderer?.domElement)) as OrbitControls;
-    this.orbitControls.update()
-    // this.orbitControls.enableZoom = false;
-    // // to disable rotation
-    // this.orbitControls.enableRotate = false;
-    // // to disable pan
-    // this.orbitControls.enablePan = false;
+    this.orbitControls.update();
+    // this.orbitControls.enableDamping = true;
   }
 
-  setModalCenter(group) {
-    var box3 = new THREE.Box3()
-  
-    // 计算层级模型group的包围盒
-    // 模型group是加载一个三维模型返回的对象,包含多个网格模型
-    box3.expandByObject(group)
-    // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
-    var center = new THREE.Vector3()
-    box3.getCenter(center)
-    // console.log('查看几何体中心坐标', center);
-    // 重新设置模型的位置,使之居中。
-    group.position.x = group.position.x - center.x
-    group.position.y = group.position.y - center.y
-    group.position.z = group.position.z - center.z
-  };
-
   setModel(modalName) {
-    const a = new Date().getTime() / 1000
+    const modelStore = useModelStore();
     return new Promise(async (resolve, reject) => {
       try {
-        const db =  window['CustomDB']
-        const modalArr =  await db.modal.where('modalName').equals(modalName).toArray()
-        // debugger
-        if(modalArr.length > 0) {
-          const modalValue = modalArr[0].modalVal
+        const a = new Date().getTime();
+        let modalValue = modelStore.modelArr.get(modalName);
+        if (!modalValue) {
+          const db = window['CustomDB'];
+          const modalArr = await db.modal.where('modalName').equals(modalName).toArray();
+          modalValue = modalArr[0].modalVal;
+        }
+
+        if (modalValue) {
+          console.log('模型下载速度', new Date().getTime() - a);
+          const b = new Date().getTime();
           try {
-            const gltfLoader = new GLTFLoader()
-            const dracoLoader = new DRACOLoader();
-            dracoLoader.setDecoderPath( '/3D/draco/gltf/' );
-            dracoLoader.setDecoderConfig({ type: "js" }); //使用兼容性强的draco_decoder.js解码器
+            const gltfLoader = this.track(new GLTFLoader());
+            const dracoLoader = this.track(new DRACOLoader());
+            dracoLoader.setDecoderPath('/model/draco/gltf/');
+            dracoLoader.setDecoderConfig({ type: 'js' }); //使用兼容性强的draco_decoder.js解码器
             dracoLoader.preload();
             gltfLoader.setDRACOLoader(dracoLoader);
 
-            gltfLoader.setPath('/3d/glft/')
-            gltfLoader.parse(modalValue, '/3d/glft/', (gltf) => {
-              const object = this.track(gltf.scene.children[0])
-              this.setModalCenter(object)
-              object.traverse((obj) => {
-                if (obj instanceof THREE.Mesh) {
-                  obj.material.emissiveIntensity = 1
-                  obj.material.emissiveMap = obj.material.map
-                  if(obj.name !== "buxiugangse") {
-                    obj.receiveShadow = true
+            gltfLoader.setPath('/model/glft/');
+            gltfLoader.parse(
+              modalValue,
+              '/model/glft/',
+              (gltf) => {
+                const object = this.track(gltf.scene);
+                setModalCenter(object);
+                object.traverse((obj) => {
+                  if (obj instanceof THREE.Mesh) {
+                    obj.material.emissiveIntensity = 1;
+                    obj.material.emissiveMap = obj.material.map;
+                    obj.material.blending = THREE.CustomBlending;
+                    if (obj.name !== 'buxiugangse') {
+                      obj.receiveShadow = true;
+                    }
+                    obj.castShadow = true;
                   }
-                  obj.castShadow = true
-                  
-                  
-                } else if (obj.type === 'Object3D') {
-                  // const text3D = this.addYFText(obj)
-                  // const textCQ3D = this.addCQText(obj)
-                  // gltf.scene.add(text3D)
-                  // gltf.scene.add(textCQ3D)
-                }
-              })
-              this.scene?.add(object);
-              console.log(object);
-              resolve(object)
-            }, (err)=> {
-              console.log(err);
-              
-            })
-  
+                });
+                object.name = modalName;
+                this.scene?.add(object);
+                console.log('模型渲染时间', object, new Date().getTime() - b);
+
+                resolve(gltf);
+                dracoLoader.dispose();
+              },
+              (err) => {
+                console.log(err);
+              }
+            );
           } catch (error) {
             console.log(error);
           }
-        } 
+        }
       } catch (error) {
-        reject('加载模型出错')
+        reject('加载模型出错');
       }
-    })
+    });
   }
 
   setTestPlane() {
-			const plane = new THREE.Mesh(
-				new THREE.PlaneGeometry( 100, 100 ),
-				new THREE.MeshPhongMaterial( { color: 0x999999, specular: 0x101010 } )
-			);
-			plane.rotation.x = - Math.PI / 2;
-			plane.position.y = 0.03;
-			plane.receiveShadow = true;
-			// this.scene?.add( plane );
+    const plane = this.track(new THREE.Mesh(new THREE.PlaneGeometry(100, 100), new THREE.MeshPhongMaterial({ color: 0x999999, specular: 0x101010 })));
+    plane.rotation.x = -Math.PI / 2;
+    plane.position.y = 0.03;
+    plane.receiveShadow = true;
+    this.scene?.add(plane);
+  }
+
+  removeSawtooth() {
+    this.composer = this.track(new EffectComposer(this.renderer as THREE.WebGLRenderer));
+    const FXAAShaderPass = this.track(new ShaderPass(FXAAShader));
+    FXAAShaderPass.uniforms['resolution'].value.set(1 / this.canvasContainer.clientWidth, 1 / this.canvasContainer.clientHeight);
+    FXAAShaderPass.renderToScreen = true;
+    this.composer.addPass(FXAAShaderPass);
   }
-  
+
   /* 自定义材质 */
-  setCustomMaterial(group){}
+  setCustomMaterial(group) {}
 
   /* 场景环境背景 */
   setEnvMap(hdr) {
-    // new RGBELoader().setPath('/public/3D/hdr/').load(hdr + '.jpeg', (texture) => {
+    // new RGBELoader().setPath('/public/model/hdr/').load(hdr + '.jpeg', (texture) => {
     //   debugger
     //   texture.mapping = THREE.EquirectangularReflectionMapping;
     //   (this.scene as THREE.Scene).background = texture;
     //   (this.scene as THREE.Scene).environment = texture;
     // });
-    new THREE.TextureLoader().setPath('/3D/hdr/').load(hdr + '.jpeg', (texture) => {
-      texture.mapping = THREE.EquirectangularReflectionMapping;
-      // (this.scene as THREE.Scene).background = new THREE.Color('#00000000');
-      (this.scene as THREE.Scene).environment = texture;
-    });
+    this.track(new THREE.TextureLoader())
+      .setPath('/model/hdr/')
+      .load(hdr + '.jpeg', (texture) => {
+        this.track(texture);
+        texture.mapping = THREE.EquirectangularReflectionMapping;
+        // (this.scene as THREE.Scene).background = texture;
+        (this.scene as THREE.Scene).environment = texture;
+      });
   }
 
   render() {
-    const delta = this.clock.getElapsedTime();
-    this.resetLookAt()
-    this.startMY()
-    this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
-    this.css3dRender?.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera)
-    this.animationMixer?.update(delta);
+    this.camera?.updateMatrixWorld();
+    this.stats?.update();
+    this.resetLookAt();
+    this.startMY();
+    this.startAnimation();
+    this.orbitControls?.update();
+    this.composer?.render();
+
+    // this.css3dRender?.render(this.scene as THREE.Scene, this.camera as THREE.PerspectiveCamera)
   }
 
   /* 实时物体刷新朝向 */
-  resetLookAt() {
-  }
+  resetLookAt() {}
   /* 漫游 */
   startMY() {}
+  /* 初始动画 */
+  startAnimation() {}
 
   animate() {
-    // this.renderer?.setAnimationLoop(this.render.bind(this));
-    if(this.animationId != -1) {
-      this.render()
-      this.animationId = requestAnimationFrame(this.animate.bind(this))
+    if (this.animationId != -1) {
+      setTimeout(() => {
+        this.animationId = requestAnimationFrame(this.animate.bind(this));
+      }, 1000 / 30);
+      this.renderer?.render(this.scene as THREE.Object3D, this.camera as THREE.Camera);
+      this.render();
+      // this.animationId = requestAnimationFrame(this.animate.bind(this));
+      // const T = this.clock.getDelta();
+      // this.timeS += T;
+      // if (this.timeS > 1 / this.FPS) {
+      //   this.render();
+      //   this.timeS = 0;
+      // }
     }
   }
+
   resizeRenderer() {
     // 更新相机比例
-    (this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
+    (this.camera as THREE.PerspectiveCamera).aspect = this.canvasContainer.clientWidth / this.canvasContainer.clientHeight;
     // 刷新相机矩阵
     this.camera?.updateProjectionMatrix();
     // 设置场景尺寸
-    this.renderer?.setSize(window.innerWidth, window.innerHeight);
-    this.css3dRender?.setSize(window.innerWidth, window.innerHeight);
+    this.renderer?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
+    // this.css3dRender?.setSize(this.canvasContainer.clientWidth, this.canvasContainer.clientHeight);
   }
   wheelRenderer(e: WheelEvent) {
     const timeScale = e.deltaY > 0 ? 1 : -1;
@@ -280,162 +295,86 @@ class UseThree {
       this.animationAction?.halt(0.5);
     }, 500);
   }
+
   deleteModal() {
     try {
-      
-      this.resourceTracker && this.resourceTracker.dispose()
-      this.renderer?.dispose()
-      this.renderer?.forceContextLoss()
-      // this.renderer?.domElement = null
-      this.renderer = null
-
-      // this.css3dRender?.domElement = null
+      const gl = this.renderer?.domElement?.getContext('webgl');
+      gl && gl.getExtension('WEBGL_lose_context')?.loseContext();
+
+      const gl1 = this.css3dRender?.domElement?.getContext('webgl');
+      gl1 && gl1.getExtension('WEBGL_lose_context')?.loseContext();
+      this.renderer?.forceContextLoss();
+      this.renderer?.dispose();
+      if (this.renderer) this.renderer.domElement = null;
+      this.renderer = null;
+
+      this.resourceTracker && this.resourceTracker.dispose();
+
+      if (this.canvasContainer) this.canvasContainer.innerHTML = '';
+      this.canvasContainer = null;
+      this.orbitControls = null;
+
       this.css3dRender = null;
+      this.camera = null;
+      this.clock = null;
 
       cancelAnimationFrame(this.animationId);
 
-      const gl = this.renderer?.domElement.getContext("webgl");
-      gl && gl?.getExtension("WEBGL_lose_context")?.loseContext();
+      this.animationId = -1;
 
-      const gl1 = this.css3dRender?.domElement.getContext("webgl");
-      gl1 && gl1?.getExtension("WEBGL_lose_context")?.loseContext();
+      this.scene = null;
+      this.mixers = [];
 
-      this.animationId = -1
-
-      this.resourceTracker?.untrack(this.resourceTracker.resources)
-      // this.scene = null
-      console.log('销毁场景',this.scene);
-      
+      console.log('销毁场景', this.scene);
     } catch (error) {
       console.log(error);
     }
-    // this.scene?.children.forEach((obj:any) => {
-    //   if(obj.type === 'Group'){
-    //     obj.traverse(function(item:any) {
-    //       if (item.type === 'Mesh') {
-    //         item.geometry.dispose();
-    //         item.material.dispose();
-    //         !!item.clear&&item.clear();
-    //       }
-    //     })
-    //   }else if (obj.material) {
-    //     obj.material.dispose();
-    //   }else if (obj.geometry) {
-    //     obj.geometry.dispose();
-    //   }
-    //   this.scene?.remove(obj)
-    //   !!obj.clear??obj.clear()
-    //   obj = null;
-    // })
-    // let gl = this.renderer?.domElement.getContext("webgl");
-    // gl && gl?.getExtension("WEBGL_lose_context")?.loseContext();
-
-    // this.renderer?.forceContextLoss();
-    // this.renderer?.dispose();
-    // this.camera = null;
-    // this.orbitControls = null;
-    // this.renderer.domElement = null;
-    // this.renderer = null;
-
-    // css3dRender.domElement = null;
-    // css3dRender = null;
-    // model3D.innerHTML = '';
-    // css3D.innerHTML = '';
-    // model3D = null
-    // css3D = null
-
-    // this.stats = null
-    // scene = null
-
-    THREE.Cache.clear();  
+    THREE.Cache.clear();
     console.log('3D环境已清理干净');
   }
 
+  deleteModal1() {
+    this.scene?.children.forEach((obj) => {
+      if (obj.type === 'Group') {
+        obj.traverse(function (item) {
+          if (item.type === 'Mesh') {
+            item.geometry.dispose();
+            item.material.dispose();
+            !!item.clear && item.clear();
+          }
+        });
+      } else if (obj.material) {
+        obj.material.dispose();
+      } else if (obj.geometry) {
+        obj.geometry.dispose();
+      }
+      this.scene?.remove(obj);
+      !!obj.clear ?? obj.clear();
+      obj = null;
+    });
 
-  /* 创建字体精灵 */
-  addTextSprite(message, parameters) {
-    if (parameters === undefined) parameters = {};
-
-    var fontface = parameters.hasOwnProperty("fontface") ?
-        parameters["fontface"] : "Arial";
-
-    /* 字体大小 */
-    var fontsize = parameters.hasOwnProperty("fontsize") ?
-        parameters["fontsize"] : 18;
-
-    /* 边框厚度 */
-    var borderThickness = parameters.hasOwnProperty("borderThickness") ?
-        parameters["borderThickness"] : 4;
-
-    /* 边框颜色 */
-    var borderColor = parameters.hasOwnProperty("borderColor") ?
-        parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
-
-    /* 背景颜色 */
-    var backgroundColor = parameters.hasOwnProperty("backgroundColor") ?
-        parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 };
-
-    /* 创建画布 */
-    var canvas = document.createElement('canvas');
-    var context = canvas.getContext('2d') as CanvasRenderingContext2D;
-    // canvas.width = 0
-    // canvas.height = 200
-
-    /* 字体加粗 */
-    context.font = "Bold " + fontsize + "px " + fontface;
-
-    /* 获取文字的大小数据,高度取决于文字的大小 */
-    var metrics = context.measureText(message);
-    var textWidth = metrics.width;
-
-    /* 背景颜色 */
-    context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + ","
-        + backgroundColor.b + "," + backgroundColor.a + ")";
-
-    /* 边框的颜色 */
-    context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + ","
-        + borderColor.b + "," + borderColor.a + ")";
-    context.lineWidth = borderThickness;
-
-    /* 绘制圆角矩形 */
-    this.roundRect(context, borderThickness / 2, borderThickness / 2, textWidth + borderThickness, fontsize * 1.4 + borderThickness, 6);
-    // this.roundRect(context, borderThickness / 2, borderThickness / 2, 10000, 10000, 6);
-    /* 字体颜色 */
-    context.fillStyle = "rgba(0, 0, 0, 1.0)";
-    context.fillText(message, borderThickness, fontsize + borderThickness);
-    // context.fillRect(borderThickness / 2, borderThickness / 2, 10000, 10000)
-    /* 画布内容用于纹理贴图 */
-    var texture = new THREE.Texture(canvas);
-    texture.needsUpdate = true;
-
-    var spriteMaterial = new THREE.SpriteMaterial({ map: texture, transparent:false });
-    var sprite = new THREE.Sprite(spriteMaterial);
-
-    /* 缩放比例 */
-    sprite.scale.set(10, 5, 1);
-
-    return sprite;
-  }
-
-  /* 绘制圆角矩形 */
-  roundRect(ctx, x, y, w, h, r) {
-
-    ctx.beginPath();
-    ctx.moveTo(x + r, y);
-    ctx.lineTo(x + w - r, y);
-    ctx.quadraticCurveTo(x + w, y, x + w, y + r);
-    ctx.lineTo(x + w, y + h - r);
-    ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
-    ctx.lineTo(x + r, y + h);
-    ctx.quadraticCurveTo(x, y + h, x, y + h - r);
-    ctx.lineTo(x, y + r);
-    ctx.quadraticCurveTo(x, y, x + r, y);
-    ctx.closePath();
-    ctx.fill();
-    ctx.stroke();
-
+    const gl = this.renderer?.domElement.getContext('webgl');
+    gl && gl.getExtension('WEBGL_lose_context')?.loseContext();
+
+    this.renderer?.forceContextLoss();
+    this.renderer?.dispose();
+    this.camera = null;
+    this.orbitControls = null;
+    if (this.renderer) this.renderer.domElement = null;
+    this.renderer = null;
+    if (this.css3dRender) this.css3dRender.domElement = null;
+    this.css3dRender = null;
+    if (this.canvasContainer) this.canvasContainer.innerHTML = '';
+    if (this.CSSCanvasContainer) this.CSSCanvasContainer.innerHTML = '';
+
+    !!this.scene?.clear ?? this.scene?.clear();
+    cancelAnimationFrame(this.animationId);
+    this.animationId = -1;
+    this.stats = null;
+    this.scene = null;
+    THREE.Cache.clear();
+    console.log('3D环境已清理干净');
   }
-
 }
 
 export default UseThree;

+ 7 - 6
src/hooks/system/useListPage.ts

@@ -64,10 +64,10 @@ export function useListPage(options: ListPageOptions) {
   // 导出 excel
   async function onExportXls() {
     //update-begin---author:wangshuai ---date:20220411  for:导出新增自定义参数------------
-    let { url, name, params } = options?.exportConfig ?? {};
-    let realUrl = typeof url === 'function' ? url() : url;
+    const { url, name, params } = options?.exportConfig ?? {};
+    const realUrl = typeof url === 'function' ? url() : url;
     if (realUrl) {
-      let title = typeof name === 'function' ? name() : name;
+      const title = typeof name === 'function' ? name() : name;
       //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出报错,原因未知-
       let paramsForm = {};
       try {
@@ -80,7 +80,7 @@ export function useListPage(options: ListPageOptions) {
       //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导出动态设置mainId
       if (params) {
         Object.keys(params).map((k) => {
-          let temp = (params as object)[k];
+          const temp = (params as object)[k];
           if (temp) {
             paramsForm[k] = unref(temp);
           }
@@ -100,9 +100,9 @@ export function useListPage(options: ListPageOptions) {
 
   // 导入 excel
   function onImportXls(file) {
-    let { url, success } = options?.importConfig ?? {};
+    const { url, success } = options?.importConfig ?? {};
     //update-begin-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
-    let realUrl = typeof url === 'function' ? url() : url;
+    const realUrl = typeof url === 'function' ? url() : url;
     if (realUrl) {
       return handleImportXls(file, realUrl, success || reload);
       //update-end-author:taoyan date:20220507 for: erp代码生成 子表 导入地址是动态的
@@ -304,6 +304,7 @@ export function useListTable(tableProps: TableProps): [
     onChange(...args) {
       selectedRowKeys.value = args[0];
       selectedRows.value = args[1];
+
       if (typeof rowSelection.onChange === 'function') {
         rowSelection.onChange(...args);
       }

+ 1 - 1
src/layouts/default/header/components/FullScreen.vue

@@ -1,5 +1,5 @@
 <template>
-  <Tooltip :title="getTitle" placement="bottomCenter" :mouseEnterDelay="0.5">
+  <Tooltip :title="getTitle" placement="bottom" :mouseEnterDelay="0.5">
     <span @click="toggle">
       <FullscreenOutlined v-if="!isFullscreen" />
       <FullscreenExitOutlined v-else />

+ 1 - 1
src/layouts/default/header/components/LockScreen.vue

@@ -1,5 +1,5 @@
 <template>
-  <Tooltip :title="t('layout.header.tooltipLock')" placement="bottomCenter" :mouseEnterDelay="0.5" @click="handleLock">
+  <Tooltip :title="t('layout.header.tooltipLock')" placement="bottom" :mouseEnterDelay="0.5" @click="handleLock">
     <LockOutlined />
   </Tooltip>
   <LockModal @register="register" />

+ 91 - 59
src/utils/threejs/ResourceTracker.js

@@ -1,64 +1,96 @@
 import * as THREE from 'three';
 
 export default class ResourceTracker {
-   constructor() {
-   	this.resources = new Set();
-   }
-   track(resource) {
-   	if (!resource) {
-   		return resource;
-   	}
+  constructor() {
+    this.resources = new Set();
+  }
+  track(resource) {
+    if (!resource) {
+      return resource;
+    }
 
-   	// handle children and when material is an array of materials or
-   	// uniform is array of textures
-   	if (Array.isArray(resource)) {
-   		resource.forEach(resource => this.track(resource));
-   		return resource;
-   	}
+    // handle children and when material is an array of materials or
+    // uniform is array of textures
+    if (Array.isArray(resource)) {
+      resource.forEach((resource) => this.track(resource));
+      return resource;
+    }
 
-   	if (resource.dispose || resource instanceof THREE.Object3D) {
-   		this.resources.add(resource);
-   	}
-   	if (resource instanceof THREE.Object3D) {
-   		this.track(resource.geometry);
-   		this.track(resource.material);
-   		this.track(resource.children);
-   	} else if (resource instanceof THREE.Material) {
-   		// We have to check if there are any textures on the material
-   		for (const value of Object.values(resource)) {
-   			if (value instanceof THREE.Texture) {
-   				this.track(value);
-   			}
-   		}
-   		// We also have to check if any uniforms reference textures or arrays of textures
-   		if (resource.uniforms) {
-   			for (const value of Object.values(resource.uniforms)) {
-   				if (value) {
-   					const uniformValue = value.value;
-   					if (uniformValue instanceof THREE.Texture ||
-   						Array.isArray(uniformValue)) {
-   						this.track(uniformValue);
-   					}
-   				}
-   			}
-   		}
-   	}
-   	return resource;
-   }
-   untrack(resource) {
-   	this.resources.delete(resource);
-   }
-   dispose() {
-   	for (const resource of this.resources) {
-   		if (resource instanceof THREE.Object3D) {
-   			if (resource.parent) {
-   				resource.parent.remove(resource);
-   			}
-   		}
-   		if (resource.dispose) {
-   			resource.dispose();
-   		}
-   	}
-   	this.resources.clear();
-   }
-}
+    if (resource.dispose || resource instanceof THREE.Object3D) {
+      this.resources.add(resource);
+    }
+    if (resource instanceof THREE.Object3D) {
+      this.track(resource.geometry);
+      this.track(resource.material);
+      this.track(resource.children);
+    } else if (resource instanceof THREE.Material) {
+      // We have to check if there are any textures on the material
+      for (const value of Object.values(resource)) {
+        if (value instanceof THREE.Texture) {
+          this.track(value);
+        }
+      }
+      // We also have to check if any uniforms reference textures or arrays of textures
+      if (resource.uniforms) {
+        for (const value of Object.values(resource.uniforms)) {
+          if (value) {
+            const uniformValue = value.value;
+            if (uniformValue instanceof THREE.Texture || Array.isArray(uniformValue)) {
+              this.track(uniformValue);
+            }
+          }
+        }
+      }
+    }
+    return resource;
+  }
+  untrack(resource) {
+    resource = null;
+    this.resources.delete(resource);
+  }
+  dispose() {
+    for (const resource of this.resources) {
+      try {
+        if (resource instanceof THREE.Object3D) {
+          if (resource.parent) {
+            resource.parent.remove(resource);
+          }
+        }
+        if (resource.dispose) {
+          resource.dispose();
+        }
+        this.untrack(resource);
+      } catch (error) {
+        console.log('资源删除失败------>', resource);
+      }
+    }
+    console.log('this.resources--------------->', this.resources);
+    this.resources.clear();
+  }
+  doDispose(obj) {
+    if (obj !== null) {
+      for (var i = 0; i < obj.children.length; i++) {
+        doDispose(obj.children[i]);
+      }
+      if (obj.geometry) {
+        obj.geometry.dispose();
+        obj.geometry = undefined;
+      }
+      if (obj.material) {
+        if (obj.material.materials) {
+          for (i = 0; i < obj.material.materials.length; i++) {
+            obj.material.materials[i].dispose();
+          }
+        } else {
+          obj.material.dispose();
+        }
+        obj.material = undefined;
+      }
+      if (obj.texture) {
+        obj.texture.dispose();
+        obj.texture = undefined;
+      }
+    }
+    obj = undefined;
+  }
+}

+ 56 - 32
src/utils/threejs/loadGltf.worker.js

@@ -1,44 +1,68 @@
-import * as THREE from 'three'
+import * as THREE from 'three';
 import Dexie from 'dexie';
 
-const db = new Dexie('DB')
+const db = new Dexie('DB');
 db.version(1).stores({
-    modal: "++id, modalName, modalVal",
-})
+  modal: '++id, modalName, modalVal',
+});
+self['modelLen'] = -1;
 
-self.addEventListener('message', function(e) {
+/* self.addEventListener(
+  'message',
+  async function (e) {
     const { data, message } = e.data;
-    if(message == 'end') {
-        // self.postMessage({message: 'end', data: null})
-        // self.close()
-    }else {
-        if(data) {
-            loadGltf(data)
+    if (message == 'load') {
+      self['modelLen']--;
+      if (data) {
+        const model = await loadGltf(data);
+        if (self['modelLen'] >= 0) {
+          self.postMessage({ message: 'save', data: model });
         }
+      }
+    } else if (message == 'start') {
+      self['modelLen'] += data + 1;
     }
-    
-}, false);
-
-function loadGltf(url){
-    const loader = new THREE.FileLoader(); 
+    if (self['modelLen'] == 0) {
+      self.postMessage({ message: 'end', data: null });
+      this.close();
+    }
+  },
+  false
+); */
+self.addEventListener(
+  'message',
+  async function (e) {
+    const { data, message } = e.data;
+    if (message == 'load') {
+      if (data) {
+        const model = await loadGltf(data);
+        self.postMessage({ message: 'end', data: model });
+      }
+    }
+  },
+  false
+);
 
-    loader.setPath( '/3d/glft/' );
-    loader.setResponseType( 'arraybuffer' );
-    loader.setRequestHeader( {} );
-    loader.setWithCredentials( false );
+function loadGltf(url) {
+  return new Promise((resolve, reject) => {
+    const loader = new THREE.FileLoader();
+    loader.setPath('/model/glft/');
+    loader.setResponseType('arraybuffer');
+    loader.setRequestHeader({});
+    loader.setWithCredentials(false);
     try {
-        loader.load(url, (data)=> {
-            const modalName = url.replace(/(.*\/)*([^.]+).*/ig,"$2")
-            
-            db.modal.add({
-                modalName: modalName,
-                modalVal: data
-            })
-            self.postMessage({message: 'end', data: null})
-            // self.close()
-        })
+      loader.load(url, async (data) => {
+        const modalName = url.replace(/(.*\/)*([^.]+).*/gi, '$2');
+        const model = {
+          modalName: modalName,
+          modalVal: data,
+        };
+        await db.modal.add(model);
+        self.postMessage({ message: 'save', data: model });
+        resolve(model);
+      });
     } catch (error) {
-        console.log(error);
+      reject(error);
     }
+  });
 }
-

+ 50 - 27
src/utils/threejs/main.worker.ts

@@ -1,38 +1,61 @@
-import modalWorker from "./loadGltf.worker?worker";
+import modalWorker from './loadGltf.worker?worker';
 import Dexie from 'dexie';
-import { useGlobSetting } from '/@/hooks/setting';
-const glob = useGlobSetting();
+import { useModelStore } from '/@/store/modules/threejs';
 
+export function initModalWorker() {
+  type model = {
+    modelName: string;
+    modelVal: any;
+  };
 
-const db:any = new Dexie('DB')
-window['CustomDB'] = db
+  const modalUrlArr = ['fm/fm-5.glb', 'fc/fc.glb'];
 
+  const db: any = new Dexie('DB');
+  window['CustomDB'] = db;
 
-export function initModalWorker () {
-
+  const modelStore = useModelStore();
   db.version(1).stores({
-    modal: "++id, modalName, modalVal",
-  })
-
-  const worker = new modalWorker()
-
-  worker.onmessage = async function(e) {
-    const { data, message } = e.data;
-    if(message === 'end'){
-      setTimeout(() => {
-        worker.terminate()
-      }, 10000)
-    }
-  }
+    modal: '++id, modalName, modalVal',
+  });
 
-  glob.modalUrlArr.forEach( async (url) => {    
-    const modalName = url.replace(/(.*\/)*([^.]+).*/ig,"$2")
-    const modalArr =  await db.modal.where('modalName').equals(modalName).toArray()
-    if(modalArr.length < 1) {
-      worker.postMessage({message: 'load', data: url})
+  new Promise(async (resolve) => {
+    const validateModelUrlArr: string[] = [];
+    for (let i = 0; i < modalUrlArr.length; i++) {
+      const url = modalUrlArr[i];
+      const modalName = url.replace(/(.*\/)*([^.]+).*/gi, '$2');
+      const modalArr = await db.modal.where('modalName').equals(modalName).toArray();
+      if (modalArr.length < 1) {
+        console.log('utl------>', url);
+        validateModelUrlArr.push(url);
+      } else {
+        if (!modelStore.modelArr.get(modalName)) {
+          const model: model = {
+            modelName: modalName,
+            modelVal: modalArr[0].modalVal,
+          };
+          modelStore.setModel(model);
+        }
+      }
     }
+    resolve(validateModelUrlArr);
+  }).then((validateModelUrlArr: string[]) => {
+    validateModelUrlArr.forEach((url) => {
+      const worker = new modalWorker();
+
+      worker.onmessage = async function (e) {
+        const { data, message } = e.data;
+        if (message === 'end') {
+          setTimeout(() => {
+            if (data) {
+              modelStore.setModel(data);
+            }
+            worker.terminate();
+          }, 1000);
+        }
+      };
+      worker.postMessage({ message: 'load', data: url });
+    });
   });
-  
 }
 
-initModalWorker()
+initModalWorker();

+ 331 - 68
src/utils/threejs/util.ts

@@ -1,36 +1,89 @@
 import * as THREE from 'three';
 import { TransformControls } from 'three/examples/jsm/controls/TransformControls'; // 引入模块
-import * as dat from "dat.gui";
+import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
+import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
+import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
+import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
+import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
+import gsap from 'gsap';
+// import * as dat from "dat.gui";
 
-let modal, cubeList:THREE.Mesh[] = [], curve, line
+let modal,
+  cubeList: THREE.Mesh[] = [],
+  curve,
+  line;
 
-//创建gui对象
-const gui = new dat.GUI();
-gui.domElement.style.top = '300'
-gui.domElement.style.clientTop = 300
-gui.domElement.style.zIndex = 9999
+// //创建gui对象
+// const gui = new dat.GUI();
+// gui.domElement.style.top = '300'
+// gui.domElement.style.clientTop = 300
+// gui.domElement.style.zIndex = 9999
 
 // 画点
 const addCube = (initialPoints, scene) => {
-  return initialPoints.map(pos => {
+  return initialPoints.map((pos) => {
     const geometry = new THREE.BoxBufferGeometry(1, 1, 1);
-      const material = new THREE.MeshBasicMaterial({
-        color: 0xffffff,
-        side: THREE.DoubleSide
-      });
-      const cube = new THREE.Mesh(geometry, material);
-      cube.position.copy(pos);
-      scene.add(cube);
-      cubeList.push(cube)
-      // gui.add(cube.position, 'x', -1000, 1000)
-      // gui.add(cube.position, 'y', -1000, 1000)
-      // gui.add(cube.position, 'z', -1000, 1000)
+    const material = new THREE.MeshBasicMaterial({
+      color: 0xffffff,
+      side: THREE.DoubleSide,
+    });
+    const cube = new THREE.Mesh(geometry, material);
+    cube.position.copy(pos);
+    scene.add(cube);
+    cubeList.push(cube);
+    // gui.add(cube.position, 'x', -1000, 1000)
+    // gui.add(cube.position, 'y', -1000, 1000)
+    // gui.add(cube.position, 'z', -1000, 1000)
+  });
+};
+
+// 获取点击位置
+const addChangeEvent = () => {
+  // debugger
+  const control = new TransformControls(modal.camera, modal.renderer.domElement);
+  // 获取点击位置
+  const mouse = new THREE.Vector2();
+  window.addEventListener(
+    'mousedown',
+    (event) => {
+      // debugger
+      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
+      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
+      // mouse.x = ((event.clientX - dom.offsetLeft) / dom.clientWidth) * 2 - 1; // dom.offsetLeft -- dom元素距离浏览器左侧的距离   dom.clientWidth -- dom元素宽度
+      // mouse.y = -((event.clientY - dom.offsetTop) / dom.clientHeight) * 2 + 1; // dom.offsetTop -- dom元素距离浏览器顶部的距离    dom.clientHeight -- dom元素高度
+      //根据照相机,把这个向量转换到视点坐标系
+      const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(modal.camera);
+
+      // 方块点击检测
+      // const rayCaster = new THREE.Raycaster();
+      //在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。
+      const rayCaster = new THREE.Raycaster(modal.camera.position, vector.sub(modal.camera.position).normalize());
+      // rayCaster.setFromCamera(mouse, modal.camera);
+      const intersects = rayCaster.intersectObjects(cubeList);
+      if (intersects.length) {
+        const target = intersects[0].object;
+        control.attach(target); // 绑定controls和方块
+        modal.scene.add(control);
+      }
+    },
+    false
+  );
+
+  // 修改曲线后同步修改实体线条
+  control.addEventListener('dragging-changed', (event) => {
+    if (!event.value) {
+      console.log(event.value);
+
+      const points = curve.getPoints(50);
+      line.geometry.setFromPoints(points);
+    }
   });
-}
+};
+
 // 画线
 export const drawLine = (initialPoints, obj) => {
-  modal = obj
-  addCube(initialPoints, modal.scene)
+  modal = obj;
+  addCube(initialPoints, modal.scene);
   curve = new THREE.CatmullRomCurve3(
     cubeList.map((cube) => cube.position) // 直接绑定方块的position以便后续用方块调整曲线
   );
@@ -38,56 +91,266 @@ export const drawLine = (initialPoints, obj) => {
   curve.closed = false; // 曲线是否闭合
 
   const points = curve.getPoints(50); // 50等分获取曲线点数组
-  line = new THREE.LineLoop(
-      new THREE.BufferGeometry().setFromPoints(points),
-      new THREE.LineBasicMaterial({ color: 0xffffff,
-        linewidth: 10 })
-  ); // 绘制实体线条,仅用于示意曲线,后面的向量线条同理,相关代码就省略了
+  line = new THREE.LineLoop(new THREE.BufferGeometry().setFromPoints(points), new THREE.LineBasicMaterial({ color: 0xffffff, linewidth: 10 })); // 绘制实体线条,仅用于示意曲线,后面的向量线条同理,相关代码就省略了
 
   modal.scene.add(line);
 
-  addChangeEvent()
-}
+  addChangeEvent();
+};
 
-// 获取点击位置
-const addChangeEvent = () => {
-  debugger
-  const control = new TransformControls(modal.camera, modal.renderer.domElement);
-  // 获取点击位置
-  const mouse = new THREE.Vector2();
-  window.addEventListener(
-      'click',
-      (event) => {
-        debugger
-          mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
-          mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
-          // mouse.x = ((event.clientX - dom.offsetLeft) / dom.clientWidth) * 2 - 1; // dom.offsetLeft -- dom元素距离浏览器左侧的距离   dom.clientWidth -- dom元素宽度
-          // mouse.y = -((event.clientY - dom.offsetTop) / dom.clientHeight) * 2 + 1; // dom.offsetTop -- dom元素距离浏览器顶部的距离    dom.clientHeight -- dom元素高度
-          //根据照相机,把这个向量转换到视点坐标系
-          const vector = new THREE.Vector3(mouse.x, mouse.y, 0.5).unproject(modal.camera);
-          
-          // 方块点击检测
-          // const rayCaster = new THREE.Raycaster();
-          //在视点坐标系中形成射线,射线的起点向量是照相机, 射线的方向向量是照相机到点击的点,这个向量应该归一标准化。
-          const rayCaster = new THREE.Raycaster(modal.camera.position, vector.sub(modal.camera.position).normalize());
-          // rayCaster.setFromCamera(mouse, modal.camera);
-          const intersects = rayCaster.intersectObjects(cubeList);
-          if (intersects.length) {
-              const target = intersects[0].object;
-              control.attach(target); // 绑定controls和方块
-              modal.scene.add(control);
-          }
-      },
-      false
-  );
+/* 设置模型居中 */
+export const setModalCenter = (group) => {
+  const box3 = new THREE.Box3();
+  // 计算层级模型group的包围盒
+  // 模型group是加载一个三维模型返回的对象,包含多个网格模型
+  box3.expandByObject(group);
+  // 计算一个层级模型对应包围盒的几何体中心在世界坐标中的位置
+  const center = new THREE.Vector3();
+  box3.getCenter(center);
+  // console.log('查看几何体中心坐标', center);
+  // 重新设置模型的位置,使之居中。
+  group.position.x = group.position.x - center.x;
+  group.position.y = group.position.y - center.y;
+  group.position.z = group.position.z - center.z;
+};
 
-  // 修改曲线后同步修改实体线条
-  control.addEventListener('dragging-changed', (event) => {
-      if (!event.value) {
-          console.log(event.value);
+// 获取一个canvas 图文纹理
+export const getTextCanvas = (w, h, textArr, imgUrl) => {
+  // canvas 宽高最好是2的倍数
+  const width = w;
+  const height = h;
+  // 创建一个canvas元素 获取上下文环境
+  const canvas = document.createElement('canvas');
+  canvas.style.letterSpacing = 10 + 'px';
+  const ctx = canvas.getContext('2d') as CanvasRenderingContext2D;
+  canvas.width = width;
+  canvas.height = height;
+  // 设置样式
+  ctx.textAlign = 'start';
+  // ctx.fillStyle = 'rgba(44, 62, 80, 0.65)';
+  ctx.fillStyle = 'rgba(0, 0, 0, 0)';
+  // 创建渐变
+  // var gradient=ctx.createLinearGradient(0,0, canvas.width,0);
+  // gradient.addColorStop(0,"magenta");
+  // gradient.addColorStop(0.5,"blue");
+  // gradient.addColorStop(1.0,"red");
+  // // 用渐变填色
+  // ctx.fillStyle=gradient;
+
+  ctx.shadowColor = 'rgba(0, 10,0,0.8)';
+  ctx.shadowBlur = 4;
+  ctx.shadowOffsetX = 1;
+  ctx.shadowOffsetY = 1;
+
+  ctx.fillRect(0, 0, width, height);
+  //添加背景图片,进行异步,否则可能会过早渲染,导致空白
+  return new Promise((resolve, reject) => {
+    if (imgUrl) {
+      const img = new Image();
+
+      img.src = new URL('../../assets/images/vent/model_image/' + imgUrl, import.meta.url).href;
+      img.onload = () => {
+        //将画布处理为透明
+        ctx.clearRect(0, 0, width, height);
+        //绘画图片
+        ctx.drawImage(img, 0, 0, width, height);
+        ctx.textBaseline = 'middle';
+        // 由于文字需要自己排版 所以循环一下
+        // item 是自定义的文字对象 包含文字内容 字体大小颜色 位置信息等
+        textArr.forEach((item) => {
+          ctx.font = item.font;
+          ctx.fillStyle = item.color;
+          ctx.fillText(item.text, item.x, item.y, 1024);
+        });
+        resolve(canvas);
+      };
+      //图片加载失败的方法
+      img.onerror = (e) => {
+        reject(e);
+      };
+    } else {
+      //将画布处理为透明
+      ctx.clearRect(0, 0, width, height);
+      ctx.textBaseline = 'middle';
+
+      textArr.forEach((item) => {
+        ctx.lineWidth = 2;
+        ctx.font = item.font;
+        ctx.fillStyle = item.color;
+        !!item.strokeStyle && (ctx.strokeStyle = item.strokeStyle);
+        ctx.strokeText(item.text, item.x, item.y, 1024);
+        ctx.fillText(item.text, item.x, item.y, 1024);
+      });
+      resolve(canvas);
+    }
+  });
+};
+
+// 发光路径
+export const setLineGeo = (scene) => {
+  const box = new THREE.BoxGeometry(30, 30, 30);
+  // 立方体几何体box作为EdgesGeometry参数创建一个新的几何体
+  const edges = new THREE.EdgesGeometry(box);
+  // 立方体线框,不显示中间的斜线
+  new THREE.TextureLoader().setPath('/model/hdr/').load('8.png', (texture) => {
+    const edgesMaterial = new THREE.MeshBasicMaterial({
+      // color: 0x00ffff,
+      map: texture,
+      transparent: true,
+      depthWrite: false,
+    });
+
+    const line = new THREE.LineSegments(edges, edgesMaterial);
+    // 网格模型和网格模型对应的轮廓线框插入到场景中
+    scene.add(line);
+  });
+
+  debugger;
+  box.attributes.position.array;
+
+  const lightMaterial = new THREE.ShaderMaterial({
+    vertexShader: `varying vec3 vPosition;
+    varying vec2 vUv;
+    uniform float uTime;
+    
+    void main(){
+        // vec3 scalePosition = vec3(position.x+uTime,position.y,position.z+uTime);
+        vec4 viewPosition = viewMatrix * modelMatrix * vec4(position,1);
+        gl_Position = projectionMatrix *  viewPosition;
+        vPosition = position;
+        vUv = uv;
         
-          const points = curve.getPoints(50);
-          line.geometry.setFromPoints(points);
-      }
+    }`,
+    fragmentShader: `varying vec3 vPosition;
+    varying vec2 vUv;
+    uniform vec3 uColor;
+    uniform float uHeight;
+    vec4 permute(vec4 x)
+    {
+        return mod(((x*34.0)+1.0)*x, 289.0);
+    }
+
+    vec2 fade(vec2 t)
+    {
+        return t*t*t*(t*(t*6.0-15.0)+10.0);
+    }
+    float cnoise(vec2 P)
+    {
+        vec4 Pi = floor(P.xyxy) + vec4(0.0, 0.0, 1.0, 1.0);
+        vec4 Pf = fract(P.xyxy) - vec4(0.0, 0.0, 1.0, 1.0);
+        Pi = mod(Pi, 289.0); // To avoid truncation effects in permutation
+        vec4 ix = Pi.xzxz;
+        vec4 iy = Pi.yyww;
+        vec4 fx = Pf.xzxz;
+        vec4 fy = Pf.yyww;
+        vec4 i = permute(permute(ix) + iy);
+        vec4 gx = 2.0 * fract(i * 0.0243902439) - 1.0; // 1/41 = 0.024...
+        vec4 gy = abs(gx) - 0.5;
+        vec4 tx = floor(gx + 0.5);
+        gx = gx - tx;
+        vec2 g00 = vec2(gx.x,gy.x);
+        vec2 g10 = vec2(gx.y,gy.y);
+        vec2 g01 = vec2(gx.z,gy.z);
+        vec2 g11 = vec2(gx.w,gy.w);
+        vec4 norm = 1.79284291400159 - 0.85373472095314 * vec4(dot(g00, g00), dot(g01, g01), dot(g10, g10), dot(g11, g11));
+        g00 *= norm.x;
+        g01 *= norm.y;
+        g10 *= norm.z;
+        g11 *= norm.w;
+        float n00 = dot(g00, vec2(fx.x, fy.x));
+        float n10 = dot(g10, vec2(fx.y, fy.y));
+        float n01 = dot(g01, vec2(fx.z, fy.z));
+        float n11 = dot(g11, vec2(fx.w, fy.w));
+        vec2 fade_xy = fade(Pf.xy);
+        vec2 n_x = mix(vec2(n00, n01), vec2(n10, n11), fade_xy.x);
+        float n_xy = mix(n_x.x, n_x.y, fade_xy.y);
+        return 2.3 * n_xy;
+    }
+    
+    void main(){
+    
+       float strength = (vPosition.y+uHeight/2.0)/uHeight;
+        gl_FragColor = vec4(uColor,1.0 - strength);
+      // float strength =1.0 - abs(cnoise(vUv * 10.0)) ;
+      // gl_FragColor =vec4(strength,strength,strength,1);
+    }`,
+    transparent: true,
+    side: THREE.DoubleSide,
   });
-}
+  const lightMesh = new THREE.Mesh(box, lightMaterial);
+  lightMesh.geometry.computeBoundingBox();
+  const { min, max } = lightMesh.geometry.boundingBox;
+  const uHeight = max.y - min.y;
+  lightMaterial.uniforms.uHeight = {
+    value: uHeight,
+  };
+
+  lightMaterial.uniforms.uColor = {
+    value: new THREE.Color(0x00ff00),
+  };
+
+  lightMaterial.uniforms.uTime = {
+    value: 0,
+  };
+  // gsap.to(lightMesh.scale, {
+  //   // x: 2,
+  //   // z: 2,
+  //   y: 0.8,
+  //   duration: 1,
+  //   ease: 'none',
+  //   repeat: -1,
+  //   yoyo: true,
+  // });
+  scene.add(lightMesh);
+};
+
+export const setOutline = (model, group) => {
+  const { scene, renderer, camera } = model;
+  const params = {
+    edgeStrength: 10.0,
+    edgeGlow: 1,
+    edgeThickness: 1.0,
+    pulsePeriod: 5,
+    rotate: false,
+    usePatternTexture: false,
+  };
+
+  const composer = new EffectComposer(renderer);
+
+  const renderPass = new RenderPass(scene, camera);
+  composer.addPass(renderPass);
+
+  const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
+  composer.addPass(outlinePass);
+
+  outlinePass.visibleEdgeColor.set(parseInt(0xffffff));
+  outlinePass.hiddenEdgeColor.set('#190a05');
+  outlinePass.edgeStrength = params.edgeStrength;
+  outlinePass.edgeThickness = params.edgeThickness;
+  outlinePass.pulsePeriod = params.pulsePeriod;
+  outlinePass.usePatternTexture = params.usePatternTexture;
+
+  // const textureLoader = new THREE.TextureLoader();
+  // textureLoader.load('model/hdr/tri_pattern.jpg', function (texture) {
+  //   outlinePass.patternTexture = texture;
+  //   texture.wrapS = THREE.RepeatWrapping;
+  //   texture.wrapT = THREE.RepeatWrapping;
+  // });
+
+  const effectFXAA = new ShaderPass(FXAAShader);
+  effectFXAA.uniforms['resolution'].value.set(1 / window.innerWidth, 1 / window.innerHeight);
+  composer.addPass(effectFXAA);
+
+  const scale = 1;
+  group.traverse(function (child) {
+    if (child instanceof THREE.Mesh) {
+      // child.geometry.center();
+
+      child.geometry.computeBoundingSphere();
+    }
+  });
+
+  // group.scale.multiplyScalar(Math.random() * 0.3 + 0.1);
+  group.scale.divideScalar(scale);
+  return { outlinePass, composer };
+};

+ 58 - 61
src/views/demo/threejs/damper.vue

@@ -14,18 +14,19 @@
   const loading = ref(false);
   let model;
 
-
-  const setCustomMaterial = (group)=>{
-    const cityMaterial = model.track(new THREE.MeshBasicMaterial({
-      color: new THREE.Color(0x0c016f),
-    }));
+  const setCustomMaterial = (group) => {
+    const cityMaterial = model.track(
+      new THREE.MeshBasicMaterial({
+        color: new THREE.Color(0x0c016f),
+      })
+    );
     group?.traverse((item: THREE.Mesh) => {
       if (item.type === 'Mesh') {
         item.material = cityMaterial;
         setMaterial(item);
       }
     });
-  }
+  };
 
   /* 自定义材质 */
   const setMaterial = (obj) => {
@@ -45,7 +46,7 @@
       addLineSpread(shader);
       addToSpread(shader);
     };
-  }
+  };
   /* 自定义着色器 */
   const addGrad = (shader) => {
     shader.vertexShader = shader.vertexShader.replace(
@@ -89,7 +90,7 @@
       //#end#
       `
     );
-  }
+  };
   /* 自定义着色器 */
   const addRadiusSpread = (shader) => {
     // 设置扩散中心店
@@ -133,9 +134,9 @@
       ease: 'none',
       repeat: -1,
     });
-  }
+  };
   /* 自定义着色器 */
-  const addLineSpread = (shader)=>{
+  const addLineSpread = (shader) => {
     // 设置扩散中心店
     shader.uniforms.uLineSpreadCenter = {
       value: new THREE.Vector2(0, 0),
@@ -177,7 +178,7 @@
       ease: 'none',
       repeat: -1,
     });
-  }
+  };
   /* 自定义着色器 */
   const addToSpread = (shader) => {
     // 设置扩散中心店
@@ -221,105 +222,101 @@
       ease: 'none',
       repeat: -1,
     });
-  }
+  };
 
   /* 模型线框 */
-  const addLine = (geometry)=>{
+  const addLine = (geometry) => {
     const edges = new THREE.EdgesGeometry(geometry);
     const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0xff0000 }));
     (model.scene as THREE.Scene).add(line);
-  }
+  };
 
   const addLight = (scene) => {
-
-    const light = new THREE.PointLight( 0xffffff, 1, 1000 );
-    light.position.set( 50, 10, 50 );
+    const light = new THREE.PointLight(0xffffff, 1, 1000);
+    light.position.set(50, 10, 50);
     // scene.add( light );
 
-    const pointLight = new THREE.PointLight( 0xffffff, 1, 200 );
-    pointLight.position.set(0, 0, 10)
+    const pointLight = new THREE.PointLight(0xffffff, 1, 200);
+    pointLight.position.set(0, 0, 10);
     // scene.add( pointLight );
-    
-    const pointLight2 = new THREE.PointLight( 0xffffff, 1, 100 );
-    pointLight2.position.set(0, 3, 10)
+
+    const pointLight2 = new THREE.PointLight(0xffffff, 1, 100);
+    pointLight2.position.set(0, 3, 10);
     // light2.castShadow = true
-    pointLight2.shadow.bias = 0.05
-    scene.add(pointLight2)
+    pointLight2.shadow.bias = 0.05;
+    scene.add(pointLight2);
     // const pointLightHelper2 = new THREE.PointLightHelper( pointLight2, 1 );
     // scene.add( pointLightHelper2 );
 
-    const pointLight3 = new THREE.PointLight( 0xffffff, 1, 100 );
-    pointLight3.position.set(-80, 3, 10)
+    const pointLight3 = new THREE.PointLight(0xffffff, 1, 100);
+    pointLight3.position.set(-80, 3, 10);
     // light2.castShadow = true
-    pointLight3.shadow.bias = 0.05
-    scene.add(pointLight3)
+    pointLight3.shadow.bias = 0.05;
+    scene.add(pointLight3);
     // const pointLightHelper = new THREE.PointLightHelper( pointLight3, 1 );
     // scene.add( pointLightHelper );
 
-    const pointLight4 = new THREE.PointLight( 0xffffff, 1, 100 );
-    pointLight4.position.set(37, 3, 10)
+    const pointLight4 = new THREE.PointLight(0xffffff, 1, 100);
+    pointLight4.position.set(37, 3, 10);
     // light2.castShadow = true
-    pointLight4.shadow.bias = 0.05
-    scene.add(pointLight4)
+    pointLight4.shadow.bias = 0.05;
+    scene.add(pointLight4);
     // const pointLightHelper4 = new THREE.PointLightHelper( pointLight4, 1 );
     // scene.add( pointLightHelper4 );
 
-    const pointLight5 = new THREE.PointLight( 0xffffff, 1, 100 );
-    pointLight5.position.set(100, 3, 10)
+    const pointLight5 = new THREE.PointLight(0xffffff, 1, 100);
+    pointLight5.position.set(100, 3, 10);
     // light2.castShadow = true
-    pointLight5.shadow.bias = 0.05
-    scene.add(pointLight5)
+    pointLight5.shadow.bias = 0.05;
+    scene.add(pointLight5);
     // const pointLightHelper5 = new THREE.PointLightHelper( pointLight5, 1 );
     // scene.add( pointLightHelper5 );
 
- 
     pointLight2.shadow.mapSize.width = 10; // default
     pointLight2.shadow.mapSize.height = 10; // default
     pointLight2.shadow.camera.near = -0.0000001; // default
     pointLight2.shadow.camera.far = 20; // default
-  
+
     const spotLight = new THREE.SpotLight();
     spotLight.angle = Math.PI / 16;
     spotLight.penumbra = 0;
     spotLight.castShadow = true;
-    spotLight.intensity = 2
-    spotLight.position.set( -400, 400, 400 );
-    scene.add( spotLight );
+    spotLight.intensity = 2;
+    spotLight.position.set(-400, 400, 400);
+    scene.add(spotLight);
 
-    spotLight.shadow.mapSize.width = 2000;  // default
+    spotLight.shadow.mapSize.width = 2000; // default
     spotLight.shadow.mapSize.height = 1000; // default
-    spotLight.shadow.camera.near = 0.5;    // default
-    spotLight.shadow.camera.far = 800      // default
-    spotLight.shadow.focus = 1; 
-    spotLight.shadow.bias = -0.000001
-
-  }
+    spotLight.shadow.camera.near = 0.5; // default
+    spotLight.shadow.camera.far = 800; // default
+    spotLight.shadow.focus = 1;
+    spotLight.shadow.bias = -0.000001;
+  };
   const resetCamera = () => {
-    model.camera.position.setZ(200)
-    model.camera.position.setY(120)
-    model.camera?.lookAt( -100, 0.1, 50 );
+    model.camera.position.setZ(200);
+    model.camera.position.setY(120);
+    model.camera?.lookAt(-100, 0.1, 50);
     model.camera.updateProjectionMatrix();
-    model.orbitControls.update()
-  }
+    model.orbitControls.update();
+  };
 
   onMounted(() => {
     model = new UseThree('#damper3D');
     model.setEnvMap('test1');
-    model.setCustomMaterial = setCustomMaterial
+    model.setCustomMaterial = setCustomMaterial;
     loading.value = true;
-    model.setModel('fm-n-processed').then(() => {
-      addLight(model.scene)
-      resetCamera()
+    model.setModel('9f-processed').then(() => {
+      addLight(model.scene);
+      resetCamera();
       // 模型加载成功
       loading.value = false;
     });
   });
 
   onUnmounted(() => {
-    if(model){
-      model.deleteModal()
+    if (model) {
+      model.deleteModal();
     }
-  })
-
+  });
 </script>
 <style scoped lang="scss"></style>

+ 2 - 2
src/views/sys/login/LoginForm.vue

@@ -117,8 +117,8 @@
   const rememberMe = ref(false);
 
   const formData = reactive({
-    account: 'admin',
-    password: '123456',
+    account: '',
+    password: '',
     inputCode: '',
   });
   const randCodeData = reactive({

+ 23 - 16
src/views/vent/monitorManager/comment/MonitorTable.vue

@@ -1,5 +1,5 @@
 <template>
-  <div>
+  <div class="vent-table">
     <BasicTable @register="registerTable" :rowSelection="rowSelection" :pagination="false" :loading="loading">
       <template #tableTitle>
         <div></div>
@@ -16,13 +16,12 @@
 
 <script lang="ts" name="system-user" setup>
   //ts语法
+  import { computed } from '@vue/reactivity';
   import { defineExpose, toRaw, watch, ref } from 'vue';
   import { BasicTable, TableAction } from '/@/components/Table';
   import { useListPage } from '/@/hooks/system/useListPage';
-  import { useMessage } from '/@/hooks/web/useMessage';
   import { getTableHeaderColumns, setWebColumnsKey } from '/@/hooks/web/useWebColumns';
-
-  const $message = useMessage();
+  import { findIndex } from 'lodash-es';
   const props = defineProps({
     columnsType: {
       type: String,
@@ -46,16 +45,14 @@
     title: {
       type: String,
     },
-
   });
+  const emits = defineEmits(['selectRow']);
   const dataTableSource = ref([]);
-  const loading = ref(true)
-
-
+  const loading = ref(true);
+  const tableMaxHeight = 150;
   watch(
     () => {
-      // loading.value = true
-      return props.dataSource
+      return props.dataSource;
     },
     (newVal) => {
       const list = [];
@@ -67,11 +64,11 @@
       }
 
       dataTableSource.value = list;
-      loading.value = false
+      loading.value = false;
     }
   );
   setWebColumnsKey(props.columnsType);
-  const columns = getTableHeaderColumns;
+  const columns = computed(() => getTableHeaderColumns);
   // 列表页面公共参数、方法
   const { prefixCls, tableContext, doRequest } = useListPage({
     designScope: props.designScope,
@@ -83,8 +80,9 @@
       size: 'small',
       useSearchForm: false,
       showTableSetting: false,
-      maxHeight: 150,
+      maxHeight: tableMaxHeight,
       bordered: false,
+      rowKey: 'id',
       actionColumn: {
         width: 180,
       },
@@ -96,8 +94,17 @@
 
   //注册table数据
   const [registerTable, { reload, setLoading }, { rowSelection, selectedRowKeys }] = tableContext;
+  rowSelection.type = 'radio';
+  rowSelection.onSelect = (record) => {
+    const index = findIndex(dataTableSource.value, (data: any) => {
+      return data.deviceID == record.deviceID;
+    });
+    console.log('选中行', index);
+
+    emits('selectRow', record, index);
+  };
 
-  rowSelection.type = 'radio'
+  rowSelection.selectedRowKeys = [0];
 
   function openDetail(record) {
     record;
@@ -116,7 +123,7 @@
    */
   function getActions(record) {
     return [
-    {
+      {
         label: '开启',
         onClick: openDetail.bind(null, record),
       },
@@ -153,7 +160,7 @@
   :deep(.ant-table-body) {
     height: auto !important;
   }
-  :deep(.jeecg-basic-table .ant-table-wrapper .ant-table-title){
+  :deep(.jeecg-basic-table .ant-table-wrapper .ant-table-title) {
     min-height: 0;
   }
 </style>

+ 30 - 32
src/views/vent/monitorManager/gateMonitor/detail.vue

@@ -1,36 +1,34 @@
 <template>
-    <div style="width: 100%; height: calc(100vh - 200px); display: flex; justify-content: center; align-items: center">
-      <a-spin :spinning="loading" />
-      <div id="fengmen3D" v-show="!loading"> </div>
-    </div>
-  </template>
-  
-  <script setup lang="ts">
-    import { ref, onMounted, onUnmounted } from 'vue';
-    import UseThree from '/@/hooks/core/useThree';
-    import * as THREE from 'three';
-    import gsap from 'gsap';
-  
-    const loading = ref(false);
-    let model;
+  <div style="width: 100%; height: calc(100vh - 200px); display: flex; justify-content: center; align-items: center">
+    <a-spin :spinning="loading" />
+    <div id="fengmen3D" v-show="!loading"> </div>
+  </div>
+</template>
 
-    onMounted(() => {
-      model = new UseThree('#fengmen3D');
-      // model.setEnvMap('test');
+<script setup lang="ts">
+  import { ref, onMounted, onUnmounted } from 'vue';
+  import UseThree from '/@/hooks/core/useThree';
+  import * as THREE from 'three';
+  import gsap from 'gsap';
+
+  const loading = ref(false);
+  let model;
+
+  onMounted(() => {
+    model = new UseThree('#fengmen3D');
+    // model.setEnvMap('test');
     //   model.setCustomMaterial = setCustomMaterial
-      loading.value = true;
-      model.setModel('9f-processed').then(() => {
-        // 模型加载成功
-        loading.value = false;
-      });
+    loading.value = true;
+    model.setModel('9f-processed').then(() => {
+      // 模型加载成功
+      loading.value = false;
     });
-  
-    onUnmounted(() => {
-      if(model){
-        model.deleteModal()
-      }
-    })
-  
-  </script>
-  <style scoped lang="scss"></style>
-  
+  });
+
+  onUnmounted(() => {
+    if (model) {
+      model.deleteModal();
+    }
+  });
+</script>
+<style scoped lang="scss"></style>

+ 214 - 346
src/views/vent/monitorManager/gateMonitor/index.vue

@@ -2,68 +2,71 @@
   <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden;">
     <a-spin :spinning="loading" />
     <div id="damper3D"  v-show="!loading" style="width: 100%; height: 100%;; position: absolute; overflow: hidden;"> </div>
-    <div id="damper3DCSS" v-show="!loading" style="width: 100%; height: 100%; top:0; left: 0; position: absolute; overflow: hidden;">
+    <!-- <div id="damper3DCSS" v-show="!loading" style="width: 100%; height: 100%; top:0; left: 0; position: absolute; overflow: hidden;">
       <div>
         <div ref="elementContent" class="elementContent">
-          <p><span class="data-title">压力(Pa):</span>{{monitorData.pressure}}</p>
-          <p><span class="data-title">动力源压力(MPa):</span>{{monitorData.powerPressure}}</p>
+          <p><span class="data-title">压力(Pa):</span>{{selectData.frontRearDP}}</p>
+          <p><span class="data-title">动力源压力(MPa):</span>{{selectData.sourcePressure}}</p>
           <p><span class="data-title">故障诊断:</span>
             <i
-              :class="{'state-icon': true, 'open': monitorData.messageBoxStatus, 'close': !monitorData.messageBoxStatus}"
-            ></i>{{monitorData.fault}}</p>
+              :class="{'state-icon': true, 'open': selectData.messageBoxStatus, 'close': !selectData.messageBoxStatus}"
+            ></i>{{selectData.fault}}</p>
         </div>
       </div>
-    </div>
+    </div> -->
   </div>
   <div class="scene-box">
     <div class="top-box">
       <div class="top-left row">
-        2-3煤辅运大巷水仓自动风门远程控制
+        井下风门远程集中管理
       </div>
       <div class="top-center row">
-          <div class="button-box">打开前门</div>
-          <div class="button-box">关闭前门</div>
-          <div class="button-box">打开后门</div>
-          <div class="button-box">关闭后门</div>
-          <div class="button-box">打开前后门</div>
-          <div class="button-box">关闭前后门</div>
-          <div class="button-box" @click="enterMY">漫游</div>
-        </div>
+        <div class="button-box" :class="{'button-disable': backDoorIsOpen}" @click="playAnimation(1)">打开前门</div>
+        <div class="button-box" :class="{'button-disable': backDoorIsOpen}" @click="playAnimation(2)">关闭前门</div>
+        <div class="button-box" :class="{'button-disable': frontDoorIsOpen}" @click="playAnimation(3)">打开后门</div>
+        <div class="button-box" :class="{'button-disable': frontDoorIsOpen}" @click="playAnimation(4)">关闭后门</div>
+        <div class="button-box" :class="{'button-disable': frontDoorIsOpen || backDoorIsOpen}" @click="playAnimation(5)">打开前后门</div>
+        <div class="button-box" :class="{'button-disable': (frontDoorIsOpen && !backDoorIsOpen) || (backDoorIsOpen && !frontDoorIsOpen)}" @click="playAnimation(6)">关闭前后门</div>
+        <!-- <div class="button-box" @click="enterMY">漫游</div> -->
+      </div>
       <div class="top-right row">
         <div class="control-type row">
           <div class="control-title">控制模式:</div>
-          <a-radio-group v-model:value="controlType">
-            <a-radio :value="1">就地</a-radio>
-            <a-radio :value="2">远程</a-radio>
+          <a-radio-group v-model:value="selectData.autoRoManual" @change="changeType">
+            <a-radio :value="`0`">就地</a-radio>
+            <a-radio :value="`1`">远程</a-radio>
           </a-radio-group>
         </div>
         <div class="run-type row">
           <div class="control-title">运行状态:</div>
           <a-radio-group v-model:value="controlType">
-            <a-radio :value="1">检修</a-radio>
+            <a-radio :value="`1`">检修</a-radio>
           </a-radio-group>
         </div>
         <div class="run-state row">
           <div class="control-title">网络状态:</div>
-          <a-radio-group v-model:value="controlType">
-            <a-radio :value="1">运行</a-radio>
+          <a-radio-group v-model:value="selectData.netStatus">
+            <a-radio :value="`1`">运行</a-radio>
           </a-radio-group>
         </div>
       </div>
       
     </div>
+    <div class="title-box">
+      2-2煤主辅三联巷自动风门
+    </div>
     <div class="tabs-box">
-      <a-tabs v-model:activeKey="activeKey">
+      <a-tabs v-model:activeKey="activeKey" @change="tabChange">
         <a-tab-pane key="1" tab="实时监测">
-            <MonitorTable class="monitor-table" columnsType="gate_monitor" :dataSource="dataSource" design-scope="gate-monitor" title="风门监测" />
+            <MonitorTable class="monitor-table" columnsType="gate_monitor" :dataSource="dataSource" design-scope="gate-monitor" @selectRow="getSelectRow" title="风门监测" />
         </a-tab-pane>
         <a-tab-pane key="2" tab="实时曲线图" force-render>
           <div class="tab-item" v-if="activeKey === '2'">
-            <Bar
+            <BarAndLine
               :chartData="dataSource"
               xAxisPropType="strname"
-              seriesPropType="sourcePressure"
-              height="200px"
+              :propTypeArr="propTypeArr"
+              height="100%"
               :option="option"
             />
           </div>
@@ -91,372 +94,237 @@
 
 <script setup lang="ts">
   import '/@/assets/less/modal.less';
-  import {onBeforeMount, computed, onUnmounted, onMounted, ref, Ref, reactive } from 'vue';
-  import Bar from '/@/components/chart/Bar.vue';
+  import {onBeforeMount, computed, onUnmounted, onMounted, ref, Ref, reactive, toRaw, nextTick } from 'vue';
+  import BarAndLine from '/@/components/chart/BarAndLine.vue';
   import MonitorTable from '../comment/MonitorTable.vue';
   import { initWebSocket, getRecordList } from '/@/hooks/web/useVentWebSocket';
-  import gsap from 'gsap';
-  import UseThree from '/@/hooks/core/useThree';
-  import * as THREE from 'three';
-  import { CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'
-     
+  import { mountedThree, addFmText, play, destroy} from './gate.threejs'
+  import { deviceControlApi } from '/@/api/vent/index';
+  import { message } from 'ant-design-vue';
+  // import gsap from 'gsap';
+  // import { flyLine } from '/@/utils/threejs/FlyLine'
+
+
+
+
   const elementContent = <Ref<HTMLElement>>ref()
   const activeKey = ref('1')
   const loading = ref(false);
-
-  // 模型对象、 文字对象
-  let model, fm1CSS3D, fm2CSS3D;
   
+  
+  const propTypeArr = [
+    {
+      name: '气源压力(MPa)',
+      type: 'line',
+      filed: 'sourcePressure'
+    },
+    {
+      name: '压差(Pa)',
+      type: 'line',
+      filed: 'frontRearDP'
+    },
+    {
+      name: '测试数据1',
+      type: 'bar',
+      filed: 'test'
+    }
+  ];
+
+  const frontDoorIsOpen = ref(false); //前门是否开启
+  const backDoorIsOpen = ref(false); //后门是否开启
+
   // 监测数据
-  const monitorData = reactive({
-    pressure: 25,
-    powerPressure: 30,
-    fault: '起源压力超限'
+  const selectData = reactive({
+    deviceID: '',
+    deviceType: '',
+    strname: '',
+    frontRearDP: '-', //压差
+    sourcePressure: '-', //气源压力
+    netStatus: '0', //通信状态
+    fault: '气源压力超限',
+    autoRoManual: 0
   })
 
+  const selectRowIndex = ref(0)
+
   //  实时监测数据
-  const dataSource = computed(() => {
-    return [...getRecordList()].reverse() || [];
-  });
+  const dataSource:any = computed(() => {
+    // const data = [...getRecordList()].reverse() || []
+    const data = [...getRecordList()] || []
+    
+    Object.assign(selectData, toRaw(data[selectRowIndex.value]))
+    // console.log(data);
+    
+    addFmText(selectData)
+    return data;
+  });  
 
   // echarts 图标样式
   const option = {
     grid: {
+      show: false,
       left: 60,
       right: 50,
       bottom: 20,
     },
+    xAxis: {
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '#ffffffcc'
+        }
+      },
+    },
     yAxis: {
       name: '气源压力(Pa)',
       nameTextStyle: {
         color: '#fff',
         fontSize: 14,
       },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '#ffffffcc'
+        }
+      },
+      // axisTick: {
+      //   show: false
+      // }
+      splitLine: {
+        show: false
+      }
     },
   }
+  
   // 设备数据
   const controlType = ref(1)
-  
-  // 打灯光
-  const addLight = (scene) => {
-
-
-      const pointLight2 = new THREE.PointLight( 0xffeeee, 0.9, 30 );
-      pointLight2.position.set(-2, 18.8, 10)
-      // light2.castShadow = true
-      pointLight2.shadow.bias = 0.05
-      scene.add(pointLight2)
-      // const pointLightHelper2 = new THREE.PointLightHelper( pointLight2, 1 );
-      // scene.add( pointLightHelper2 );
-   
-      pointLight2.shadow.mapSize.width = 10; // default
-      pointLight2.shadow.mapSize.height = 10; // default
-      pointLight2.shadow.camera.near = -0.0000001; // default
-      pointLight2.shadow.camera.far = 20; // default
-
-      const pointLight3 = new THREE.PointLight( 0xffffff, 2, 100 );
-      pointLight3.position.set(-145, 7, 42)
-      // light2.castShadow = true
-      pointLight3.shadow.bias = 0.05
-      scene.add(pointLight3)
-      // const pointLightHelper = new THREE.PointLightHelper( pointLight3, 1 );
-      // scene.add( pointLightHelper );
-    
-
-      const pointLight4 = new THREE.PointLight( 0xffeeee, 0.6, 100 );
-      pointLight4.position.set(-42, 8, 25.3)
-      // light2.castShadow = true
-      pointLight4.shadow.bias = 0.05
-      scene.add(pointLight4)
-      // const pointLightHelper4 = new THREE.PointLightHelper( pointLight4, 1 );
-      // scene.add( pointLightHelper4 );
-
-      const pointLight5 = new THREE.PointLight( 0xffffff, 0.8, 100 );
-      pointLight5.position.set(67, 49, -3)
-      // light2.castShadow = true
-      pointLight5.shadow.bias = 0.05
-      scene.add(pointLight5)
-      // const pointLightHelper5 = new THREE.PointLightHelper( pointLight5, 1 );
-      // scene.add( pointLightHelper5 );
-
-      const pointLight6 = new THREE.PointLight( 0xffdddd, 0.8, 100 );
-      pointLight6.position.set(36, 57, 0)
-      // light2.castShadow = true
-      pointLight6.shadow.bias = 0.05
-      scene.add(pointLight6)
-      // const pointLightHelper6 = new THREE.PointLightHelper( pointLight6, 1 );
-      // scene.add( pointLightHelper6 );
-
-      const pointLight7 = new THREE.PointLight( 0xff9999, 0.8, 100 );
-      pointLight7.position.set(-87, 36.6, 3)
-      // light2.castShadow = true
-      pointLight7.shadow.bias = 0.05
-      scene.add(pointLight7)
-      // const pointLightHelper7 = new THREE.PointLightHelper( pointLight7, 1 );
-      // scene.add( pointLightHelper7 );
-
-
-      const spotLight = new THREE.SpotLight();
-      spotLight.angle = Math.PI / 16;
-      spotLight.penumbra = 0;
-      spotLight.castShadow = true;
-      spotLight.intensity = 2
-      spotLight.position.set( -224, 492, 687 );
-      scene.add( spotLight );
 
-      spotLight.shadow.mapSize.width = 2000;  // default
-      spotLight.shadow.mapSize.height = 1000; // default
-      spotLight.shadow.camera.near = 0.5;    // default
-      spotLight.shadow.camera.far = 1500      // default
-      spotLight.shadow.focus = 1; 
-      spotLight.shadow.bias = -0.000002
-
-     
+  const tabChange = (activeKeyVal) => {
+    activeKey.value = activeKeyVal
   }
 
-  // 重置摄像头
-  const resetCamera = () => {
-    model.camera.position.set(30.328, 58.993, 148.315)
-    model.camera.rotation.set(-27.88, 14.35, 7.47)
-    model.orbitControls.update()
-    model.camera.updateProjectionMatrix();
-  }
-  // 设置模型位置
-  const setModalPosition = (group) => {
-    group.position.set(-35, 20, 9)
-    console.log(group);
+  // 切换检测数据
+  const getSelectRow = (selectRow, index) => {
+    selectRowIndex.value = index
+    loading.value = true
+    Object.assign(selectData, selectRow)
+    setTimeout(() => {
+      loading.value = false
+    }, 300)
   }
-  // 文字
-  const addFm1Text = () => {
-    const element = document.createElement('div')
-    element.className = 'elementTag'
-    element.innerHTML = `
-    <div class="elementContent">
-      <p>压力:26</p>
-      <p>排气温度:50%</p>
-    </div>
-  `
-    fm1CSS3D = new CSS3DObject(element)
-    fm1CSS3D.scale.set(0.11, 0.11, 0.11)
-    fm1CSS3D.position.set( -45, 50, 10)
-    fm1CSS3D.lookAt(model.camera.position.clone())
-    // model?.scene.add(fm1CSS3D)
+  
+  // 播放动画
+  const playAnimation = (handlerState) => {
+    const data = {
+      deviceid: selectData.deviceID,
+      devicetype: selectData.deviceType,
+      paramcode: '',
+      value: null,
+      autoRoManual: selectData.autoRoManual
+    };
+    let handler = () => {};
+    switch (handlerState) {
+      case 1: // 打开前门
+        if (!frontDoorIsOpen.value && !backDoorIsOpen.value) {
+          handler = () => {
+            frontDoorIsOpen.value = true;
+          };
+          data.paramcode = 'frontGateOpen_S';
+        }
+        break;
+      case 2: // 关闭前门
+        if (frontDoorIsOpen.value && !backDoorIsOpen.value) {
+          handler = () => {
+            frontDoorIsOpen.value = false;
+          };
+          data.paramcode = 'frontGateClose_S';
+        }
+        break;
+      case 3: // 打开后门
+        if (!backDoorIsOpen.value && !frontDoorIsOpen.value) {
+          handler = () => {
+            backDoorIsOpen.value = true;
+          };
+          data.paramcode = 'rearGateOpen_S';
+        }
+        break;
+      case 4: // 关闭后门
+        if (backDoorIsOpen.value && !frontDoorIsOpen.value) {
+          handler = () => {
+            backDoorIsOpen.value = false;
+          };
+          data.paramcode = 'rearGateClose_S';
+        }
+        break;
+      case 5: // 打开前后门
+        if (!frontDoorIsOpen.value && !backDoorIsOpen.value) {
+          handler = () => {
+            frontDoorIsOpen.value = true;
+            backDoorIsOpen.value = true
+          };
+          data.paramcode = 'sameTimeOpen';
+        }
+        break;
+      case 6: // 关闭前后门
+        if (frontDoorIsOpen.value && backDoorIsOpen.value) {
+          handler = () => {
+            frontDoorIsOpen.value = false;
+            backDoorIsOpen.value = false;
+          };
+          data.paramcode = 'sameTimeClose';
+        }
+        break;
+    }
 
-  //   const element1 = document.createElement('div')
-  //   element1.className = 'elementTag'
-  //   element1.innerHTML = `
-  //   <div class="elementContent">
-  //     <p>压力(Pa):26</p>
-  //     <p>动力源压力(MPa):100KPa</p>
-  //     <p>故障诊断:起源压力超限</p>
-  //   </div>
-  // `
-    
-    fm2CSS3D = new CSS3DObject(elementContent.value)
-    fm2CSS3D.scale.set(0.12, 0.12, 0.12)
-    fm2CSS3D.position.set( 0, 45, 0)
-    fm2CSS3D.lookAt(model.camera.position.clone())
-    model?.scene.add(fm2CSS3D)
-  }
-  // 路线
-  const createLine = ()=>{
-    const position = model.camera.position.clone()
-		//创建样条曲线,作为运动轨迹
-		const curve = new THREE.CatmullRomCurve3([
-      new THREE.Vector3(position.x, position.y, position.z),
-      new THREE.Vector3(26.586, 17.860, 14.144),
-      new THREE.Vector3(-0.075, 15.669, 15.051),
-      new THREE.Vector3(-154.882, 17.462, 14.981),
-      // new THREE.Vector3(76, 28, 27),
-    ])
-    const geometry = new THREE.BufferGeometry().setFromPoints(curve.getPoints(5000))
-    // 材质对象
-    var material = new THREE.LineBasicMaterial({
-      color: 'red'
-    })
-    // 线条模型对象
-    var line = new THREE.Line(geometry, material)
-    // model?.scene.add(line) // 线条对象添加到场景中
-    return curve
-	}
-  // 漫游
-  const enterMY = () => {
-    model.camera.position.set(114.270, 15.293, 14.189)
-    model.camera.rotation.set(-86.23, 69.89, 85.98)
-    const curve = createLine()
-    let progress = 0
-    model.startMY = () => {
-      if (progress <= 1 - 0.004 * 20){
-        const point = curve.getPointAt(progress) //获取样条曲线指定点坐标,作为相机的位置
-        const pointBox = curve.getPointAt(progress + 0.004 * 20) //获取样条曲线指定点坐标
-        model.camera.position.set(point.x, point.y, point.z)
-        model.camera.lookAt(pointBox.x + 5, pointBox.y, pointBox.z)
-        model.orbitControls.position0.set(point.x, point.y, point.z) //非必要,场景有控件时才加上
-        model.orbitControls.target.set(pointBox.x, pointBox.y , pointBox.z) //非必要,场景有控件时才加上
-        progress += 0.004
-        console.log(progress);
-      } else {
-        // progress = 0
-        model.camera.position.set(30.328, 58.993, 148.315)
-        model.camera.rotation.set(-27.88, 14.35, 7.47)
-        model.camera.lookAt(0,0,0)
-        model.startMY = () => {}
-      }
+    if (data.paramcode) {
+      deviceControlApi(data)
+        .then((res) => {
+          if (res.success) {
+          }
+        })
+        .finally(() => {   
+          handler();
+          play(handlerState)
+        });
     }
-    
+  };
+
+  // 远程、就地切换
+  const changeType = () => {
+    const data = {
+      deviceid: selectData.deviceID,
+      devicetype: selectData.deviceType,
+      paramcode: 'autoRoManual',
+      value: selectData.autoRoManual
+    };
+    deviceControlApi(data).then(() => {
+      message.success('状态切换成功!')
+    })
   }
   
+  
   onBeforeMount(() => {
     const sendVal = JSON.stringify({ pagetype: 'normal', devicetype: 'gate', orgcode: '', ids: '', systemID: '' });
     initWebSocket(sendVal);
-  });
-
+  }); 
+  
   onMounted(() => {
-    model = new UseThree('#damper3D', '#damper3DCSS');
-    model.setEnvMap('test1');
     loading.value = true;
-    model.setModel('fm-n-processed').then((group) => {
-      addLight(model.scene)
-      resetCamera();
-      setModalPosition(group)
-      addFm1Text()
-
-      model.resetLookAt = () => {
-        fm1CSS3D.lookAt(model?.camera?.position.clone())
-        fm2CSS3D.lookAt(model?.camera?.position.clone())
-      }
-      loading.value = false;
-      // 测试
-      // setTimeout(() => {
-      //   monitorData.pressure = 29
-      // }, 1000)
-    });
+    mountedThree().then(() => {
+      // addFmText(selectData)
+      nextTick(() => {
+        loading.value = false;
+      })
+    })
   });
 
   onUnmounted(() => {
-    if(model){
-      model.deleteModal()
-    }
+    destroy()
   })
 
 </script>
 
 <style  lang="less" scoped>
-.scene-box{
-    width: 100%;
-    height: 100%;
-    position: absolute;
-    z-index: 2;
-    left: 0;
-    top: 0;
-    pointer-events: none;
-    display: flex;
-    flex-direction: column;
-    justify-items: center;
-    overflow: hidden;
-    .top-box{
-      position: absolute;
-      top: 0;
-      width: 100%;
-      height: 50px;
-      background-image: linear-gradient(to right, #046AD500, #006effe7, #046AD500);
-      display: flex;
-      align-items: center;
-      pointer-events: auto;
-      .row{
-        color: #fff;
-        display: flex;
-      }
-      .top-left{
-        width: 400px;
-        height: 100%;
-        font-size: 16px;
-        display: flex;
-        align-items: center;
-        padding-left: 20px;
-        background: url('/@/assets/images/vent/tj.png') no-repeat;
-        background-position: center right;
-        background-size: auto 100% ;
-        cursor: pointer;
-        padding-right: 80px;
-      }
-      .top-center{
-        flex: 2.2;
-        justify-content: space-around;
-      }
-      .top-right{
-        flex: 1.8;
-        justify-content: center;
-        .run-type{  
-          margin: 0 10px;
-        }
-        .control-title{
-          color: rgb(0, 255, 242)
-        }
-      }
-      .button-box{
-        position: relative;
-        padding: 5px;
-        border:1px transparent solid;
-        // background-clip: border-box;
-        border-radius: 5px;
-        margin-left: 8px;
-        width: auto;
-        height: 40px;
-        border: 1px solid #65DBEA;     
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        color: #fff;
-        padding: 0 15px;
-        cursor:pointer;
-        &:hover{
-          background: linear-gradient( #3eb2ff55, #00c3ff55);
-        }
-        &::before {
-          width: calc(100% - 6px);
-          height: 32px;
-          content: '';
-          position: absolute;
-          top: 3px;
-          right: 0;
-          left: 3px;
-          bottom: 0;
-          z-index: -1;
-          border-radius: inherit; /*important*/
-          background: linear-gradient( #014978, #3BF3FC);
-        }
-        &::after {
-          width: calc(100% + 32px);
-          height: 10px;
-          content: '';
-          position: absolute;
-          top: 40px;
-          right: 0;
-          left: -16px;
-          bottom: 0;
-          z-index: -1;
-          border-radius: inherit; /*important*/
-          background: url('/@/assets/images/vent/short-light.png') no-repeat;
-          background-position: center;
-          background-size:100%;
-          z-index: 999;
-        } 
-      }
-    }
-    .tabs-box{
-      position: fixed;
-      bottom: 0;
-      height: 265px;
-      pointer-events: auto;
-      background-color: #ffffff11;
-      backdrop-filter: blur(10px);
-      .tab-item{
-        height: 100%;
-        color: #fff;
-      }
-    }
-}
 :deep(.jeecg-basic-table .ant-table-wrapper){
   background-color: #ffffff00;
 }
@@ -541,13 +409,13 @@
 }
 
 .elementContent{
-  background-color: rgb(20 143 221 / 68%);
+  background-color: rgb(20 143 221 / 40%);
   box-shadow: 0px 0px 12px rgb(0 128 255 / 75%);
   border: 1px solid rgb(127 177 255 / 75%);
-  padding: 10px 20px 0px 20px;
+  padding: 10px 20px 15px 20px;
   color: #efefef;
   p{
-    line-height: 1rem;
+    // line-height: 1rem;
   }
 }
 

+ 258 - 15
src/views/vent/monitorManager/windowMonitor/index.vue

@@ -1,33 +1,276 @@
 <template>
-  <BarMulti
-    :chartData="dataSource"
-    xAxisPropType="strname"
-    :propTypeArr="propTypeArr"
-    height="40vh"
-    :option="{
-      yAxis: {
-        name: '单位(m/min)',
-      },
-    }"
-  />
-  <MonitorTable columnsType="window_monitor" :dataSource="dataSource" design-scope="window-monitor" title="风窗监测" />
+  <div class="bg" style="width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; overflow: hidden">
+    <a-spin :spinning="loading" />
+    <div id="window3D" v-show="!loading" style="width: 100%; height: 100%; position: absolute; overflow: hidden"> </div>
+    <!-- <div id="damper3DCSS" v-show="!loading" style="width: 100%; height: 100%; top:0; left: 0; position: absolute; overflow: hidden;">
+      <div>
+        <div ref="elementContent" class="elementContent">
+          <p><span class="data-title">压力(Pa):</span>{{selectData.frontRearDP}}</p>
+          <p><span class="data-title">动力源压力(MPa):</span>{{selectData.sourcePressure}}</p>
+          <p><span class="data-title">故障诊断:</span>
+            <i
+              :class="{'state-icon': true, 'open': selectData.messageBoxStatus, 'close': !selectData.messageBoxStatus}"
+            ></i>{{selectData.fault}}</p>
+        </div>
+      </div>
+    </div> -->
+  </div>
+  <div class="scene-box">
+    <div class="top-box">
+      <div class="top-left row"> 井下风窗远程集中管理 </div>
+      <div class="top-center row">
+        <div class="input-box">
+          <span class="input-title">风窗角度:</span>
+          <a-input-number placeholder="0" :min="0" :max="90" :step="1" v-model:value="windowAngle" />
+        </div>
+        <div class="button-box" @click="playAnimation(1)">设定前窗面积</div>
+        <div class="button-box" @click="playAnimation(2)">设定后窗面积</div>
+        <div class="button-box" @click="playAnimation(2)" style="display: none">设定风窗面积</div>
+      </div>
+      <div class="top-right row">
+        <div class="control-type row">
+          <div class="control-title">控制模式:</div>
+          <a-radio-group v-model:value="controlType">
+            <a-radio :value="1">就地</a-radio>
+            <a-radio :value="2">远程</a-radio>
+          </a-radio-group>
+        </div>
+        <div class="run-type row">
+          <div class="control-title">运行状态:</div>
+          <a-radio-group v-model:value="controlType">
+            <a-radio :value="1">检修</a-radio>
+          </a-radio-group>
+        </div>
+        <div class="run-state row">
+          <div class="control-title">网络状态:</div>
+          <a-radio-group v-model:value="controlType">
+            <a-radio :value="1">运行</a-radio>
+          </a-radio-group>
+        </div>
+      </div>
+    </div>
+    <div class="title-box"> 2-2煤主辅三联巷自动风窗 </div>
+    <div class="tabs-box">
+      <a-tabs v-model:activeKey="activeKey" @change="tabChange">
+        <a-tab-pane key="1" tab="实时监测">
+          <MonitorTable columnsType="window_monitor" :dataSource="dataSource" @selectRow="getSelectRow" design-scope="window-monitor" title="风窗监测" />
+        </a-tab-pane>
+        <a-tab-pane key="2" tab="实时曲线图" force-render>
+          <div class="tab-item" v-if="activeKey === '2'">
+            <BarMulti :chartData="dataSource" xAxisPropType="strname" :propTypeArr="propTypeArr" height="100%" :option="option" />
+          </div>
+        </a-tab-pane>
+        <a-tab-pane key="3" tab="历史数据">
+          <div class="tab-item"> Content of Tab Pane 2 </div>
+        </a-tab-pane>
+        <a-tab-pane key="4" tab="操作历史">
+          <div class="tab-item"> Content of Tab Pane 2 </div>
+        </a-tab-pane>
+        <a-tab-pane key="5" tab="实时报警">
+          <div class="tab-item"> Content of Tab Pane 2 </div>
+        </a-tab-pane>
+      </a-tabs>
+    </div>
+  </div>
 </template>
 
 <script setup lang="ts">
+  import '/@/assets/less/modal.less';
   import BarMulti from '/@/components/chart/BarMulti.vue';
-  import { onBeforeMount, computed } from 'vue';
+  import { onBeforeMount, computed, ref, onMounted, nextTick, onUnmounted, reactive } from 'vue';
   import MonitorTable from '../comment/MonitorTable.vue';
-  import { initWebSocket, getRecordList, closeWebSocket } from '/@/hooks/web/useVentWebSocket';
+  import { initWebSocket, getRecordList } from '/@/hooks/web/useVentWebSocket';
+  import { mountedThree, destroy, addFmText, play } from './window.threejs';
+  const activeKey = ref('1');
+  const loading = ref(false);
+  const windowAngle = ref(0);
+  const rotationParam = {
+    frontDeg0: 0, // 前门初始
+    frontDeg1: windowAngle.value, // 前门目标
+    backDeg0: 0, // 后门初始
+    backDeg1: windowAngle.value, // 后门目标
+  };
+
+  const option = {
+    grid: {
+      show: false,
+      left: 60,
+      right: 50,
+      bottom: 30,
+    },
+    xAxis: {
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '#ffffffcc',
+        },
+      },
+    },
+    yAxis: {
+      name: '单位(m/min)',
+      nameTextStyle: {
+        color: '#fff',
+        fontSize: 14,
+      },
+      axisLine: {
+        show: true,
+        lineStyle: {
+          color: '#ffffffcc',
+        },
+      },
+      splitLine: {
+        show: false,
+      },
+    },
+  };
+
   const dataSource = computed(() => {
     return [...getRecordList()].reverse() || [];
   });
+
   const propTypeArr = new Map([
     ['frontPresentValue', '前窗风速'],
     ['rearPresentValue', '后窗风速'],
   ]);
+
+  // 设备数据
+  const controlType = ref(1);
+
+  // 默认初始是第一行
+  const selectRowIndex = ref(0);
+
+  const tabChange = (activeKeyVal) => {
+    activeKey.value = activeKeyVal;
+  };
+
+  // 监测数据
+  const selectData = reactive({
+    deviceID: '',
+    deviceType: '',
+    strname: '',
+    frontRearDP: '-', //压差
+    sourcePressure: '-', //气源压力
+    netStatus: '0', //通信状态
+    fault: '气源压力超限',
+  });
+
+  // 切换检测数据
+  const getSelectRow = (selectRow, index) => {
+    selectRowIndex.value = index;
+    loading.value = true;
+    Object.assign(selectData, selectRow);
+    setTimeout(() => {
+      loading.value = false;
+    }, 300);
+  };
+
+  const playAnimation = (flag) => {
+    if (flag == 1) rotationParam.frontDeg1 = windowAngle.value;
+    if (flag == 2) rotationParam.backDeg1 = windowAngle.value;
+    play(rotationParam, flag).then(() => {
+      if (flag == 1) rotationParam.frontDeg0 = windowAngle.value;
+      if (flag == 2) rotationParam.backDeg0 = windowAngle.value;
+    });
+  };
+
   onBeforeMount(() => {
     const sendVal = JSON.stringify({ pagetype: 'normal', devicetype: 'window', orgcode: '', ids: '', systemID: '' });
     initWebSocket(sendVal);
   });
+  onMounted(() => {
+    loading.value = true;
+    mountedThree().then(() => {
+      nextTick(() => {
+        loading.value = false;
+        addFmText(selectData);
+      });
+    });
+  });
+  onUnmounted(() => {
+    destroy();
+  });
 </script>
-<style scoped lang="scss"></style>
+<style lang="less" scoped>
+  .input-box {
+    display: flex;
+    align-items: center;
+    .input-title {
+      color: rgb(0, 255, 242);
+      width: auto;
+    }
+    margin-right: 10px;
+  }
+
+  :deep(.jeecg-basic-table .ant-table-wrapper) {
+    background-color: #ffffff00;
+  }
+  :deep(.ant-tabs-bar) {
+    margin: 0;
+  }
+  :deep(.ant-table) {
+    background-color: #ffffff00 !important;
+    color: #fff;
+  }
+  :deep(.ant-table-header) {
+    background-color: transparent;
+    // height: 42px;
+  }
+  :deep(.ant-table-thead > tr > th) {
+    background-color: transparent;
+    border: none;
+  }
+  :deep(.ant-table-body > tr > th) {
+    background-color: transparent;
+    border: none;
+  }
+  :deep(.ant-table-body > tr > td) {
+    border: none;
+  }
+  :deep(.ant-table-fixed-header > .ant-table-content > .ant-table-scroll > .ant-table-body) {
+    background-color: #ffffff05;
+    margin-top: 8px;
+    &::-webkit-scrollbar {
+      display: none;
+    }
+  }
+
+  :deep(.jeecg-basic-table .ant-table-wrapper .ant-table-title) {
+    padding: 0;
+  }
+  :deep(.jeecg-basic-table-row__striped td) {
+    background-color: transparent;
+  }
+  :deep(.ant-table-tbody > tr:hover.ant-table-row > td) {
+    background-color: #ffffff22;
+  }
+  :deep(.ant-table-tbody > tr:hover.ant-table-row > th) {
+    background-color: #ffffff22;
+  }
+  :deep(.ant-table-thead > tr:hover.ant-table-row > td) {
+    background-color: #ffffff22;
+  }
+  :deep(.ant-table-tbody > tr.ant-table-row-selected td) {
+    background-color: #ffffff22;
+  }
+  :deep(.ant-table-tbody > tr > td) {
+    border-color: #ffffff22;
+  }
+  :deep(.ant-table-thead > tr > th:hover) {
+    background-color: transparent !important;
+  }
+  :deep(.ant-table-thead > tr > th) {
+    color: #fff;
+  }
+  :deep(.ant-table-fixed-header .ant-table-scroll .ant-table-header) {
+    background: #ffffff44;
+    position: relative;
+    z-index: 999;
+    padding: 4px 0 !important;
+    &::-webkit-scrollbar {
+      display: none;
+    }
+  }
+  :deep(.ant-tabs-nav) {
+    color: #fff;
+  }
+</style>

+ 2 - 1
tsconfig.json

@@ -40,7 +40,8 @@
     "build/**/*.ts",
     "build/**/*.d.ts",
     "mock/**/*.ts",
-    "vite.config.ts"
+    "vite.config.ts",
+    "src/**/*.glsl"
   ],
   "exclude": ["node_modules", "tests/server/**/*.ts", "dist", "**/*.js"]
 }

+ 2 - 2
types/config.d.ts

@@ -146,7 +146,7 @@ export interface GlobConfig {
   urlPrefix?: string;
   // Project abbreviation
   shortName: string;
-  modalUrlArr: Array;
+  modalUrlArr: string;
 }
 export interface GlobEnvConfig {
   // Site title
@@ -170,5 +170,5 @@ export interface GlobEnvConfig {
   // view url
   VITE_GLOB_ONLINE_VIEW_URL?: string;
   // 3D modal names arr
-  VITE_3D_MODAL_ARR: Array
+  VITE_3D_MODAL_ARR: string
 }

+ 2 - 0
types/module.d.ts

@@ -20,3 +20,5 @@ declare module 'virtual:*' {
   const result: any;
   export default result;
 }
+
+declare module "*.glsl";

+ 1 - 0
vite.config.ts

@@ -10,6 +10,7 @@ import { wrapperEnv } from './build/utils';
 import { createVitePlugins } from './build/vite/plugin';
 import { OUTPUT_DIR } from './build/constant';
 
+
 function pathResolve(dir: string) {
   return resolve(process.cwd(), '.', dir);
 }