Преглед изворни кода

Merge branch 'master' of http://39.97.59.228:8013/lxh/gas-injection

lxh пре 2 месеци
родитељ
комит
bf93854cec

+ 1 - 1
build/vite/plugin/theme.ts

@@ -7,7 +7,7 @@ import path from 'path';
 import { viteThemePlugin, antdDarkThemePlugin, mixLighten, mixDarken, tinycolor } from '@rys-fe/vite-plugin-theme';
 import { getThemeColors, generateColors } from '../../config/themeConfig';
 import { generateModifyVars } from '../../generate/generateModifyVars';
-import { antPrefixCls } from '/@/settings/designSetting'
+import { antPrefixCls } from '/@/settings/designSetting';
 
 export function configThemePlugin(isBuild: boolean): PluginOption[] {
   const colors = generateColors({

BIN
public/texture/3-2.png


+ 19 - 7
src/layouts/default/header/components/user-dropdown/index.vue

@@ -139,11 +139,23 @@
       }
 
       function clearModalCache() {
-        if (window['CustomDB']) {
-          const tables = window['CustomDB'].tables[0];
-          tables.clear();
-          location.reload();
-        }
+        return new Promise((resolve) => {
+          if (window['CustomDB']) {
+            // 清除指定表的所有数据
+            window['CustomDB'].modal
+              .clear()
+              .then(() => {
+                console.log('表数据已成功清除!');
+                resolve(null);
+                window.location.reload();
+              })
+              .catch((error) => {
+                console.error('清除数据时发生错误:', error);
+                resolve(null);
+                window.location.reload();
+              });
+          }
+        });
       }
 
       // 切换部门
@@ -159,7 +171,7 @@
         updatePasswordRef.value.show(userStore.getUserInfo.username);
       }
       // update-end--author:liaozhiyang---date:20230901---for:【QQYUN-6333】空路由问题—首次访问资源太大
-      function handleMenuClick(e: { key: MenuEvent }) {
+      async function handleMenuClick(e: { key: MenuEvent }) {
         switch (e.key) {
           case 'logout':
             handleLoginOut();
@@ -174,7 +186,7 @@
             clearCache();
             break;
           case 'modalCache':
-            clearModalCache();
+            await clearModalCache();
             break;
           case 'depart':
             updateCurrentDepart();

+ 91 - 0
src/utils/threejs/useEvent.ts

@@ -168,3 +168,94 @@ export default function useEvent() {
 
   return { mouseDownFn, mouseUpFn, mousemoveFn };
 }
+
+export function modelMouseHandler(modal: UseThree, group: THREE.Object3D | THREE.Object3D[], event: MouseEvent, callBack?: Function) {
+  if (!modal || !modal.canvasContainer || !modal.orbitControls) return;
+  const appStore = useAppStore();
+
+  const widthScale = appStore.getWidthScale;
+  const heightScale = appStore.getHeightScale;
+  // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
+  modal.mouse.x = ((-modal.canvasContainer.getBoundingClientRect().left + event.clientX) / (modal.canvasContainer.clientWidth * widthScale)) * 2 - 1;
+  modal.mouse.y =
+    -((-modal.canvasContainer.getBoundingClientRect().top + event.clientY) / (modal.canvasContainer.clientHeight * heightScale)) * 2 + 1;
+  (modal.rayCaster as THREE.Raycaster).setFromCamera(modal.mouse, modal.camera as THREE.Camera);
+  if (group) {
+    let intersects = <THREE.Intersection[]>[];
+    if (Object.prototype.toString.call(group) === '[object Array]') {
+      intersects = modal.rayCaster?.intersectObjects([...(group as THREE.Object3D[])], true) as THREE.Intersection[];
+    } else {
+      intersects = modal.rayCaster?.intersectObjects((group as THREE.Object3D).children, true) as THREE.Intersection[];
+    }
+    if (callBack) callBack(intersects);
+  }
+}
+
+export const mouseUpFn = (modal, point: THREE.Vector3, renderCallBack?) => {
+  const target0 = <THREE.Vector3>modal.orbitControls.target.clone();
+  const oldCameraPosition = <THREE.Vector3>modal.camera.position.clone();
+
+  const nor = point.clone().sub(oldCameraPosition.clone()).normalize();
+  const d = 1;
+
+  const animateObj = {
+    x1: target0.x, // 相机x
+    y1: target0.y, // 相机y
+    z1: target0.z, // 相机z
+    x2: oldCameraPosition.x, // 相机x
+    y2: oldCameraPosition.y, // 相机y
+    z2: oldCameraPosition.z, // 相机z
+  };
+  let newAnimateObj;
+
+  if (oldCameraPosition.z > target0.z) {
+    newAnimateObj = {
+      x1: point.x,
+      y1: point.y,
+      z1: point.z,
+      x2: point.x,
+      y2: Math.abs(nor.y) * d * 0.1 + point.y,
+      z2: Math.abs(nor.z) * d * 0.3 + point.z,
+    };
+  } else {
+    newAnimateObj = {
+      x1: point.x,
+      y1: point.y,
+      z1: point.z,
+      x2: point.x,
+      y2: Math.abs(nor.y) * d * 0.1 + point.y,
+      z2: -Math.abs(nor.z) * d * 0.3 + point.z,
+    };
+  }
+
+  modal.orbitControls.renderEnabled = false;
+  modal.orbitControls.enabled = false;
+  gsap.fromTo(
+    animateObj,
+    {
+      x1: target0.x, // 相机x
+      y1: target0.y, // 相机y
+      z1: target0.z, // 相机z
+      x2: oldCameraPosition.x, // 相机x
+      y2: oldCameraPosition.y, // 相机y
+      z2: oldCameraPosition.z, // 相机z
+    },
+    {
+      ...newAnimateObj,
+      duration: 1,
+      ease: 'easeInCirc',
+      onUpdate: function (object) {
+        modal.orbitControls.target.set(object.x1, object.y1, object.z1);
+        modal.camera.position.set(object.x2, object.y2, object.z2);
+        if (renderCallBack) renderCallBack();
+      },
+      onUpdateParams: [animateObj],
+      onComplete: function () {
+        modal.orbitControls.renderEnabled = true;
+        modal.orbitControls.enabled = true;
+      },
+    }
+  );
+
+  console.log('摄像机信息------->', modal.camera, modal.orbitControls);
+};

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

@@ -63,8 +63,8 @@
   const rememberMe = ref(false);
 
   const formData = reactive({
-    account: '',
-    password: '',
+    account: 'zhuqi',
+    password: 'zhuqi123zhuqi',
     inputCode: '',
   });
   const randCodeData = reactive({

+ 3 - 3
src/views/sys/login/useLogin.ts

@@ -4,8 +4,8 @@ import { ref, computed, unref, Ref } from 'vue';
 import { useI18n } from '/@/hooks/web/useI18n';
 import { checkOnlyUser } from '/@/api/sys/user';
 import { defHttp } from '/@/utils/http/axios';
-import { OAUTH2_THIRD_LOGIN_TENANT_ID } from "/@/enums/cacheEnum";
-import { getAuthCache } from "/@/utils/auth";
+import { OAUTH2_THIRD_LOGIN_TENANT_ID } from '/@/enums/cacheEnum';
+import { getAuthCache } from '/@/utils/auth';
 
 export enum LoginStateEnum {
   LOGIN,
@@ -184,7 +184,7 @@ export function sysOAuth2Login(source) {
   url += `?state=${encodeURIComponent(window.location.origin)}`;
   //update-begin---author:wangshuai ---date:20230224  for:[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------
   let tenantId = getAuthCache(OAUTH2_THIRD_LOGIN_TENANT_ID);
-  if(tenantId){
+  if (tenantId) {
     url += `&tenantId=${tenantId}`;
   }
   //update-end---author:wangshuai ---date:20230224  for:[QQYUN-3440]新建企业微信和钉钉配置表,通过租户模式隔离------------

+ 86 - 82
src/views/system/loginmini/MiniLogin.vue

@@ -1,6 +1,6 @@
 <template>
   <div :class="prefixCls" class="login-background-img">
-    <AppLocalePicker class="absolute top-4 right-4 enter-x xl:text-gray-600" :showText="false"/>
+    <AppLocalePicker class="absolute top-4 right-4 enter-x xl:text-gray-600" :showText="false" />
     <AppDarkModeToggle class="absolute top-3 right-7 enter-x" />
     <div class="aui-logo" v-if="!getIsMobile">
       <div>
@@ -89,7 +89,8 @@
                 <div class="aui-formButton">
                   <div class="aui-flex">
                     <a-button :loading="loginLoading" class="aui-link-login aui-flex-box" type="primary" @click="loginHandleClick">
-                      {{ t('sys.login.loginButton') }}</a-button>
+                      {{ t('sys.login.loginButton') }}</a-button
+                    >
                   </div>
                   <div class="aui-flex">
                     <a class="aui-linek-code aui-flex-box" @click="codeHandleClick">{{ t('sys.login.qrSignInFormTitle') }}</a>
@@ -163,8 +164,8 @@
   // import adTextImg from '/@/assets/loginmini/icon/jeecg_ad_text.png';
   import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application';
   import { useLocaleStore } from '/@/store/modules/locale';
-  import { useDesign } from "/@/hooks/web/useDesign";
-  import { useAppInject } from "/@/hooks/web/useAppInject";
+  import { useDesign } from '/@/hooks/web/useDesign';
+  import { useAppInject } from '/@/hooks/web/useAppInject';
   import { GithubFilled, WechatFilled, DingtalkCircleFilled, createFromIconfontCN } from '@ant-design/icons-vue';
 
   const IconFont = createFromIconfontCN({
@@ -387,7 +388,7 @@
    */
   function handleSuccess(value) {
     Object.assign(formData, value);
-    Object.assign(phoneFormData, { mobile: "", smscode: "" });
+    Object.assign(phoneFormData, { mobile: '', smscode: '' });
     type.value = 'login';
     activeIndex.value = 'accountLogin';
     handleChangeCheckCode();
@@ -448,11 +449,11 @@
     color: #aaa !important;
   }
 
-  :deep(.jeecg-dark-switch){
-    position:absolute;
+  :deep(.jeecg-dark-switch) {
+    position: absolute;
     margin-right: 10px;
   }
-  .aui-link-login{
+  .aui-link-login {
     height: 42px;
     padding: 10px 15px;
     font-size: 14px;
@@ -460,100 +461,103 @@
     margin-top: 15px;
     margin-bottom: 8px;
   }
-  .aui-phone-logo{
+  .aui-phone-logo {
     position: absolute;
     margin-left: 10px;
     width: 60px;
-    top:2px;
+    top: 2px;
     z-index: 4;
   }
-  .top-3{
+  .top-3 {
     top: 0.45rem;
   }
 </style>
 
 <style lang="less">
-@prefix-cls: ~'@{namespace}-mini-login';
-@dark-bg: #293146;
+  @prefix-cls: ~'@{namespace}-mini-login';
+  @dark-bg: #293146;
 
-html[data-theme='dark'] {
-  .@{prefix-cls} {
-    background-color: @dark-bg !important;
-    background-image: none;
+  html[data-theme='dark'] {
+    .@{prefix-cls} {
+      background-color: @dark-bg !important;
+      background-image: none;
 
-    &::before {
-      background-image: url(/@/assets/svg/login-bg-dark.svg);
-    }
-    .aui-inputClear{
-      background-color: #232a3b !important;
-    }
-    .ant-input,
-    .ant-input-password {
-      background-color: #232a3b !important;
-    }
+      &::before {
+        background-image: url(/@/assets/svg/login-bg-dark.svg);
+      }
+      .aui-inputClear {
+        background-color: #232a3b !important;
+      }
+      .ant-input,
+      .ant-input-password {
+        background-color: #232a3b !important;
+      }
 
-    .ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
-      border: 1px solid #4a5569 !important;
-    }
+      .ant-btn:not(.ant-btn-link):not(.ant-btn-primary) {
+        border: 1px solid #4a5569 !important;
+      }
 
-    &-form {
-      background: @dark-bg !important;
-    }
+      &-form {
+        background: @dark-bg !important;
+      }
 
-    .app-iconify {
-      color: #fff !important;
-    }
-    .aui-inputClear input,.aui-input-line input,.aui-choice{
-      color: #c9d1d9 !important;
-    }
+      .app-iconify {
+        color: #fff !important;
+      }
+      .aui-inputClear input,
+      .aui-input-line input,
+      .aui-choice {
+        color: #c9d1d9 !important;
+      }
 
-    .aui-formBox{
-      background-color: @dark-bg !important;
-    }
-    .aui-third-text span{
-      background-color: @dark-bg !important;
-    }
-    .aui-form-nav .aui-flex-box{
-      color: #c9d1d9 !important;
-    }
+      .aui-formBox {
+        background-color: @dark-bg !important;
+      }
+      .aui-third-text span {
+        background-color: @dark-bg !important;
+      }
+      .aui-form-nav .aui-flex-box {
+        color: #c9d1d9 !important;
+      }
 
-    .aui-formButton .aui-linek-code{
-      background:  @dark-bg !important;
-      color: white !important;
-    }
-    .aui-code-line{
-      border-left: none !important;
-    }
-    .ant-checkbox-inner,.aui-success h3{
-      border-color: #c9d1d9;
-    }
-    //update-begin---author:wangshuai ---date:20230828  for:【QQYUN-6363】这个样式代码有问题,不在里面,导致表达式有问题------------
-    &-sign-in-way {
-      .anticon {
-        font-size: 22px !important;
-        color: #888 !important;
-        cursor: pointer !important;
-
-        &:hover {
-          color: @primary-color !important;
+      .aui-formButton .aui-linek-code {
+        background: @dark-bg !important;
+        color: white !important;
+      }
+      .aui-code-line {
+        border-left: none !important;
+      }
+      .ant-checkbox-inner,
+      .aui-success h3 {
+        border-color: #c9d1d9;
+      }
+      //update-begin---author:wangshuai ---date:20230828  for:【QQYUN-6363】这个样式代码有问题,不在里面,导致表达式有问题------------
+      &-sign-in-way {
+        .anticon {
+          font-size: 22px !important;
+          color: #888 !important;
+          cursor: pointer !important;
+
+          &:hover {
+            color: @primary-color !important;
+          }
         }
       }
+      //update-end---author:wangshuai ---date:20230828  for:【QQYUN-6363】这个样式代码有问题,不在里面,导致表达式有问题------------
     }
-    //update-end---author:wangshuai ---date:20230828  for:【QQYUN-6363】这个样式代码有问题,不在里面,导致表达式有问题------------
-  }
 
-  input.fix-auto-fill,
-  .fix-auto-fill input {
-    -webkit-text-fill-color: #c9d1d9 !important;
-    box-shadow: inherit !important;
-  }
-  
-  .ant-divider-inner-text {
-    font-size: 12px !important;
-    color: @text-color-secondary !important;
-  }
-  .aui-third-login a{
-    background: transparent;
+    input.fix-auto-fill,
+    .fix-auto-fill input {
+      -webkit-text-fill-color: #c9d1d9 !important;
+      box-shadow: inherit !important;
+    }
+
+    .ant-divider-inner-text {
+      font-size: 12px !important;
+      color: @text-color-secondary !important;
+    }
+    .aui-third-login a {
+      background: transparent;
+    }
   }
-}
 </style>

+ 1 - 1
src/views/vent/home/configurable/components/gasInject/LineAndBarDetail.vue

@@ -33,7 +33,7 @@
       </div>
     </template>
     <template v-else>
-      <zqHistoryExport :tableColumn="tableColumn" @handleBack="handleBack"></zqHistoryExport>
+      <zqHistoryExport :tableColumn="tableColumn" @handleBack="handleBack" :deviceID="deviceID"></zqHistoryExport>
     </template>
   </div>
 </template>

+ 14 - 61
src/views/vent/home/configurable/components/gasInject/gasInject.api.ts

@@ -6,24 +6,10 @@ import { loginCipher } from '/@/settings/encryptionSetting';
 
 
 enum Api {
-getlist = '/safety/ventanalyDevicesetLog/list',
-devicecontrol='/safety/ventanalyMonitorData/devicecontrol',
-listdays = '/safety/ventanalyMonitorData/listdays',
-historydata='/safety/ventanalyMonitorData/export/historydata',
-
-getCameraUrl = '/monitor/camera/queryByCameraCode',
-//insertSyncRule = '/ventanaly-device/synccontrol/upcoming/saveOrUpdateRule',
-insertSyncRule = '/ventanaly-device/monitor/timeSync/insertSyncRuleClock',
-upcoming = '/ventanaly-device/synccontrol/upcoming',
-GetSyncRule='/ventanaly-device/synccontrol/upcoming/GetSyncRule',
-GetSyncRuleOperationLog='/ventanaly-device/synccontrol/upcoming/GetSyncRuleOperationLog',
-GetSyncRuleExecLog='/ventanaly-device/synccontrol/upcoming/GetSyncRuleExecLog',
-list='/safety/ventanalyDeviceInfo/list',
-edit='/ventanaly-device/safety/ventanalyDeviceInfo/batchEdit',
-manualTimeSync='/ventanaly-device/monitor/timeSync/manualTimeSync',
-isPasswordCurrect='/ventanaly-device/monitor/timeSync/isPasswordCurrect',
- editDoor='/safety/ventanalyDeviceInfo/edit'
-
+  getlist = '/safety/ventanalyDevicesetLog/list',
+  devicecontrol = '/safety/ventanalyMonitorData/devicecontrol',
+  listdays = '/safety/ventanalyMonitorData/listdays',
+  historydata = '/safety/ventanalyMonitorData/export/historydata',
 }
 
 
@@ -32,7 +18,7 @@ isPasswordCurrect='/ventanaly-device/monitor/timeSync/isPasswordCurrect',
 * 装备运行状态与控制-详情列表
 * @param params
 */
-export const getList = (params) => defHttp.get({ url: Api.getlist, params },{ joinParamsToUrl: true });
+export const getList = (params) => defHttp.get({ url: Api.getlist, params }, { joinParamsToUrl: true });
 
 
 /**
@@ -40,53 +26,20 @@ export const getList = (params) => defHttp.get({ url: Api.getlist, params },{ jo
 * @param params
 */
 export const devicecontrol = (params) => {
-// 加密password
-const encryption = new AesEncryption({ key: loginCipher.key, iv: loginCipher.iv });
-params.password = encryption.encryptByAES(params.password);
-return defHttp.put({ url: Api.devicecontrol, params });
+  // 加密password
+  const encryption = new AesEncryption({ key: loginCipher.key, iv: loginCipher.iv });
+  params.password = encryption.encryptByAES(params.password);
+  return defHttp.put({ url: Api.devicecontrol, params });
 };
 /**
 * 详情历史曲线
 * @param params
 */
-export const listdays = (params) => defHttp.get({ url: Api.listdays, params },{ joinParamsToUrl: true });
-
-export const cameraAddr = (params) => defHttp.get({ url: Api.getCameraUrl, params });
-
-/**
-* 详情历史-导出
-* @param params
-*/
-export const historydataExport = (params) => defHttp.get({ url: Api.historydata, params, responseType: 'blob' });
-
+export const listdays = (params) => defHttp.get({ url: Api.listdays, params }, { joinParamsToUrl: true });
 /**
-* 定时设置-读取规则
-* @param params
-*/
-export const GetSyncRule = (params) => defHttp.post({ url: Api.GetSyncRule, params });
-/**
-* 定时设置-修改规则
-* @param params
-*/
-export const insertSyncRule = (params) => {
-return defHttp.post({ url: Api.insertSyncRule, params });
-};
-
-//读取未来30秒内的预告
-export const upcoming = (params) => defHttp.get({ url: Api.upcoming, params });
+ * 导出详情历史
+ * @param params
+ */
+export const historydata = (params) => defHttp.get({ url: Api.historydata, params }, { joinParamsToUrl: true });
 
-//读取规则日志
-export const GetSyncRuleOperationLog = (params) => defHttp.get({ url: Api.GetSyncRuleOperationLog, params });
 
-//读取集控风门操作日志
-export const GetSyncRuleExecLog = (params) => defHttp.get({ url: Api.GetSyncRuleExecLog, params });
-//集控风门列表
-export const controlList = (params) => defHttp.get({ url: Api.list, params });
-//集控风门-确认选中
-export const confirmChoice = (params) => defHttp.put({ url: Api.edit, params });
-//同步PLC时钟
-export const manualTimeSync = () => defHttp.post({ url: Api.manualTimeSync });
-//设备集控选择密码设置
-export const isPasswordCurrect = (params) => defHttp.get({ url: Api.isPasswordCurrect,params });
-//风门勾选-分组
-export const editDoor = (params) => defHttp.put({ url: Api.editDoor, params });

+ 77 - 82
src/views/vent/home/configurable/components/gasInject/zqDetailCard.vue

@@ -9,100 +9,95 @@
         </div>
       </div>
     </div>
-
   </div>
 </template>
 
 <script setup lang="ts">
-import { ref } from 'vue'
-
-let props = defineProps({
-  //详情界面配置项
-  optionDetail: {
-    type: Array as any
-  },
-  //数据序列
-  dataIndex: {
-    type: Number
-  },
-  //分组数据
-  zqDetailData: {
-    type: Object as any
-  },
-
-})
+  import { ref } from 'vue';
+
+  let props = defineProps({
+    //详情界面配置项
+    optionDetail: {
+      type: Array as any,
+    },
+    //数据序列
+    dataIndex: {
+      type: Number,
+    },
+    //分组数据
+    zqDetailData: {
+      type: Object as any,
+    },
+  });
 </script>
 
 <style lang="less" scoped>
-@import '/@/design/theme.less';
+  @import '/@/design/theme.less';
 
-@{theme-deepblue} {
-  .detail-card {
-    --image-inject_zq_card: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-5.png');
-    --image-inject_zq_card1: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-7.png');
+  @{theme-deepblue} {
+    .detail-card {
+      --image-inject_zq_card: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-5.png');
+      --image-inject_zq_card1: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-7.png');
+    }
   }
-}
-
-.detail-card {
-  --image-inject_zq_card: url('@/assets/images/gasInjection/9-5.png');
-  --image-inject_zq_card1: url('@/assets/images/gasInjection/9-7.png');
-  width: 100%;
-  height: 100%;
-
-  .card-title {
-    display: flex;
-    align-items: center;
-    width: 100%;
-    height: 52px;
-    margin-bottom: 4px;
-    padding: 0px 20px;
-    font-size: 16px;
-    color: #fff;
-    background: var(--image-inject_zq_card) no-repeat;
-    background-size: 100% 100%;
-  }
-
-  .card-content {
-
-    width: 100%;
-    height: calc(100% - 56px);
-    border-radius: 15px;
-    /* 内阴影 */
-    box-shadow: inset 0px 0px 15px rgba(34, 142, 220, 0.8);
-    padding: 10px 15px;
 
-  }
-
-  .card-content-box {
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    align-items: center;
+  .detail-card {
+    --image-inject_zq_card: url('@/assets/images/gasInjection/9-5.png');
+    --image-inject_zq_card1: url('@/assets/images/gasInjection/9-7.png');
     width: 100%;
     height: 100%;
-    overflow-y: auto;
-  }
-
-  .card-content-item {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    width: 100%;
-    // height: 26px;
-    margin: 5px 0px;
-    padding: 0px 15px;
-    color: #fff;
-    background: linear-gradient(to right, rgba(17, 50, 86, 1), rgba(17, 50, 86, .1));
-  }
-
-  .content-item-label {
-    color: #c3f5ff;
-  }
-
-  .content-item-value {
 
-    font-family: 'douyuFont';
-    color: #91faff;
+    .card-title {
+      display: flex;
+      align-items: center;
+      width: 100%;
+      height: 52px;
+      margin-bottom: 4px;
+      padding: 0px 20px;
+      font-size: 16px;
+      color: #fff;
+      background: var(--image-inject_zq_card) no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .card-content {
+      width: 100%;
+      height: calc(100% - 56px);
+      border-radius: 15px;
+      /* 内阴影 */
+      box-shadow: inset 0px 0px 15px rgba(34, 142, 220, 0.8);
+      padding: 10px 15px;
+    }
+
+    .card-content-box {
+      display: flex;
+      flex-direction: column;
+      justify-content: space-between;
+      align-items: center;
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+    }
+
+    .card-content-item {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 100%;
+      // height: 26px;
+      margin: 5px 0px;
+      padding: 0px 15px;
+      color: #fff;
+      background: linear-gradient(to right, rgba(17, 50, 86, 1), rgba(17, 50, 86, 0.1));
+    }
+
+    .content-item-label {
+      color: #c3f5ff;
+    }
+
+    .content-item-value {
+      font-family: 'douyuFont';
+      color: #91faff;
+    }
   }
-}
 </style>

+ 27 - 16
src/views/vent/home/configurable/components/gasInject/zqHistoryExport.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="history-export">
-    <SvgIcon v-if="!isShowIcon" class="icon-style-back" size="32" name="modal-back" @click="handleBack"></SvgIcon>
+    <SvgIcon v-if="!isShowIcon" class="icon-style-back" size="32" name="modal-back" @click="handleBack" />
     <div class="nav-box">
       <div class="nav-search">
         <div class="search-item">
@@ -14,7 +14,7 @@
       </div>
       <div class="nav-btn" @click="handleHisExport">
         <div class="nav-text">
-          <SvgIcon class="icon-style" size="18" name="gas-export"></SvgIcon>
+          <SvgIcon class="icon-style" size="18" name="gas-export" />
           <span>数据导出</span>
         </div>
       </div>
@@ -33,7 +33,7 @@
 <script setup lang="ts">
 import { reactive, ref, onMounted } from 'vue'
 import { SvgIcon } from '/@/components/Icon';
-import { listdays, getList, historydataExport } from './gasInject.api'
+import { listdays, getList, } from './gasInject.api'
 import dayjs from 'dayjs';
 
 let props = defineProps({
@@ -53,19 +53,16 @@ let props = defineProps({
     type: String
   },
   //设备类型
-  strtype:{
-    type:String
+  strtype: {
+    type: String
   }
 })
 
 let searchData = reactive({
   startTime: dayjs().subtract(1, 'day').format('YYYY-MM-DD HH:mm:ss'),
   endTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
-
 })
-let dataSource = ref<any[]>([
-  // { fileName: '测试' },
-])
+let dataSource = ref<any[]>([])
 //分页参数配置
 let pagination = reactive({
   current: 1, // 当前页码
@@ -73,10 +70,10 @@ let pagination = reactive({
   total: 0, // 总条目数,后端返回
   // showTotal: (total, range) => `${range[0]}-${range[1]} 条,总共 ${total} 条`, // 分页右下角显示信息
   showSizeChanger: true, // 是否可改变每页显示条数
-  pageSizeOptions: ['15', '20', '25', '30', '50'], // 可选的每页显示条数
+  pageSizeOptions: ['15', '30', '50'], // 可选的每页显示条数
 });
-let $emit = defineEmits(['handleBack'])
 
+let $emit = defineEmits(['handleBack'])
 //返回上一级
 function handleBack() {
   $emit('handleBack')
@@ -120,8 +117,8 @@ function changePage(val) {
 }
 //导出历史数据
 async function handleHisExport() {
-  let res = await historydataExport({ ttime_begin: searchData.startTime, ttime_end: searchData.endTime, skip: '8', gdeviceids: props.deviceId, pageNo: pagination.current, pageSize: pagination.pageSize, strtype: props.strtype })
-  console.log(res, '导出')
+  // let res = await historydataExport({ ttime_begin: searchData.startTime, ttime_end: searchData.endTime, skip: '8', gdeviceids: props.deviceId, pageNo: pagination.current, pageSize: pagination.pageSize, strtype: props.strtype })
+  // console.log(res, '导出')
 }
 
 
@@ -175,6 +172,18 @@ onMounted(() => {
     margin-right: 40px;
   }
 
+  .search-btn-item {
+    display: flex;
+    width: 100px;
+    height: 78%;
+    padding: 2px;
+    cursor: pointer;
+    justify-content: center;
+    align-items: center;
+    background-color: #1081d7;
+    border-radius: 4px;
+  }
+
   .nav-btn {
     width: 100px;
     height: 78%;
@@ -202,6 +211,7 @@ onMounted(() => {
     justify-content: space-between;
     width: 100%;
     height: calc(100% - 54px);
+    min-height: 500px;
   }
 }
 
@@ -231,7 +241,6 @@ onMounted(() => {
   margin-left: 10px;
 }
 
-
 :deep(.zxm-table-header) {
   border-left: 1px solid #2896ca !important;
   border-right: 1px solid #2896ca !important;
@@ -241,19 +250,21 @@ onMounted(() => {
   border-right: 1px solid #2896ca !important;
 }
 
+:deep(.zxm-table-cell-fix-left) {
+  background-color: var(--vent-table-thead) !important;
+}
+
 :deep(.zxm-table-tbody > tr) {
   &:nth-child(odd) {
     td {
       background-color: #0d2c50 !important;
     }
-
   }
 
   &:nth-child(even) {
     td {
       background-color: #0d233f !important;
     }
-
   }
 }
 

+ 185 - 223
src/views/vent/home/configurable/components/gasInject/zqMonitorDetail.vue

@@ -54,10 +54,10 @@
 </template>
 
 <script setup lang="ts">
-import { reactive, ref, watch } from 'vue'
-import { SvgIcon } from '/@/components/Icon';
-import zqDetailCard from './zqDetailCard.vue'
-import zqHistoryExport from './zqHistoryExport.vue'
+  import { reactive, ref, watch } from 'vue';
+  import { SvgIcon } from '/@/components/Icon';
+  import zqDetailCard from './zqDetailCard.vue';
+  import zqHistoryExport from './zqHistoryExport.vue';
 
 let props = defineProps({
   //是否打开历史数据页面
@@ -83,45 +83,8 @@ let props = defineProps({
 })
 
 let showExport = ref(false)
-//注气详情数据
-let zqDetailData1 = reactive({
-  value1: '10',
-  value2: '10',
-  value3: '10',
-  value4: '10',
-  value5: '10',
-  value6: '10',
-})
-let zqDetailData2 = reactive({
-  value1: '20',
-  value2: '20',
-  value3: '20',
-  value4: '20',
-  value5: '20',
-  value6: '20',
-})
-let zqDetailData3 = reactive({
-  value1: '30',
-  value2: '30',
-  value3: '30',
-  value4: '30',
-  value5: '30',
-  value6: '30',
-})
-let zqDetailData4 = reactive({
-  value1: '40',
-  value2: '40',
-  value3: '40',
-  value4: '40',
-  value5: '40',
-  value6: '40',
-})
 
 
-//历史数据跳转
-function handleExport() {
-  showExport.value = true
-}
 //历史数据返回至详情
 function handleBack() {
   showExport.value = false
@@ -133,211 +96,210 @@ watch(() => props.isShowExport, (newV, oldV) => {
 </script>
 
 <style lang="less" scoped>
-@import '/@/design/theme.less';
-
-@{theme-deepblue} {
-  .gas-inject-card {
-    --image-inject_zq_monitor: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-3.png');
-    --image-inject_zq_monitor1: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-6.png');
+  @import '/@/design/theme.less';
 
+  @{theme-deepblue} {
+    .gas-inject-card {
+      --image-inject_zq_monitor: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-3.png');
+      --image-inject_zq_monitor1: url('@/assets/images/themify/deepblue/home-container/configurable/gasInjection/9-6.png');
+    }
   }
-}
 
-.zq-monitor {
-  --image-inject_zq_monitor: url('@/assets/images/gasInjection/9-3.png');
-  --image-inject_zq_monitor1: url('@/assets/images/gasInjection/9-6.png');
-  width: 100%;
-  height: 650px;
-  padding: 15px 30px;
-  box-sizing: border-box;
-
-  .nav-box {
-    display: flex;
-    justify-content: flex-end;
-    align-items: center;
+  .zq-monitor {
+    --image-inject_zq_monitor: url('@/assets/images/gasInjection/9-3.png');
+    --image-inject_zq_monitor1: url('@/assets/images/gasInjection/9-6.png');
     width: 100%;
-    height: 39px;
-    padding: 0px 20px;
-    background: var(--image-inject_zq_monitor) no-repeat;
-    background-size: 100% 100%;
-  }
+    height: 650px;
+    padding: 15px 30px;
+    box-sizing: border-box;
 
-  .nav-btn {
-    width: 125px;
-    height: 78%;
-    padding: 2px;
-    border: 1px solid #1081d7;
-    cursor: pointer;
-    color: #fff;
-  }
+    .nav-box {
+      display: flex;
+      justify-content: flex-end;
+      align-items: center;
+      width: 100%;
+      height: 39px;
+      padding: 0px 20px;
+      background: var(--image-inject_zq_monitor) no-repeat;
+      background-size: 100% 100%;
+    }
 
-  .nav-text {
-    width: 100%;
-    height: 100%;
-    display: flex;
-    justify-content: center;
-    align-items: center;
-    background-color: #1081d7;
-  }
+    .nav-btn {
+      width: 125px;
+      height: 78%;
+      padding: 2px;
+      border: 1px solid #1081d7;
+      cursor: pointer;
+      color: #fff;
+    }
 
-  .icon-style {
-    margin-right: 5px;
-  }
+    .nav-text {
+      width: 100%;
+      height: 100%;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      background-color: #1081d7;
+    }
 
-  .main-content {
-    display: flex;
-    justify-content: space-between;
-    width: 100%;
-    height: calc(100% - 39px);
-    padding: 30px 0px 20px 0px;
-  }
+    .icon-style {
+      margin-right: 5px;
+    }
 
-  .common-area {
-    display: flex;
-    flex-direction: column;
-    justify-content: space-between;
-    width: 320px;
-    height: 100%;
-  }
+    .main-content {
+      display: flex;
+      justify-content: space-between;
+      width: 100%;
+      height: calc(100% - 39px);
+      padding: 30px 0px 20px 0px;
+    }
 
-  .area-item {
-    width: 100%;
-    height: calc(50% - 15px);
-  }
+    .common-area {
+      display: flex;
+      flex-direction: column;
+      justify-content: space-between;
+      width: 320px;
+      height: 100%;
+    }
 
-  .center-area {
-    position: relative;
-    width: calc(100% - 640px);
-    height: 100%;
-  }
+    .area-item {
+      width: 100%;
+      height: calc(50% - 15px);
+    }
 
-  .center-content {
-    position: absolute;
-    left: 50%;
-    top: 50%;
-    transform: translate(-50%, -50%);
-    width: 500px;
-    height: 315px;
-    border-radius: 20px;
-    box-shadow: inset 0px 0px 20px rgba(34, 142, 220, 0.8);
-  }
+    .center-area {
+      position: relative;
+      width: calc(100% - 640px);
+      height: 100%;
+    }
 
-  .line-L {
-    display: flex;
-    position: absolute;
-    left: 0;
-    top: 24px;
-    width: 90px;
-    height: 282px;
-  }
+    .center-content {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
+      width: 500px;
+      height: 315px;
+      border-radius: 20px;
+      box-shadow: inset 0px 0px 20px rgba(34, 142, 220, 0.8);
+    }
 
-  .line-R {
-    display: flex;
-    position: absolute;
-    right: 0;
-    top: 24px;
-    width: 90px;
-    height: 282px;
-  }
+    .line-L {
+      display: flex;
+      position: absolute;
+      left: 0;
+      top: 24px;
+      width: 90px;
+      height: 282px;
+    }
 
-  .line-L-1 {
-    width: 50%;
-    height: 100%;
-    border-top: 2px solid #277297;
-    border-right: 2px solid #277297;
-    border-bottom: 2px solid #277297;
-  }
+    .line-R {
+      display: flex;
+      position: absolute;
+      right: 0;
+      top: 24px;
+      width: 90px;
+      height: 282px;
+    }
 
-  .line-R-1 {
-    position: absolute;
-    left: 50%;
-    top: 0;
-    width: 50%;
-    height: 100%;
-    border-top: 2px solid #277297;
-    border-left: 2px solid #277297;
-    border-bottom: 2px solid #277297;
-  }
+    .line-L-1 {
+      width: 50%;
+      height: 100%;
+      border-top: 2px solid #277297;
+      border-right: 2px solid #277297;
+      border-bottom: 2px solid #277297;
+    }
 
-  .t-icon {
-    position: absolute;
-    left: 0;
-    top: -8px;
-    width: 30px;
-    height: 18px;
-    background: var(--image-inject_zq_monitor1) no-repeat;
-    background-size: 100% 100%;
-  }
+    .line-R-1 {
+      position: absolute;
+      left: 50%;
+      top: 0;
+      width: 50%;
+      height: 100%;
+      border-top: 2px solid #277297;
+      border-left: 2px solid #277297;
+      border-bottom: 2px solid #277297;
+    }
 
-  .t-icon-r {
-    position: absolute;
-    right: 0;
-    top: -10px;
-    width: 30px;
-    height: 18px;
-    background: var(--image-inject_zq_monitor1) no-repeat;
-    background-size: 100% 100%;
-    transform: rotate(180deg);
-  }
+    .t-icon {
+      position: absolute;
+      left: 0;
+      top: -8px;
+      width: 30px;
+      height: 18px;
+      background: var(--image-inject_zq_monitor1) no-repeat;
+      background-size: 100% 100%;
+    }
 
-  .t-icon1 {
-    position: absolute;
-    left: 0;
-    bottom: -8px;
-    width: 30px;
-    height: 18px;
-    background: var(--image-inject_zq_monitor1) no-repeat;
-    background-size: 100% 100%;
-  }
+    .t-icon-r {
+      position: absolute;
+      right: 0;
+      top: -10px;
+      width: 30px;
+      height: 18px;
+      background: var(--image-inject_zq_monitor1) no-repeat;
+      background-size: 100% 100%;
+      transform: rotate(180deg);
+    }
 
-  .t-icon-r1 {
-    position: absolute;
-    right: 0;
-    bottom: -10px;
-    width: 30px;
-    height: 18px;
-    background: var(--image-inject_zq_monitor1) no-repeat;
-    background-size: 100% 100%;
-    transform: rotate(180deg);
-  }
+    .t-icon1 {
+      position: absolute;
+      left: 0;
+      bottom: -8px;
+      width: 30px;
+      height: 18px;
+      background: var(--image-inject_zq_monitor1) no-repeat;
+      background-size: 100% 100%;
+    }
 
-  .line-L-2 {
-    position: absolute;
-    width: 50%;
-    height: 2px;
-    left: 50%;
-    top: 190px;
-    background-color: #277297;
-  }
+    .t-icon-r1 {
+      position: absolute;
+      right: 0;
+      bottom: -10px;
+      width: 30px;
+      height: 18px;
+      background: var(--image-inject_zq_monitor1) no-repeat;
+      background-size: 100% 100%;
+      transform: rotate(180deg);
+    }
 
-  .line-R-2 {
-    position: absolute;
-    width: 50%;
-    height: 2px;
-    left: 0;
-    top: 190px;
-    background-color: #277297;
-  }
+    .line-L-2 {
+      position: absolute;
+      width: 50%;
+      height: 2px;
+      left: 50%;
+      top: 190px;
+      background-color: #277297;
+    }
 
-  .t-icon2 {
-    position: absolute;
-    right: 5px;
-    top: 182px;
-    width: 30px;
-    height: 18px;
-    background: var(--image-inject_zq_monitor1) no-repeat;
-    background-size: 100% 100%;
-  }
+    .line-R-2 {
+      position: absolute;
+      width: 50%;
+      height: 2px;
+      left: 0;
+      top: 190px;
+      background-color: #277297;
+    }
 
-  .t-icon2-r {
-    position: absolute;
-    left: 5px;
-    top: 182px;
-    width: 30px;
-    height: 18px;
-    background: var(--image-inject_zq_monitor1) no-repeat;
-    background-size: 100% 100%;
-    transform: rotate(180deg);
+    .t-icon2 {
+      position: absolute;
+      right: 5px;
+      top: 182px;
+      width: 30px;
+      height: 18px;
+      background: var(--image-inject_zq_monitor1) no-repeat;
+      background-size: 100% 100%;
+    }
+
+    .t-icon2-r {
+      position: absolute;
+      left: 5px;
+      top: 182px;
+      width: 30px;
+      height: 18px;
+      background: var(--image-inject_zq_monitor1) no-repeat;
+      background-size: 100% 100%;
+      transform: rotate(180deg);
+    }
   }
-}
 </style>

+ 6 - 7
src/views/vent/home/configurable/configurable.api.ts

@@ -5,7 +5,6 @@ import { useGlobSetting } from '/@/hooks/setting';
 import { ref, reactive } from 'vue';
 import _ from 'lodash';
 
-
 enum Api {
   list = '/safety/ventanalyDevice/homedata2',
   getHomeData = '/safety/ventanalyDevice/homedata',
@@ -19,7 +18,7 @@ enum Api {
   getDisasterProportion = '/safety/ventanalyAlarmLog/getDisasterProportion',
   system = '/ventanaly-device/monitor/device',
   getSystem = '/monitor/deviceForZhuQi',
-  listdays = '/safety/ventanalyMonitorData/listdays'
+  listdays = '/safety/ventanalyMonitorData/listdays',
 }
 
 
@@ -656,7 +655,7 @@ export const sysCc = (params) => {
 //获取抽采系统数据
 function getCcSystem() {
   return new Promise(async (resolve) => {
-    const result = await sysCc({ devicetype: 'sys', systemID: "2036610034808954882" });
+    const result = await sysCc({ devicetype: 'sys', systemID: '2036610034808954882' });
     resolve(result);
   });
 }
@@ -694,8 +693,8 @@ export const getSystemApi = (params) => {
     //注气驱替促抽系统状态
     res.zyStatusData = {
       Pressure: zqData.readData.Pressure,
-      paiqiPressure: zqData.readData.paiqiPressure
-    }
+      paiqiPressure: zqData.readData.paiqiPressure,
+    };
     //注气/抽采历史图表数据
     let historyDataZq = reactive<any>({})
     historyDataZq.hourData = await getZqSystem({ pageNo: 1, pageSize: 100, gdeviceids: zqData.deviceID, strtype: zqData.deviceType, skip: '8' })
@@ -709,14 +708,14 @@ export const getSystemApi = (params) => {
       runStatus: zqData.readData.runStatus,
       paramSetting: zqData.readData.paramSetting,
       paiqiStatus: zqData.readData.paiqiStatus,
-    }
+    };
     //注气数据监测
     res.dataMonitor = {
       flowStdInstant: zqData.readData.flowStdInstant,
       flowStdAccum: zqData.readData.flowStdAccum,
       injectionTemperature: zqData.readData.injectionTemperature,
       injectionPressure: zqData.readData.injectionPressure,
-    }
+    };
     //环境监测
     res.environmentData = {
       coVal: res.co.readData.coval || '-',

+ 29 - 16
src/views/vent/home/configurable/gasInjection.vue

@@ -35,22 +35,22 @@
         />
       </template>
       <div :class="{ 'vent-modal': menuName == 'zjm', 'vent-modal-1': menuName == 'syq' || menuName == 'zqxt' || menuName == 'ccxt' }">
-        <div class="modal-box">
+        <a-spin class="spin" :spinning="!isInitModal" tip="正在加载,请稍等..." />
+        <div class="modal-box" id="modalBox" v-if="isInitModal">
           <Three3D ref="three3D" :modalName="'zhuqi'" class="modal-3d" @success="initModalAnimate" />
+          <div class="modal-css3d" id="css3dContainer"> </div>
         </div>
       </div>
-      <!-- <div v-if="menuName === 'syq'" class="syq-modal">
-        </div> -->
     </div>
   </div>
 </template>
 <script lang="ts" setup>
-  import { onMounted, onUnmounted, ref, computed, nextTick } from 'vue';
+  import { onMounted, onUnmounted, ref, computed, nextTick, onBeforeMount, watch } from 'vue';
   import { useInitConfigs, useInitPage } from './hooks/useInit';
   import ModuleGasInject from './components/ModuleGasInject.vue';
   import navMenu from './components/gasInject/navMenu.vue';
   import Three3D from './components/three3D.vue';
-  import { getSystemApi } from './configurable.api';
+  import { getHomeData, getSystemApi } from './configurable.api';
   // import { useRoute } from 'vue-router';
   import { testConfigGasInject, testConfigGasInjectZq, testConfigGasInjectCc, testConfigGasInjectSy } from './configurable.data';
   import { animateCamera } from '/@/utils/threejs/util';
@@ -64,6 +64,7 @@
   const three3D = ref(null);
   let interval: number | undefined;
   let menuName = ref('zjm');
+  const isInitModal = ref(false);
 
   //选项切换
   function toggleMenu(param) {
@@ -91,8 +92,8 @@
         const oldCamera = modal.camera;
         const oldCameraPosition = { x: oldCamera.position.x, y: oldCamera.position.y, z: oldCamera.position.z };
         if (param == 'zqxt') {
-          const newP = { x: -29.4124614570094, y: -1.6093280622283133, z: 39.51639267244655 };
-          const newT = { x: 11.205344322178817, y: -1.7174801102124406, z: 40.19437751610255 };
+          const newP = { x: -30.683057066775824, y: -0.9161184591449614, z: 39.6716716890212 };
+          const newT = { x: 11.204235206978213, y: -1.0276506907362775, z: 40.37084673927868 };
           await animateCamera(oldCameraPosition, { x: 0, y: 0, z: 0 }, newP, newT, modal);
         } else if (param == 'zjm') {
           const newP = { x: -45.69228602978097, y: 49.59721939545517, z: 2.6454258202266985 };
@@ -108,24 +109,29 @@
   }
 
   function initModalAnimate(modal) {
-    console.log('初始化模型', modal);
+    modalAnimate(modal, data);
     modal.isRender = true;
-
-    modalAnimate(modal);
   }
 
   onMounted(() => {
     fetchConfigs('gas_injection').then(() => {
       configs.value = testConfigGasInject;
-      //  updateEnhancedConfigs(configs.value);
-      getSystemApi({devicetype:'sys', systemID: '2036323791827165185' }).then(updateData);
+      getSystemApi({ devicetype: 'sys', systemID: '2036323791827165185' }).then(updateData);
     });
-
-    setInterval(() => {
-      getSystemApi({devicetype:'sys', systemID: '2036323791827165185' }).then(updateData);
-    }, 60000);
+    interval = setInterval(() => {
+      getSystemApi({ devicetype: 'sys', systemID: '2036323791827165185' }).then(updateData);
+    }, 10000);
   });
 
+  watch(
+    () => data.value,
+    (newData, oldData) => {
+      if (newData && !Object.keys(oldData).length) {
+        isInitModal.value = true;
+      }
+    }
+  );
+
   onUnmounted(() => {
     clearInterval(interval);
   });
@@ -303,4 +309,11 @@
   .modal-3d {
     width: 100%;
   }
+  .spin {
+    width: 100%;
+    position: absolute;
+    top: 50%;
+    left: 0;
+    z-index: 0;
+  }
 </style>

+ 543 - 109
src/views/vent/home/configurable/threejs/gasInjection.ts

@@ -1,3 +1,4 @@
+import { Ref, ref, watch } from 'vue';
 import * as THREE from 'three';
 import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
 import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
@@ -5,24 +6,94 @@ import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
 import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js';
 import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js';
 import { PathPointList, PathGeometry } from 'three.path';
+import { modelMouseHandler } from '/@/utils/threejs/useEvent';
+import { panelManager } from './PanelManager';
+
+// const cgqMarkerList = [];
+// const lljMarkerList = [];
+const zhuqiPipList = []; // 注气支管
+const zhuqiZkList = []; // 注气钻孔
+const gasUnitList = []; //瓦斯抽采单元
+const pipList = [];
+let mouseoverEvent: any;
+let mouseUpEvent: any;
+let clickSelecteObject: any;
+let pipDataList = [];
+export function modalAnimate(modal, data: Ref<any, any>) {
+  const zhuqiPipDataRef = ref(null);
+  const maxIndex = getPipIndex(data.value) || 0;
+  const zhuqiModal = modal.scene.getObjectByName('zhuqi').getObjectByName('ZhuQiChangJing_1');
 
-export function modalAnimate(modal) {
-  modal.renderer.toneMappingExposure = 1.2;
-
-  console.log('初始化模型', modal);
-  modal.isRender = true;
-
-  // 【关键步骤】设置旋转速度
-  // 默认是 1.0,设置为 0.5 会让移动幅度减半(更慢/更精细)
-  modal.orbitControls.rotateSpeed = 0.5;
+  watch(data, (newValue) => {
+    const zhuqiData = newValue.msgTxt.find((item) => item.type.includes('injection'))?.datalist[0];
+    if (zhuqiData) {
+      const readData = zhuqiData.readData;
+      const zzgData = {
+        positionName: '总支管#',
+        flowStdInstant: readData['flowStdInstant'], // 瞬时流量
+        flowStdAccum: readData['flowStdAccum'], // 累计流量
+        injectionTemperature: readData['injectionTemperature'], // 温度
+        injectionPressure: readData['injectionPressure'], // 压力
+        valveOpening: readData['valveOpening'], // 阀门开度
+        paiqiStatus: readData['paiqiStatus'], // 阀门状态
+      };
+      zhuqiPipDataRef.value = zzgData;
+
+      for (let i = 1; i <= maxIndex; i++) {
+        if (pipDataList[i - 1]) {
+          const zzgData = {
+            positionName: `${i}#`,
+            flowStdInstant: readData['flowStdInstant_' + i], // 瞬时流量
+            flowStdAccum: readData['flowStdAccum_' + i], // 累计流量
+            injectionTemperature: readData['injectionTemperature_' + i], // 温度
+            injectionPressure: readData['injectionPressure_' + i], // 压力
+            valveOpening: readData[`valve${i}Opening`], // 阀门开度
+            paiqiStatus: readData['paiqiStatus_' + i], // 阀门状态
+          };
+          pipDataList[i - 1].value = zzgData;
+        } else {
+          const zzgData = ref({
+            positionName: `${i}#`,
+            flowStdInstant: readData['flowStdInstant_' + i], // 瞬时流量
+            flowStdAccum: readData['flowStdAccum_' + i], // 累计流量
+            injectionTemperature: readData['injectionTemperature_' + i], // 温度
+            injectionPressure: readData['injectionPressure_' + i], // 压力
+            valveOpening: readData[`valve${i}Opening`], // 阀门开度
+            paiqiStatus: readData['paiqiStatus_' + i], // 阀门状态
+          });
+          pipDataList[i - 1] = zzgData;
+        }
+        // if (i > 4) {
+        //   zhuqiPipList[i - 1].visible = false;
+        //   zhuqiZkList[i - 1].visible = false;
+        // }
+      }
+    }
+  });
+  initAnimate(modal);
+
+  const zhuqiData = data.value.msgTxt.find((item) => item.type.includes('injection'))?.datalist[0];
+  if (zhuqiData) {
+    const readData = zhuqiData.readData;
+    const zzgData = {
+      positionName: '总支管#',
+      flowStdInstant: readData['flowStdInstant'], // 瞬时流量
+      flowStdAccum: readData['flowStdAccum'], // 累计流量
+      injectionTemperature: readData['injectionTemperature'], // 温度
+      injectionPressure: readData['injectionPressure'], // 压力
+      valveOpening: readData['valveOpening'], // 阀门开度
+      paiqiStatus: readData['paiqiStatus'], // 阀门状态
+    };
+    zhuqiPipDataRef.value = zzgData;
+    initPanel(modal, zhuqiModal, zhuqiPipDataRef);
+  }
+}
 
-  // 可选:如果你也想减慢缩放和平移的速度
-  modal.orbitControls.zoomSpeed = 0.5; // 缩放速度
-  modal.orbitControls.panSpeed = 0.5; // 平移速度
+function initAnimate(modal) {
+  const curveNum = 8;
+  const unitNum = 8;
 
-  // 启用阻尼效果(可选,让运动更有惯性,感觉更平滑)
-  modal.orbitControls.enableDamping = true;
-  modal.orbitControls.dampingFactor = 0.9;
+  resetModalParam(modal);
 
   const bounds = {
     min: new THREE.Vector3(-25, -2, -35),
@@ -37,12 +108,21 @@ export function modalAnimate(modal) {
   touming3.parent.remove(touming3);
 
   const time = Date.now() * 0.001;
-  const clock = new THREE.Clock();
 
-  const bloomObj = addZhuqiAnimate(zhuqiModal, modal);
-  const arrowObj = addArrow(zhuqiModal);
-  const unitObj = addUnit(zhuqiModal);
+  // 绘制注气钻孔
+  const arrowObj = addArrow(zhuqiModal, curveNum);
+
+  // 绘制注气支管
+  const pipObj = addPip(zhuqiModal, curveNum);
 
+  // 绘制抽采单元
+  const unitObj = addUnit(zhuqiModal, unitNum);
+
+  initCss2DContainer(modal);
+  // 添加鼠标拾取
+  initMouseEvent(modal, zhuqiModal);
+
+  // const clock = new THREE.Clock();
   // 帧刷新
   modal.startAnimation = () => {
     /**气泡运动 - 遍历所有颜色组*/
@@ -83,19 +163,6 @@ export function modalAnimate(modal) {
       instancedMesh.instanceMatrix.needsUpdate = true;
     });
 
-    /**自发光*/
-    if (bloomObj) {
-      // const { zhuqiPips, bloomComposer, finalComposer, materials, darkMaterial, shader, bloomLayer } = bloomObj;
-      // modal.scene.traverse((item) => {
-      //   darkenNonBloomed(item, bloomLayer, materials, darkMaterial);
-      // });
-      // bloomComposer.render();
-      // modal.scene.traverse((item) => {
-      //   restoreMaterial(item, materials);
-      // });
-      // finalComposer.render();
-    }
-
     /**箭头流动效果*/
     if (arrowObj) {
       const { texture } = arrowObj;
@@ -107,7 +174,179 @@ export function modalAnimate(modal) {
       const { arrowTexture } = unitObj;
       arrowTexture.offset.x -= 0.0576;
     }
+
+    if (pipObj) {
+      const { texture } = pipObj;
+      texture.offset.x -= 0.1076;
+    }
+
+    // [...cgqMarkerList, ...lljMarkerList].forEach((marker: THREE.Mesh) => {
+    //   // 更新水波纹动画
+    //   const delta = clock.getElapsedTime();
+    //   if (marker.userData.update) {
+    //     marker.userData.update(delta);
+    //   }
+    // });
+  };
+}
+
+function getPipIndex(data: Ref<any, any>) {
+  if (data && data['msgTxt']) {
+    const zhuqiData = data['msgTxt'].find((item) => item.type.includes('injection'))?.datalist[0];
+    if (zhuqiData) {
+      const readData = zhuqiData.readData;
+      const max = 20;
+      let maxIndex = 0;
+      for (let i = 1; i < max; i++) {
+        if (readData['flowWorkAccum_' + i] !== undefined) {
+          continue;
+        } else {
+          maxIndex = i;
+        }
+      }
+      for (let i = 1; i <= maxIndex; i++) {
+        const zzgData = ref({
+          positionName: `${i}#`,
+          flowStdInstant: readData['flowStdInstant_' + i], // 瞬时流量
+          flowStdAccum: readData['flowStdAccum_' + i], // 累计流量
+          injectionTemperature: readData['injectionTemperature_' + i], // 温度
+          injectionPressure: readData['injectionPressure_' + i], // 压力
+          valveOpening: readData[`valve${i}Opening`], // 阀门开度
+          paiqiStatus: readData['paiqiStatus_' + i], // 阀门状态
+        });
+        pipDataList.push(zzgData);
+      }
+      return maxIndex;
+    }
+  }
+}
+// 初始化主支管面板
+function initPanel(modal, zhuqiModal, zhuqiPipDataRef) {
+  const changjingpPipe38 = zhuqiModal.getObjectByName('guandao')?.getObjectByName('changjingpPipe38');
+  if (changjingpPipe38) {
+    const option = {
+      instanceId: changjingpPipe38.name,
+      sensorData: zhuqiPipDataRef,
+      threshold: { temperature: 20 },
+      position: [],
+      scale: 0.005,
+    };
+    createMonitorPanel3D(modal, changjingpPipe38, option);
+    const position = panelManager.getPanel(changjingpPipe38.name)?.vm.cssObject.position.clone();
+    panelManager.getPanel(changjingpPipe38.name)?.vm.cssObject.position.set(position.x, position.y + 0.6, position.z + 3.5);
+  }
+}
+
+function initMouseEvent(modal, zhuqiModal) {
+  const zhuqiModalGroup = zhuqiModal.getObjectByName('zhuqiModalGroup');
+  mouseoverEvent = (event) => {
+    modelMouseHandler(modal, pipList, event, (intersects) => {
+      partEvent(intersects, pipList, modal, event);
+    });
+  };
+  mouseUpEvent = (event) => {
+    modelMouseHandler(modal, pipList, event, (intersects) => {
+      partEvent(intersects, pipList, modal, event);
+    });
   };
+  modal.canvasContainer?.addEventListener('mousemove', mouseoverEvent);
+  modal.canvasContainer?.addEventListener('mouseup', mouseUpEvent);
+}
+
+function partEvent(intersects, partitionList, modal, event) {
+  partitionList.forEach((partition) => {
+    if (!clickSelecteObject || (clickSelecteObject && partition.name !== clickSelecteObject.name)) {
+      partition.visible = false;
+    }
+  });
+  if (intersects && intersects.length > 0) {
+    const partitionObj = intersects.find((item) => {
+      if (item.object.type === 'Line') {
+        const partition = partitionList.find((partition) => partition.name === item.object.name);
+        if (partition) {
+          partition.visible = true;
+        }
+        return true;
+      }
+      return false;
+    });
+    // 如果是点击事件
+    if (event.type === 'mouseup' && event.button == 0) {
+      if (clickSelecteObject) {
+        panelManager.destroyPanel(clickSelecteObject.name);
+        clickSelecteObject = null;
+      }
+      if (partitionObj) {
+        clickSelecteObject = partitionObj.object;
+        const index = clickSelecteObject.name.split('pipLine')[1];
+        const name = parseInt(index) + 1 + '#';
+        const pipDataRef = pipDataList.find((item) => item.value.positionName === name);
+        const option = {
+          instanceId: clickSelecteObject.name,
+          sensorData: pipDataRef,
+          threshold: { temperature: 20 },
+          position: [],
+          scale: 0.004,
+        };
+        createMonitorPanel3D(modal, clickSelecteObject, option);
+      }
+    }
+
+    return partitionObj;
+  } else {
+    if (event.type === 'mouseup' && event.button == 0) {
+      if (clickSelecteObject) {
+        panelManager.destroyPanel(clickSelecteObject.name);
+        clickSelecteObject = null;
+      }
+    }
+  }
+}
+
+function createMonitorPanel3D(modal, partition, options?) {
+  const box = new THREE.Box3();
+  box.setFromObject(partition);
+  const center = box.getCenter(new THREE.Vector3());
+  options.position = [center.x, center.y, center.z];
+  console.log('创建面板', options.position);
+
+  panelManager.createPanel(modal.scene, options);
+
+  // // 点击后相机
+  // const target0 = modal.camera.position.clone();
+  // const target1 = modal.orbitControls.target.clone();
+  // gsap.fromTo(
+  //   modal.orbitControls.target,
+  //   { x: target1.x, y: target1.y, z: target1.z },
+  //   {
+  //     x: center.x - 4,
+  //     y: center.y + 1,
+  //     z: center.z,
+  //     duration: 0.4,
+  //     ease: 'easeInCirc',
+  //     onUpdate: function (object) {
+  //       if (object) modal.camera?.lookAt(new THREE.Vector3(object.x, object.y, object.z));
+  //     },
+  //   }
+  // );
+}
+
+function resetModalParam(modal) {
+  modal.renderer.toneMappingExposure = 1.35;
+
+  console.log('初始化模型', modal);
+  modal.isRender = true;
+
+  // 【关键步骤】设置旋转速度
+  // 默认是 1.0,设置为 0.5 会让移动幅度减半(更慢/更精细)
+  modal.orbitControls.rotateSpeed = 0.3;
+  // 可选:如果你也想减慢缩放和平移的速度
+  modal.orbitControls.zoomSpeed = 0.3; // 缩放速度
+  modal.orbitControls.panSpeed = 0.3; // 平移速度
+
+  // 启用阻尼效果(可选,让运动更有惯性,感觉更平滑)
+  modal.orbitControls.enableDamping = true;
+  modal.orbitControls.dampingFactor = 0.9;
 }
 
 function initGasAnimate(modal, colorConfigs: any[] = []) {
@@ -118,14 +357,14 @@ function initGasAnimate(modal, colorConfigs: any[] = []) {
         name: 'gas',
         color: 0xffa2a244,
         emissive: 0xff4400,
-        bubbleCount: 200,
+        bubbleCount: 100,
         hslHue: 0.4, // 红色
       },
       {
         name: 'n2',
         color: 0x4fedf788,
         emissive: 0x0044aa,
-        bubbleCount: 500,
+        bubbleCount: 300,
         hslHue: 0.5, // 蓝色
       },
     ];
@@ -303,75 +542,6 @@ function addZhuqiAnimate(zhuqiModal, modal) {
   return null;
 }
 
-function addArrow1(zhuqiModal) {
-  const changjingzhuqiguan = zhuqiModal.getObjectByName('changjing5');
-  if (changjingzhuqiguan) {
-    const zhuqiPips = changjingzhuqiguan.children.filter((item) => {
-      return item.name.includes('changjingcurve10');
-    });
-
-    const flowUniforms = {
-      uTime: { value: 0 },
-      uColor: { value: new THREE.Color(0x00aaff) }, // 流体颜色
-      uSpeed: { value: 0.5 }, // 流动速度
-      uBaseColor: { value: new THREE.Color(0x222222) }, // 管道底色
-    };
-
-    const flowMaterial = new THREE.ShaderMaterial({
-      uniforms: flowUniforms,
-      vertexShader: `
-        varying vec2 vUv;
-        void main() {
-            vUv = uv;
-            gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
-        }
-    `,
-      fragmentShader: `
-        uniform float uTime;
-        uniform vec3 uColor;
-        uniform float uSpeed;
-        uniform vec3 uBaseColor;
-        varying vec2 vUv;
-
-        void main() {
-            // 核心:沿 V 轴 (长度方向) 移动纹理
-            // fract 实现无限循环
-            float flow = fract(vUv.y + uTime * uSpeed);
-            
-            // 简单的条纹效果
-            float stripe = step(0.8, fract(flow * 5.0)); 
-            
-            // 混合颜色:底色 + 流动的高亮色
-            vec3 finalColor = mix(uBaseColor, uColor, stripe * 0.8);
-            
-            // 增加一点菲涅尔效果让它看起来更立体 (可选)
-            gl_FragColor = vec4(finalColor, 1.0);
-        }
-    `,
-      side: THREE.DoubleSide,
-    });
-
-    const material1 = new THREE.MeshStandardMaterial({
-      color: '#f00',
-      roughness: 1,
-      metalness: 1,
-    });
-    debugger;
-    zhuqiPips.forEach((item) => {
-      // const oldGeometry = item.geometry.clone();
-      // // const geometry = new THREE.TubeGeometry(oldGeometry.parameters.path, oldGeometry.parameters.tubularSegments, 0.05, 8, false);
-      // const newMesh = new THREE.Mesh(oldGeometry, flowMaterial.clone());
-      // item.parent.add(newMesh);
-      // // item.parent.remove(item);
-      // item.visible = false;
-      // // material.needsUpdate = true;
-      // // item.material = material;
-    });
-    return { zhuqiPips, flowMaterial };
-  }
-  return null;
-}
-
 function addArrow(zhuqiModal, curveNum = 8) {
   const changjing5 = zhuqiModal.getObjectByName('changjing5');
   if (changjing5) {
@@ -394,7 +564,7 @@ function addArrow(zhuqiModal, curveNum = 8) {
     // 绘制钻孔曲线
     // 根据份数计算均线位置, max.x - min.x 为总长, 端点坐标
     // 纵长
-    const num1 = 9; // 钻孔个数+1
+    const num1 = curveNum + 1; // 钻孔个数+1
     const gap = (max.x - (min.x + 200)) / num1;
     const curvePoints: THREE.Vector3[] = [];
     for (let i = 0; i < num1; i++) {
@@ -451,7 +621,7 @@ function addArrow(zhuqiModal, curveNum = 8) {
     }
 
     const textureLoader = new THREE.TextureLoader();
-    const texture = textureLoader.load(`/public/texture/2-1.png`);
+    const texture = textureLoader.load(`/texture/2-1.png`);
 
     // 设置阵列模式 RepeatWrapping
     texture.wrapS = THREE.RepeatWrapping;
@@ -485,10 +655,16 @@ function addArrow(zhuqiModal, curveNum = 8) {
       pCube53.add(mesh);
 
       const geometry1 = new THREE.TubeGeometry(new THREE.CatmullRomCurve3(vec3List), 100, 40, 8, false);
-      const mesh1 = new THREE.Mesh(geometry1, tubeMaterial);
+      const mesh1 = new THREE.Mesh(geometry1, tubeMaterial.clone());
+      mesh.name = 'zk' + i;
       pCube53.add(mesh1);
+      pCube53.updateMatrixWorld();
+      pCube53.updateMatrix();
       mesh1.renderOrder = 1;
+      zhuqiZkList.push(mesh1);
     }
+
+    zhuqiZkList.reverse();
     return { texture };
   }
   return null;
@@ -530,7 +706,7 @@ function addUnit(zhuqiModal, unitNum = 8) {
     // }
 
     const textureLoader = new THREE.TextureLoader();
-    const texture = textureLoader.load(`/public/texture/3-2.png`);
+    const texture = textureLoader.load(`/texture/3-2.png`);
 
     // 设置阵列模式 RepeatWrapping
     texture.wrapS = THREE.RepeatWrapping;
@@ -582,7 +758,7 @@ function addUnit(zhuqiModal, unitNum = 8) {
         const material = new THREE.MeshPhongMaterial({
           color: '#f00',
         });
-        const mesh = new THREE.Mesh(geometry, tubeMaterial);
+        const mesh = new THREE.Mesh(geometry, tubeMaterial.clone());
         pCube53.add(mesh);
 
         // p1 p2 p3 p4
@@ -622,9 +798,10 @@ function addUnit(zhuqiModal, unitNum = 8) {
           width: 200,
           arrow: false,
         });
-        const mesh1 = new THREE.Mesh(geometry1, tubeMaterial);
+        const mesh1 = new THREE.Mesh(geometry1, tubeMaterial.clone());
         mesh1.renderOrder = 99;
         pCube53.add(mesh1);
+        gasUnitList.push(mesh1);
       }
     }
     return { arrowTexture: texture };
@@ -632,6 +809,159 @@ function addUnit(zhuqiModal, unitNum = 8) {
   return null;
 }
 
+function addPip(zhuqiModal, curveNum = 8) {
+  const zhuqiguandao = zhuqiModal.getObjectByName('zhuqiguandao');
+  const changjingpPipe37 = zhuqiModal.getObjectByName('guandao').getObjectByName('changjingpPipe37');
+  const box = new THREE.Box3();
+  box.setFromObject(changjingpPipe37);
+  const min = box.min;
+  const max = box.max;
+  const endP = new THREE.Vector3(max.x - 30, min.y + (max.y - min.y) / 2, min.z + (max.z - min.z) / 2);
+  const startP = new THREE.Vector3(min.x + 30, min.y + (max.y - min.y) / 2, min.z + (max.z - min.z) / 2);
+
+  const endP0 = new THREE.Vector3(max.x, min.y + (max.y - min.y) / 2, min.z + (max.z - min.z) / 2);
+  const startP0 = new THREE.Vector3(min.x, min.y + (max.y - min.y) / 2, min.z + (max.z - min.z) / 2);
+
+  const len = endP.x - startP.x;
+  const gap = len / curveNum;
+
+  const zhuqiguandaoList: THREE.Object3D[] = [];
+
+  for (let i = 0; i < curveNum; i++) {
+    const position = new THREE.Vector3(0 + gap * i, 0, 0);
+    const zhuqiguandaoClone = zhuqiguandao.clone(); // 创建克隆
+    zhuqiguandaoClone.name = 'zhuqiguandao' + i;
+    zhuqiguandaoClone.position.copy(position);
+
+    // 添加点击标识
+    zhuqiModal.add(zhuqiguandaoClone);
+    zhuqiguandaoList.push(zhuqiguandaoClone);
+  }
+  zhuqiguandao.visible = false;
+
+  const { texture } = pipAnimation(zhuqiguandaoList, startP0, endP0, zhuqiModal);
+
+  return { texture };
+}
+
+function pipAnimation(zhuqiguandaoList, startP, endP, zhuqiModal) {
+  const zhuqiModalGroup = new THREE.Object3D();
+  zhuqiModalGroup.name = 'zhuqiModalGroup';
+
+  const textureLoader = new THREE.TextureLoader();
+  const texture = textureLoader.load(`/texture/2-1.png`);
+
+  // 设置阵列模式 RepeatWrapping
+  texture.wrapS = THREE.RepeatWrapping;
+  texture.wrapT = THREE.RepeatWrapping;
+  // 设置x方向的重复数(沿着管道路径方向)
+  // 设置y方向的重复数(环绕管道方向)
+  texture.repeat.x = 20;
+  texture.repeat.y = 6;
+  // 设置管道纹理偏移数,便于对中
+  texture.offset.y = 100;
+  const tubeMaterial = new THREE.MeshPhongMaterial({
+    map: texture,
+    color: '#fff',
+    transparent: true,
+    opacity: 1.0,
+  });
+  const material = new THREE.MeshStandardMaterial({
+    color: '#4F4F4F',
+    roughness: 0.31,
+    metalness: 0.74,
+  });
+
+  const zhuqi_dian = zhuqiModal.getObjectByName('zhuqi_dian');
+  const dian4 = zhuqi_dian.getObjectByName('dian4');
+  const dian3 = zhuqi_dian.getObjectByName('dian3');
+
+  const center = new THREE.Vector3();
+  const box4 = new THREE.Box3();
+  box4.setFromObject(dian4);
+  box4.getCenter(center);
+
+  const pipSP = new THREE.Vector3();
+  const box3 = new THREE.Box3();
+  box3.setFromObject(dian3);
+  box3.getCenter(pipSP);
+
+  // 构建三段管道
+  const pipGeometry1 = new THREE.TubeGeometry(new THREE.CatmullRomCurve3([pipSP, center], false, 'centripetal'), 30, 8, 10, false);
+  const pipGeometry2 = new THREE.TubeGeometry(new THREE.CatmullRomCurve3([center, startP], false, 'centripetal'), 30, 8, 10, false);
+  const pipGeometry3 = new THREE.TubeGeometry(new THREE.CatmullRomCurve3([center, endP], false, 'centripetal'), 30, 8, 10, false);
+
+  const pip1 = new THREE.Mesh(pipGeometry1, tubeMaterial);
+  const pip2 = new THREE.Mesh(pipGeometry2, tubeMaterial);
+  const pip3 = new THREE.Mesh(pipGeometry3, tubeMaterial);
+
+  zhuqiModal.add(pip1, pip2, pip3);
+
+  try {
+    for (let i = 0; i < zhuqiguandaoList.length; i++) {
+      const zhuqiguandao = zhuqiguandaoList[i];
+      const zhuqi_dian_1 = zhuqiguandao.getObjectByName('zhuqi_dian_1');
+
+      const dian1 = zhuqi_dian_1?.getObjectByName('dian2') as THREE.Object3D;
+      const dian2 = zhuqi_dian_1?.getObjectByName('dian1') as THREE.Object3D;
+      const dian3 = zhuqi_dian_1?.getObjectByName('dian') as THREE.Object3D;
+
+      const position1 = new THREE.Vector3();
+      const position2 = new THREE.Vector3();
+      const position3 = new THREE.Vector3();
+
+      const box1 = new THREE.Box3();
+      box1.setFromObject(dian1);
+      box1.getCenter(position1);
+
+      const box2 = new THREE.Box3();
+      box2.setFromObject(dian2);
+      box2.getCenter(position2);
+
+      const box3 = new THREE.Box3();
+      box3.setFromObject(dian3);
+      box3.getCenter(position3);
+
+      const position21 = position2.clone().setY(position2.y - 5);
+      const position22 = position2.clone().setZ(position2.z + 5);
+      const curve = new THREE.QuadraticBezierCurve3(position22, position2, position21);
+      const points = curve.getPoints(30);
+
+      const geometry = new THREE.TubeGeometry(new THREE.CatmullRomCurve3([position1, ...points, position3], false, 'chordal'), 20, 6, 10, false);
+      const mesh = new THREE.Mesh(geometry, tubeMaterial.clone());
+      mesh.position.copy(zhuqiguandao.position);
+      mesh.name = 'pip' + i;
+      zhuqiModal.add(mesh);
+      zhuqiPipList.push(mesh);
+
+      // 绘制点击区域
+      // 鼠标经过时显示线框
+      const material = new THREE.MeshBasicMaterial({
+        color: '#f00',
+        transparent: true,
+        opacity: 1,
+      });
+      const geometry1 = new THREE.TubeGeometry(new THREE.CatmullRomCurve3([position1, ...points, position3], false, 'chordal'), 20, 6, 10, false);
+      // const mesh1 = new THREE.Mesh(geometry1, material);
+      const mesh1 = new THREE.Line(geometry1, material);
+      mesh1.position.copy(zhuqiguandao.position);
+      mesh1.name = 'pipLine' + i;
+      // mesh1.visible = false;
+      zhuqiModal.add(mesh1);
+      pipList.push(mesh1);
+    }
+  } catch (error) {
+    debugger;
+  }
+  // zhuqiModal.add(zhuqiModalGroup);
+
+  return { texture };
+}
+
+function initCss2DContainer(modal) {
+  modal.initCSS3Renderer('#css3dContainer');
+}
+
 /**
  * 计算满足不等式 gap*2 > (len - gap*2)/num 的最小临界值
  * @param {number} len - 总长度
@@ -649,3 +979,107 @@ function getMinGapThreshold(len, num) {
   // 公式推导结果: gap > len / (2 * (num + 1))
   return len / (2 * (num + 1));
 }
+
+/***绘制波纹标识 */
+function createRippleMarker() {
+  // 主容器(方便整体控制)
+  const marker = new THREE.Group();
+
+  // --------------------------
+  // 配置参数(可自由修改)
+  // --------------------------
+  const config = {
+    radius: 10, // 标注大小
+    baseColor: 0x00aaff, // 底色
+    rippleColor: 0xffffff, // 波纹颜色
+    rippleCount: 3, // 波纹层数
+    animateSpeed: 1.5, // 动画速度
+    opacity: 0.8, // 整体透明度
+  };
+
+  // --------------------------
+  // 1)底层静态圆形
+  // --------------------------
+  const baseGeometry = new THREE.CircleGeometry(config.radius, 32);
+  const baseMaterial = new THREE.MeshBasicMaterial({
+    color: config.baseColor,
+    transparent: true,
+    opacity: config.opacity * 0.3,
+  });
+  const baseCircle = new THREE.Mesh(baseGeometry, baseMaterial);
+  marker.add(baseCircle);
+
+  // --------------------------
+  // 2)动态波纹(多层)
+  // --------------------------
+  const ripples = [];
+  for (let i = 0; i < config.rippleCount; i++) {
+    const geo = new THREE.RingGeometry(
+      0, // 内半径
+      config.radius, // 外半径
+      config.radius * 2
+    );
+    const mat = new THREE.MeshBasicMaterial({
+      color: config.rippleColor,
+      transparent: true,
+      opacity: 0,
+      side: THREE.DoubleSide,
+      depthWrite: false, // 防止透明遮挡
+    });
+    const ripple = new THREE.Mesh(geo, mat);
+    ripple.userData.delay = i * 0.4; // 错开动画
+    ripple.userData.speed = config.animateSpeed;
+    ripples.push(ripple);
+    marker.add(ripple);
+  }
+
+  // --------------------------
+  // 3)动画更新函数
+  // --------------------------
+  marker.userData.update = (delta) => {
+    ripples.forEach((rip: THREE.Mesh) => {
+      let t = (delta * rip.userData.speed - rip.userData.delay) % 3;
+      if (t < 0) t += 3;
+
+      // 波纹从中心向外扩散
+      const progress = Math.min(t / 2, 1);
+      const radius = progress * config.radius;
+
+      // 更新环形大小
+      rip.geometry.dispose();
+      rip.geometry = new THREE.RingGeometry(radius * 0.8, radius, 32);
+
+      // 透明度:出现 → 最亮 → 消失
+      rip.material.opacity = progress < 0.5 ? progress * 2 : (1 - progress) * 0.8;
+    });
+  };
+
+  // marker.userData.lookAtCamera = true;
+  return marker;
+}
+
+function addMarker(zhuqiguandao: THREE.Object3D) {
+  const posObj1 = zhuqiguandao?.getObjectByName('网格004_6');
+  const box1 = new THREE.Box3();
+  box1.setFromObject(posObj1);
+  const center1 = box1.getCenter(new THREE.Vector3());
+  const marker_cgq = createRippleMarker();
+  const position = new THREE.Vector3(center1.x, center1.y, center1.z);
+  marker_cgq.position.copy(position.setZ(position.z - 10));
+  marker_cgq.name = 'cgq';
+  marker_cgq.rotateY(Math.PI / 2);
+  zhuqiguandao.add(marker_cgq);
+  cgqMarkerList.push(marker_cgq);
+
+  const posObj2 = zhuqiguandao?.getObjectByName('网格004_2');
+  const box2 = new THREE.Box3();
+  box2.setFromObject(posObj2);
+  const center2 = box2.getCenter(new THREE.Vector3());
+  const marker_llj = createRippleMarker();
+  const pos = new THREE.Vector3(center2.x, center2.y, center2.z);
+  marker_llj.position.copy(pos);
+  marker_llj.name = 'llj';
+  zhuqiguandao.add(marker_llj);
+  lljMarkerList.push(marker_llj);
+}
+/***绘制波纹标识 */