Kaynağa Gözat

模型库相关3d完成

zhangliang2 2 yıl önce
ebeveyn
işleme
2eb1f39088

+ 17 - 14
src/views/modelLibrary/components/formCamera.vue

@@ -37,7 +37,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'nearDistance' }"
                     v-model="form.nearDistance"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -47,7 +47,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'farDistance' }"
                     v-model="form.farDistance"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -57,7 +57,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovH' }"
                     v-model="form.fovH"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -67,6 +67,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovV' }"
                     v-model="form.fovV"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -129,9 +130,9 @@ export default {
             }
             callback();
         };
-        // 校验不大于180
-        let validateNoMore180 = (rule, value, callback) => {
-            if (value > 180) {
+        // 校验不大于90
+        let validateNoMore90 = (rule, value, callback) => {
+            if (value > 90) {
                 callback(new Error(rule.message));
                 return;
             }
@@ -204,8 +205,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -217,8 +218,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -315,13 +316,15 @@ export default {
             if (
                 this.form.farDistance === "" ||
                 this.form.nearDistance === "" ||
-                this.form.fovH === ""
+                this.form.fovH === "" ||
+                this.form.fovV === ""
             )
                 return;
             this.$emit("inputChange", {
-                r: this.form.farDistance,
-                h: this.form.fovH,
-                n: this.form.nearDistance,
+                n: +this.form.nearDistance,
+                f: +this.form.farDistance,
+                h: +this.form.fovH,
+                v: +this.form.fovV,
             });
         },
     },

+ 16 - 13
src/views/modelLibrary/components/formLaserRadar.vue

@@ -37,7 +37,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'nearDistance' }"
                     v-model="form.nearDistance"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -47,7 +47,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'farDistance' }"
                     v-model="form.farDistance"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -57,7 +57,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovH' }"
                     v-model="form.fovH"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -67,6 +67,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovV' }"
                     v-model="form.fovV"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -139,9 +140,9 @@ export default {
             }
             callback();
         };
-        // 校验不大于180
-        let validateNoMore180 = (rule, value, callback) => {
-            if (value > 180) {
+        // 校验不大于90
+        let validateNoMore90 = (rule, value, callback) => {
+            if (value > 90) {
                 callback(new Error(rule.message));
                 return;
             }
@@ -229,8 +230,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -339,15 +340,17 @@ export default {
         },
         inputChange() {
             if (
-                this.form.farDistance === "" ||
                 this.form.nearDistance === "" ||
-                this.form.fovH === ""
+                this.form.farDistance === "" ||
+                this.form.fovH === "" ||
+                this.form.fovV === ""
             )
                 return;
             this.$emit("inputChange", {
-                r: this.form.farDistance,
-                h: this.form.fovH,
-                n: this.form.nearDistance,
+                n: +this.form.nearDistance,
+                f: +this.form.farDistance,
+                h: +this.form.fovH,
+                v: +this.form.fovV,
             });
         },
     },

+ 26 - 20
src/views/modelLibrary/components/formPerfectSensor.vue

@@ -37,7 +37,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'nearDistance' }"
                     v-model="form.nearDistance"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -47,7 +47,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'farDistance' }"
                     v-model="form.farDistance"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -57,7 +57,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovHLeft' }"
                     v-model="form.fovHLeft"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -67,7 +67,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovHRight' }"
                     v-model="form.fovHRight"
-                    @input="inputChange"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -77,6 +77,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovVTop' }"
                     v-model="form.fovVTop"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -86,6 +87,7 @@
                     maxlength="12"
                     v-autoTrim="{ obj: form, key: 'fovVBottom' }"
                     v-model="form.fovVBottom"
+                    @change="inputChange"
                 >
                 </el-input>
             </el-form-item>
@@ -160,9 +162,9 @@ export default {
             }
             callback();
         };
-        // 校验不大于180
-        let validateNoMore180 = (rule, value, callback) => {
-            if (value > 180) {
+        // 校验不大于90
+        let validateNoMore90 = (rule, value, callback) => {
+            if (value > 90) {
                 callback(new Error(rule.message));
                 return;
             }
@@ -237,8 +239,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -250,8 +252,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -263,8 +265,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -276,8 +278,8 @@ export default {
                         trigger: ["blur"],
                     },
                     {
-                        validator: validateNoMore180,
-                        message: "请输入不大于180的非负数",
+                        validator: validateNoMore90,
+                        message: "请输入不大于90的非负数",
                         trigger: ["blur"],
                     },
                 ],
@@ -402,14 +404,18 @@ export default {
                 this.form.farDistance === "" ||
                 this.form.nearDistance === "" ||
                 this.form.fovHLeft === "" ||
-                this.form.fovHRight === ""
+                this.form.fovHRight === "" ||
+                this.form.fovVTop === "" ||
+                this.form.fovVBottom === ""
             )
                 return;
             this.$emit("inputChange", {
-                r: this.form.farDistance,
-                n: this.form.nearDistance,
-                hl: this.form.fovHLeft,
-                hr: this.form.fovHRight,
+                n: +this.form.nearDistance,
+                f: +this.form.farDistance,
+                hl: +this.form.fovHLeft,
+                hr: +this.form.fovHRight,
+                vt: +this.form.fovVTop,
+                vb: +this.form.fovVBottom,
             });
         },
     },

+ 450 - 0
src/views/modelLibrary/components/threeSensorModel.vue

@@ -0,0 +1,450 @@
+<template>
+    <div>
+        <div id="containerThreeSensorModel"></div>
+    </div>
+</template>
+
+<script>
+import * as THREE from "three";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import { ConvexGeometry } from "three/examples/jsm/geometries/ConvexGeometry.js";
+
+export default {
+    name: "threeSensorModel", // 传感器模型中的threeJS
+    components: {},
+
+    data() {
+        return {
+            publicPath: process.env.BASE_URL,
+            scene: null,
+            camera: null,
+            renderer: null,
+            light: null,
+            transformControls: null,
+            geometryName: null,
+            controls: null,
+            mesh: null,
+            cube: null,
+            cacheList: [],
+            container: null,
+            car: null,
+            cubeTexture: null,
+            raf: null,
+            canDrag: true, // 是否可移动
+            dragControls: null,
+            scale: 1.5, // 物体加载换算倍数
+        };
+    },
+
+    props: {
+        coordinate: {
+            type: Object,
+            default: { f: 0, n: 0, h: 0, v: 0 },
+        },
+        coordinateA: {
+            type: Object,
+            default: { f: 0, n: 0, hl: 0, hr: 0, vl: 0, vb: 0 },
+        },
+        type: {
+            type: Number,
+            default: 1,
+        },
+    },
+
+    methods: {
+        // 场景
+        initScene() {
+            this.scene = new THREE.Scene();
+            let axes = new THREE.AxesHelper(1500);
+            this.scene.add(axes);
+            const gridHelper = new THREE.GridHelper(1000, 100);
+            gridHelper.material.opacity = 0.25;
+            gridHelper.material.transparent = true;
+            this.scene.add(gridHelper);
+        },
+        // 相机
+        initCamera() {
+            /* this.camera = new THREE.OrthographicCamera(
+                this.container.clientWidth / -2,
+                this.container.clientWidth / 2,
+                this.container.clientHeight / 2,
+                this.container.clientHeight / -2,
+                1,
+                // 0.1,
+                1000
+            ); */
+
+            this.camera = new THREE.PerspectiveCamera(
+                75,
+                this.container.clientWidth / this.container.clientHeight,
+                0.1,
+                1000
+            );
+            // this.camera.layers.set(1);
+
+            // this.camera = new THREE.PerspectiveCamera(45, 1.5, 1, 1000);
+            // this.camera.position.set(600, 600, 600);
+            this.camera.position.set(200, 200, 200);
+            // this.camera.lookAt(this.scene.position);
+            this.scene.add(this.camera);
+        },
+        // 渲染器
+        initRenderer() {
+            this.renderer = new THREE.WebGLRenderer({
+                antialias: true,
+                alpha: true,
+            });
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+            this.renderer.setClearColor("#272727");
+            this.container.appendChild(this.renderer.domElement);
+        },
+        // 初始化灯光
+        initLight() {
+            var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
+            hemiLight.position.set(0, 20, 0);
+            this.scene.add(hemiLight);
+
+            // 环境光会均匀的照亮场景中的所有物体
+            const light = new THREE.AmbientLight(0x5c5c5c, 0.4); // soft white light
+            this.scene.add(light);
+
+            // 点光源
+            // const light1 = new THREE.PointLight(0xffffff, 2, 100);
+            // light1.position.set(0, 0, -100);
+            // this.scene.add(light1);
+
+            // 平行光是沿着特定方向发射的光
+            const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
+            // const dirLight = new THREE.DirectionalLight(0xffffff);
+            // dirLight.position.set(0, 200, 100);
+            // dirLight.castShadow = true;
+            // dirLight.shadow.camera.top = 180;
+            // dirLight.shadow.camera.bottom = -100;
+            // dirLight.shadow.camera.left = -120;
+            // dirLight.shadow.camera.right = 120;
+
+            // dirLight.shadow.camera.near = 0.01;
+            // dirLight.shadow.camera.far = 60;
+            // dirLight.shadow.camera.top = 22;
+            // dirLight.shadow.camera.bottom = -22;
+            // dirLight.shadow.camera.left = -35;
+            // dirLight.shadow.camera.right = 35;
+            // // //设置阴影分辨率
+            // dirLight.shadow.mapSize.width = 2048; // default
+            // dirLight.shadow.mapSize.height = 2048; // default
+            // //阴影限制
+            // dirLight.shadow.radius = 1;
+            this.scene.add(dirLight);
+
+            // const spotLight = new THREE.SpotLight(0xffffff);
+            // spotLight.position.set(100, 1000, 100);
+
+            // spotLight.castShadow = true;
+
+            // spotLight.shadow.mapSize.width = 1024;
+            // spotLight.shadow.mapSize.height = 1024;
+
+            // spotLight.shadow.camera.near = 500;
+            // spotLight.shadow.camera.far = 4000;
+            // spotLight.shadow.camera.fov = 30;
+
+            // this.scene.add(spotLight);
+        },
+        // 初始化模型
+        initContent() {
+            const obj = this.scene.getObjectByName("cube");
+            if (obj) {
+                this.scene.remove(obj);
+                this.clearCache(obj);
+            }
+
+            if (this.type != 2) {
+                let r = this.coordinate.f;
+                let r1 = this.coordinate.n;
+
+                if (r <= 0 || r1 < 0 || r <= r1) {
+                    return;
+                }
+
+                this.initSenA(r * this.scale, r1 * this.scale);
+            } else {
+                let r = this.coordinateA.f;
+                let r1 = this.coordinateA.n;
+
+                if (r <= 0 || r1 < 0 || r <= r1) {
+                    return;
+                }
+
+                this.initSenB(r * this.scale, r1 * this.scale);
+            }
+        },
+        // 处理轴对称物体
+        initSenA(r, r1) {
+            let x = 0,
+                y = 0,
+                z = r;
+
+            let a = this.coordinate.h,
+                b = this.coordinate.v;
+
+            if (a >= 90) a = 89.99;
+            if (b >= 90) b = 89.99;
+            // console.log(Math.tan(THREE.MathUtils.degToRad(0)));
+
+            x = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            y = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+
+            let pointsArr = [];
+
+            if (r1 === 0) {
+                pointsArr = [
+                    [0, 0, 0],
+                    [x, y, z],
+                    [x, -y, z],
+                    [-x, -y, z],
+                    [-x, y, z],
+                ];
+            } else {
+                let x1 = 0,
+                    y1 = 0,
+                    z1 = r1;
+
+                x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+
+                pointsArr = [
+                    [x1, y1, z1],
+                    [x1, -y1, z1],
+                    [-x1, -y1, z1],
+                    [-x1, y1, z1],
+                    [x, y, z],
+                    [x, -y, z],
+                    [-x, -y, z],
+                    [-x, y, z],
+                ];
+            }
+
+            this.initMesh(pointsArr);
+        },
+        // 处理非轴对称物体
+        initSenB(r, r1) {
+            let x1 = 0,
+                x2 = 0,
+                y1 = 0,
+                y2 = 0,
+                z1 = r;
+
+            let a = this.coordinateA.hl,
+                b = this.coordinateA.vt,
+                c = this.coordinateA.hr,
+                d = this.coordinateA.vb;
+
+            if (a >= 90) a = 89.99;
+            if (b >= 90) b = 89.99;
+            if (c >= 90) c = 89.99;
+            if (d >= 90) d = 89.99;
+
+            x1 = Math.tan(THREE.MathUtils.degToRad(a)) * r;
+            y1 = Math.tan(THREE.MathUtils.degToRad(b)) * r;
+            x2 = Math.tan(THREE.MathUtils.degToRad(c)) * r;
+            y2 = Math.tan(THREE.MathUtils.degToRad(d)) * r;
+
+            let pointsArr = [];
+
+            if (r1 === 0) {
+                pointsArr = [
+                    [0, 0, 0],
+                    [x1, y1, z1],
+                    [x1, -y2, z1],
+                    [-x2, -y2, z1],
+                    [-x2, y1, z1],
+                ];
+            } else {
+                let m1 = 0,
+                    m2 = 0,
+                    n1 = 0,
+                    n2 = 0,
+                    z2 = r1;
+
+                m1 = Math.tan(THREE.MathUtils.degToRad(a)) * r1;
+                n1 = Math.tan(THREE.MathUtils.degToRad(b)) * r1;
+                m2 = Math.tan(THREE.MathUtils.degToRad(c)) * r1;
+                n2 = Math.tan(THREE.MathUtils.degToRad(d)) * r1;
+
+                pointsArr = [
+                    [m1, n1, z2],
+                    [m1, -n2, z2],
+                    [-m2, -n2, z2],
+                    [-m2, n1, z2],
+                    [x1, y1, z1],
+                    [x1, -y2, z1],
+                    [-x2, -y2, z1],
+                    [-x2, y1, z1],
+                ];
+            }
+
+            this.initMesh(pointsArr);
+        },
+        initMesh(pointsArr) {
+            let points = pointsArr.map(
+                (d) => new THREE.Vector3(d[0], d[1], d[2])
+            );
+
+            let color = null;
+            if (this.type === 1) {
+                color = 0x0000ff;
+            } else if (this.type === 2) {
+                color = 0x008000;
+            } else if (this.type === 3) {
+                color = 0xff4500;
+            } else {
+                color = 0x0000ff;
+            }
+
+            let material = new THREE.MeshPhongMaterial({
+                emissive: color,
+                // shininess: 60,
+                // specular: this.color,
+                color: 0x4c4c4c,
+                transparent: true,
+                opacity: 0.3,
+                lightMapIntensity: 0.1,
+            });
+
+            let tixing = new ConvexGeometry(points);
+            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
+            let tixingMesh = new THREE.Mesh(tixing, material);
+            tixingMesh.name = "cube";
+            this.scene.add(tixingMesh);
+        },
+        // 初始化
+        init() {
+            this.initScene();
+            this.initCamera();
+            this.initRenderer();
+            this.initLight();
+            // this.initContent();
+
+            this.controls = new OrbitControls(
+                this.camera,
+                this.renderer.domElement
+            ); //创建控件对象
+            this.controls.update();
+        },
+        animate() {
+            this.raf = requestAnimationFrame(this.animate);
+            this.renderer.render(this.scene, this.camera);
+            if (this.transformControls) {
+                this.transformControls.update();
+            }
+
+            this.controls.update();
+        },
+        onWindowResize() {
+            this.camera.aspect =
+                this.container.clientWidth / this.container.clientHeight;
+            this.camera.updateProjectionMatrix();
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+        },
+        go() {
+            this.container = document.getElementById(
+                "containerThreeSensorModel"
+            );
+            this.init();
+            this.animate();
+
+            window.addEventListener("resize", this.onWindowResize);
+        },
+        removeScene() {
+            this.clearScene();
+        },
+        clearCache(item) {
+            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
+            if (item.material && item.material.dispose) item.material.dispose();
+        },
+        clearScene() {
+            this.removeObj(this.scene);
+        },
+        removeObj(obj) {
+            let arr = obj.children.filter((x) => x);
+            arr.forEach((item) => {
+                if (item.children.length) {
+                    this.removeObj(item);
+                } else {
+                    this.clearCache(item);
+                    item.clear();
+                }
+            }),
+                obj.clear();
+            arr = null;
+        },
+    },
+
+    mounted() {
+        this.go();
+    },
+    destroyed() {
+        window.removeEventListener("resize", this.onWindowResize);
+        cancelAnimationFrame(this.raf);
+
+        if (this.renderer) {
+            this.renderer.renderLists.dispose();
+            this.renderer.dispose();
+            this.renderer.forceContextLoss();
+            this.renderer.domElement = null;
+            this.renderer.content = null;
+            this.renderer = null;
+        }
+
+        if (this.dragControls) {
+            this.dragControls.deactivate();
+            this.dragControls.dispose();
+            this.dragControls = null;
+        }
+
+        if (this.controls) {
+            this.controls.dispose();
+            this.controls = null;
+        }
+
+        if (this.transformControls) {
+            this.transformControls.detach();
+            this.transformControls.dispose();
+            this.transformControls = null;
+        }
+
+        if (this.cacheList.length > 0) {
+            this.scene.remove(...this.cacheList);
+            this.cacheList = [];
+        }
+
+        this.clearScene();
+
+        this.scene = null;
+        this.camera = null;
+        this.light = null;
+        this.geometryName = null;
+        this.mesh = null;
+        this.cube = null;
+        this.container = null;
+        this.car = null;
+        this.cubeTexture = null;
+        this.raf = null;
+
+        THREE.Cache.clear();
+    },
+};
+</script>
+
+<style lang="less" scoped>
+#containerThreeSensorModel {
+    width: 100%;
+    height: calc(100vh - 180px);
+}
+</style>

+ 412 - 137
src/views/modelLibrary/components/threeVehicleConfiguration.vue

@@ -14,6 +14,11 @@ import { MTLLoader } from "three/examples/jsm/loaders/MTLLoader.js";
 import { FBXLoader } from "three/examples/jsm/loaders/FBXLoader.js";
 import { DDSLoader } from "three/examples/jsm/loaders/DDSLoader.js";
 import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+import { ConvexGeometry } from "three/examples/jsm/geometries/ConvexGeometry.js";
+import {
+    showFullScreenLoading,
+    tryHideFullScreenLoading,
+} from "../../../axios/filter";
 
 export default {
     name: "threeVehicleConfiguration", // 车辆配置详情中的threeJS
@@ -43,6 +48,8 @@ export default {
             dragControls: null,
             sensor: null, // 当前操作的传感器模型
             ogt: null,
+            scale: 1.5, // 物体加载换算倍数
+            rate: 20, // 坐标换算倍数
         };
     },
 
@@ -121,6 +128,7 @@ export default {
             if (obj) {
                 obj.rotation.set(this.xAngle, this.yAngle, this.zAngle);
             }
+
             // obj.rotateX(this.xAngle * -1);
             // obj.rotateX(value);
 
@@ -163,8 +171,12 @@ export default {
         // 场景
         initScene() {
             this.scene = new THREE.Scene();
-            let axes = new THREE.AxesHelper(500);
+            let axes = new THREE.AxesHelper(1500);
             this.scene.add(axes);
+            const gridHelper = new THREE.GridHelper(1000, 100);
+            gridHelper.material.opacity = 0.25;
+            gridHelper.material.transparent = true;
+            this.scene.add(gridHelper);
 
             const genCubeUrls = function (prefix, postfix) {
                 return [
@@ -183,7 +195,7 @@ export default {
             const cubeTextureLoader = new THREE.CubeTextureLoader();
             const environmentMapTexture = cubeTextureLoader.load(urls);
 
-            this.scene.background = environmentMapTexture;
+            // this.scene.background = environmentMapTexture;
         },
         // 相机
         initCamera() {
@@ -221,7 +233,7 @@ export default {
                 this.container.clientWidth,
                 this.container.clientHeight
             );
-            this.renderer.setClearColor("#ddd");
+            this.renderer.setClearColor("#272727");
             this.container.appendChild(this.renderer.domElement);
         },
         // 初始化灯光
@@ -260,7 +272,7 @@ export default {
             // dirLight.shadow.mapSize.height = 2048; // default
             // //阴影限制
             // dirLight.shadow.radius = 1;
-            this.scene.add(dirLight);
+            // this.scene.add(dirLight);
 
             // const spotLight = new THREE.SpotLight(0xffffff);
             // spotLight.position.set(100, 1000, 100);
@@ -320,13 +332,13 @@ export default {
         // 初始化模型
         initContent(r, position, rotation, type) {
             var cubeGeometry = new THREE.ConeGeometry(
-                r || 45,
-                150,
+                r || 45 * this.scale,
+                150 * this.scale,
                 4,
                 1,
                 false
             );
-            cubeGeometry.translate(0, -75, 0);
+            cubeGeometry.translate(0, -150, 0);
 
             let obj = {
                 color: 0x4c4c4c,
@@ -424,13 +436,37 @@ export default {
                 this.controls.enabled = true;
             });
         },
-        initCar0() {
+        initCar0(model) {
+            if (this.car) {
+                console.log(this.car);
+                this.scene.remove(this.car);
+                this.removeObj(this.car);
+                this.car = null;
+            }
+
+            // const loading = this.$loading({
+            //     lock: true,
+            //     text: "模型加载中,请稍等...",
+            //     // spinner: "el-icon-loading",
+            //     background: "rgba(0, 0, 0, 0.2)",
+            // });
+
+            showFullScreenLoading();
+
             var that = this;
             var loader = new GLTFLoader(); //创建一个FBX加载器
-            loader.load(`${this.publicPath}untitled.glb`, function (obj) {
-                console.log(obj);
 
-                /*  for (let i = 0; i < obj.children.length; i++) {
+            // loader.load(
+            //     `${this.publicPath}glb/AudiA6_10.glb`,
+            //     function (obj) {
+            loader.load(
+                model,
+                function (obj) {
+                    // loading.close();
+                    tryHideFullScreenLoading();
+                    // console.log(obj);
+
+                    /*  for (let i = 0; i < obj.children.length; i++) {
                     // obj.children[i].position.set(0, -135, 0);
                     // obj.children[i].position.set(18, 36, 0);
                     // obj.children[i].scale.set(30, 30, 30);
@@ -444,18 +480,31 @@ export default {
                     // that.transformControls.attach(obj.children[i]);
                 } */
 
-                obj.rotation.set(
-                    (-90 * Math.PI) / 180,
-                    0,
-                    (-180 * Math.PI) / 180
-                );
-                // obj.scale.set(30, 30, 30);
+                    obj.scene.rotation.set(
+                        (-90 * Math.PI) / 180,
+                        0,
+                        (-180 * Math.PI) / 180
+                    );
 
-                // 查看动画数据  2个剪辑对象AnimationClip,一个有关键帧动画,一个没有
-                // console.log(obj.animations);
-                // that.scene.add(obj);
-                that.scene.add(obj.scene);
-            });
+                    let scale = 30 * that.scale;
+                    obj.scene.scale.set(scale, scale, scale);
+                    // obj.scene.scale.set(30, 30, 30);
+
+                    // 查看动画数据  2个剪辑对象AnimationClip,一个有关键帧动画,一个没有
+                    // console.log(obj.animations);
+                    // that.scene.add(obj);
+                    that.scene.add(obj.scene);
+                    that.car = obj.scene;
+                },
+                (xhr) => {
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    // loading.close();
+                    tryHideFullScreenLoading();
+                    console.error(error);
+                }
+            );
         },
         initCar1() {
             const loader2 = new DDSLoader();
@@ -476,47 +525,59 @@ export default {
             var loader = new FBXLoader(); //创建一个FBX加载器
             // var loader = new GLTFLoader(); //创建一个FBX加载器
             // loader.load(`${this.publicPath}6/a8/audia8.fbx`, function (obj) {
-            loader.load(`${this.publicPath}q5/audiq5.fbx`, function (obj) {
-                // loader.load(`${this.publicPath}q6/audiq5.fbx`, function (obj) {
-                // loader.load(`${this.publicPath}untitled.fbx`, function (obj) {
-                // loader.load(`${this.publicPath}untitled.glb`, function (obj) {
-                // loader.load(`${this.publicPath}fbx/Samba Dancing.fbx`, function (obj) {
-                // 可以在控制台打印obj对象,找到animations属性
-                console.log(obj);
-                obj.name = "car";
+            // loader.load(`${this.publicPath}6/q5/audiq5.fbx`, function (obj) {
+            // loader.load(`${this.publicPath}6/a8/audia8.fbx`, function (obj) {
+            // loader.load(`${this.publicPath}a8/audia8.fbx`, function (obj) {
+            // loader.load(`${this.publicPath}q5/audiq5.fbx`, function (obj) {
+            // loader.load(`${this.publicPath}lb/audiq5.fbx`, function (obj) {
+            loader.load(
+                `${this.publicPath}lb/1/fbx/untitled.fbx`,
+                function (obj) {
+                    // loader.load(`${this.publicPath}q6/audiq5.fbx`, function (obj) {
+                    // loader.load(`${this.publicPath}untitled.fbx`, function (obj) {
+                    // loader.load(`${this.publicPath}untitled.glb`, function (obj) {
+                    // loader.load(`${this.publicPath}fbx/Samba Dancing.fbx`, function (obj) {
+                    // 可以在控制台打印obj对象,找到animations属性
+                    console.log(obj);
+                    obj.name = "car";
+
+                    // obj.map = map1;
+                    function _ChangeMaterialEmissive(parent) {
+                        parent.traverse(function (child) {
+                            // if (child instanceof THREE.Mesh) {
+                            if (child.isMesh) {
+                                // child.material.emissive = new THREE.Color(1, 1, 1);
+                                // child.material.emissive = child.material.color;
+                                // child.material.emissiveIntensity = 1;
+                                // child.material.emissiveMap = child.material.map;
+                                // child.material.envMap = child.material.map;
+                                // child.material.alphaMap = child.material.map;
+                                // child.material.aoMap = child.material.map;
+                                // child.material.bumpMap = child.material.map;
+                                // child.material.lightMap = child.material.map;
+                                // child.material.normalMap = child.material.map;
+                                // child.material.specularMap = child.material.map;
+                                // child.material.transparent = true;
+                                child.castShadow = true;
+                                child.receiveShadow = true;
+                            }
+                        });
+                    }
 
-                // obj.map = map1;
-
-                function _ChangeMaterialEmissive(parent) {
-                    parent.traverse(function (child) {
-                        // if (child instanceof THREE.Mesh) {
-                        if (child.isMesh) {
-                            // child.material.emissive = new THREE.Color(1, 1, 1);
-                            child.material.emissive = child.material.color;
-                            // child.material.emissiveIntensity = 1;
-                            child.material.emissiveMap = child.material.map;
-                            // child.material.envMap = child.material.map;
-                            // child.material.emissiveMap = map3;
-                            child.castShadow = true;
-                            child.receiveShadow = true;
-                        }
-                    });
-                }
+                    _ChangeMaterialEmissive(obj);
 
-                _ChangeMaterialEmissive(obj);
-
-                // obj.traverse(function (child) {
-                //     if (child.isMesh) {
-                //         // child.material.emissive = new THREE.Color(1, 1, 1);
-                //         child.material.emissive = child.material.color;
-                //         child.material.emissiveIntensity = 1;
-                //         child.material.emissiveMap = child.material.map;
-                //         child.castShadow = true;
-                //         child.receiveShadow = true;
-                //     }
-                // });
+                    // obj.traverse(function (child) {
+                    //     if (child.isMesh) {
+                    //         // child.material.emissive = new THREE.Color(1, 1, 1);
+                    //         child.material.emissive = child.material.color;
+                    //         child.material.emissiveIntensity = 1;
+                    //         child.material.emissiveMap = child.material.map;
+                    //         child.castShadow = true;
+                    //         child.receiveShadow = true;
+                    //     }
+                    // });
 
-                /*  for (let i = 0; i < obj.children.length; i++) {
+                    /*  for (let i = 0; i < obj.children.length; i++) {
                     // obj.children[i].position.set(0, -135, 0);
                     // obj.children[i].position.set(18, 36, 0);
                     // obj.children[i].scale.set(30, 30, 30);
@@ -530,18 +591,19 @@ export default {
                     // that.transformControls.attach(obj.children[i]);
                 } */
 
-                obj.rotation.set(
-                    (-90 * Math.PI) / 180,
-                    0,
-                    (-180 * Math.PI) / 180
-                );
-                obj.scale.set(30, 30, 30);
+                    obj.rotation.set(
+                        (-90 * Math.PI) / 180,
+                        0,
+                        (-180 * Math.PI) / 180
+                    );
+                    // obj.scale.set(30, 30, 30);
 
-                // 查看动画数据  2个剪辑对象AnimationClip,一个有关键帧动画,一个没有
-                // console.log(obj.animations);
-                that.scene.add(obj);
-                // that.scene.add(obj.scene);
-            });
+                    // 查看动画数据  2个剪辑对象AnimationClip,一个有关键帧动画,一个没有
+                    // console.log(obj.animations);
+                    that.scene.add(obj);
+                    // that.scene.add(obj.scene);
+                }
+            );
         },
         initCar2() {
             var that = this;
@@ -577,7 +639,10 @@ export default {
                 // `${that.publicPath}GTR.mtl`,
                 // `${that.publicPath}lidar.mtl`,
                 // `${that.publicPath}female02/female02.mtl`,
-                `${that.publicPath}new/audiq5.mtl`,
+                // `${that.publicPath}new/audiq5.mtl`,
+                // `${that.publicPath}lb/obj/audiq5.mtl`,
+                // `${that.publicPath}lb/00obj/00.mtl`,
+                `${that.publicPath}lb/1/obj/untitled.mtl`,
                 function (materials) {
                     // 返回一个包含材质的对象MaterialCreator
                     // console.log(materials);
@@ -590,7 +655,9 @@ export default {
                     loader.load(
                         // "../../../../public/female02/female02.obj",
                         // `${that.publicPath}female02/female02.obj`,
-                        `${that.publicPath}new/audiq5.obj`,
+                        // `${that.publicPath}new/audiq5.obj`,
+                        // `${that.publicPath}lb/00obj/00.obj`,
+                        `${that.publicPath}lb/1/obj/untitled.obj`,
                         // `${that.publicPath}male02/male02.obj`,
                         // `${that.publicPath}GTR.obj`,
                         // `${that.publicPath}lidar.obj`,
@@ -606,7 +673,8 @@ export default {
 
                             for (let i = 0; i < obj.children.length; i++) {
                                 // obj.children[i].position.set(0, -135, 0);
-                                obj.children[i].position.set(0, -30, 0);
+                                // obj.children[i].position.set(0, -30, 0);
+                                obj.children[i].position.set(0, 0, 0);
                                 // obj.children[i].scale.set(30, 30, 30);
                                 obj.children[i].rotation.set(
                                     (-90 * Math.PI) / 180,
@@ -637,7 +705,7 @@ export default {
             );
         },
         // 初始化车模型
-        initCar(type = "1") {
+        initCar(model) {
             // const loading = this.$loading({
             //     lock: true,
             //     text: "模型加载中,请稍等...",
@@ -645,12 +713,12 @@ export default {
             //     background: "rgba(0, 0, 0, 0.2)",
             // });
 
-            const car = this.scene.getObjectByName("car");
+            // const car = this.scene.getObjectByName("car");
             // console.log(car);
             // console.log(this.scene);
-            if (car) {
-                this.scene.remove(car);
-            }
+            // if (car) {
+            //     this.scene.remove(car);
+            // }
 
             // console.log(type);
 
@@ -664,7 +732,11 @@ export default {
             //     this.initCar1();
             // }
 
-            this.initCar1();
+            console.log(model);
+            if (!model) return;
+            if (!model.includes(".glb")) return;
+
+            this.initCar0(model);
 
             // console.log(this.scene);
 
@@ -753,8 +825,12 @@ export default {
         // 每编辑一个传感器则重新生成对应的物体
         reset(type) {
             this.canDrag = true;
+
             if (this.cacheList.length > 0) {
                 this.scene.remove(...this.cacheList);
+                this.cacheList.forEach((i) => {
+                    this.clearCache(i);
+                });
                 this.cacheList = [];
             }
 
@@ -776,6 +852,10 @@ export default {
             let p = +this.$parent.formA.sensorR || 0;
             let r = +this.$parent.formA.sensorH || 0;
 
+            x = Math.floor(x / this.rate);
+            y = Math.floor(y / this.rate);
+            z = Math.floor(z / this.rate);
+
             this.initContent(
                 null,
                 { x, y, z },
@@ -798,7 +878,7 @@ export default {
 
                 // obj.rotateX(0 * -1);
                 // obj.rotateX((h * Math.PI) / 180);
-                this.xAngle = ((h-90) * Math.PI) / 180;
+                this.xAngle = ((h - 90) * Math.PI) / 180;
 
                 // obj.rotateY(0 * -1);
                 // obj.rotateY((p * Math.PI) / 180);
@@ -876,55 +956,85 @@ export default {
                 objUrl = `${that.publicPath}sensor/gps/lidar.obj`;
             }
 
-            Loader.load(mtlUrl, function (materials) {
-                // console.log(materials);
-                loader.setMaterials(materials);
-                loader.load(objUrl, function (obj) {
-                    // console.log(obj);
-                    // console.log(obj.clone());
-                    // console.log(JSON.stringify(obj.toJSON()));
-                    // if (type === "ogt" && !that.ogt) {
-                    //     that.ogt = obj.clone();
-                    // }
+            let loading = null;
+            if (type === "ogt") {
+                // loading = this.$loading({
+                //     lock: true,
+                //     text: "模型加载中,请稍等...",
+                //     // spinner: "el-icon-loading",
+                //     background: "rgba(0, 0, 0, 0.2)",
+                // });
+                showFullScreenLoading();
+            }
 
-                    that.cacheList.push(obj);
-                    for (let i = 0; i < obj.children.length; i++) {
-                        if (type === "camera") {
-                            obj.children[i].scale.set(0.8, 0.8, 0.8);
-                        } else if (type === "ogt") {
-                            obj.children[i].scale.set(0.2, 0.2, 0.2);
-                        } else if (type === "lidar") {
-                            obj.children[i].scale.set(0.1, 0.1, 0.1);
-                        } else if (type === "gps") {
-                            obj.children[i].scale.set(40, 40, 40);
+            Loader.load(
+                mtlUrl,
+                function (materials) {
+                    // console.log(materials);
+                    loader.setMaterials(materials);
+                    loader.load(objUrl, function (obj) {
+                        // console.log(obj);
+                        // console.log(obj.clone());
+                        // console.log(JSON.stringify(obj.toJSON()));
+                        // if (type === "ogt" && !that.ogt) {
+                        //     that.ogt = obj.clone();
+                        // }
+
+                        if (type === "ogt") {
+                            // loading.close();
+                            tryHideFullScreenLoading();
                         }
-                        // obj.children[i].position.set(0, 0, 0);
-                        // obj.children[i].geometry.translate(pos.x, pos.y, pos.z);
-                        // obj.children[i].position.set(pos.x, pos.y, pos.z);
-                        // obj.children[i].geometry.translate(-pos.x, -pos.y, -pos.z);
-                        // obj.children[i].scale.set(30, 30, 30);
-                    }
-                    obj.position.set(pos.x, pos.y, pos.z);
-                    // obj.geometry.translate.set(pos.x, pos.y, pos.z);
 
-                    if (canMove) {
-                        that.sensor = obj;
-                    }
+                        that.cacheList.push(obj);
+                        for (let i = 0; i < obj.children.length; i++) {
+                            if (type === "camera") {
+                                // let scale = 0.8 * that.scale;
+                                obj.children[i].scale.set(0.8, 0.8, 0.8);
+                            } else if (type === "ogt") {
+                                obj.children[i].scale.set(0.2, 0.2, 0.2);
+                            } else if (type === "lidar") {
+                                obj.children[i].scale.set(0.1, 0.1, 0.1);
+                            } else if (type === "gps") {
+                                obj.children[i].scale.set(40, 40, 40);
+                            }
+                            // obj.children[i].position.set(0, 0, 0);
+                            // obj.children[i].geometry.translate(pos.x, pos.y, pos.z);
+                            // obj.children[i].position.set(pos.x, pos.y, pos.z);
+                            // obj.children[i].geometry.translate(-pos.x, -pos.y, -pos.z);
+                            // obj.children[i].scale.set(30, 30, 30);
+                        }
+                        obj.position.set(pos.x, pos.y, pos.z);
+                        // obj.geometry.translate.set(pos.x, pos.y, pos.z);
 
-                    that.scene.add(obj); //返回的组对象插入场景中
-                });
-            });
+                        if (canMove) {
+                            that.sensor = obj;
+                        }
+
+                        that.scene.add(obj); //返回的组对象插入场景中
+                    });
+                },
+                (xhr) => {
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    if (type === "ogt") {
+                        // loading.close();
+                        tryHideFullScreenLoading();
+                    }
+                    console.error(error);
+                }
+            );
         },
         // 初始化已保存过的传感器
         initContentToShow(r, position, rotation, type) {
             var cubeGeometry = new THREE.ConeGeometry(
-                r || 45,
-                150,
+                r || 45 * this.scale,
+                150 * this.scale,
                 4,
                 1,
                 false
             );
-            cubeGeometry.translate(0, -75, 0);
+            cubeGeometry.translate(0, -150, 0);
 
             let obj = {
                 transparent: true,
@@ -963,12 +1073,18 @@ export default {
                 cube.position.z = position.z;
             }
             if (rotation) {
-                cube.rotateX(0 * -1);
-                cube.rotateX((rotation.x * Math.PI) / 180);
-                cube.rotateY(0 * -1);
-                cube.rotateY((rotation.y * Math.PI) / 180);
-                cube.rotateZ(0 * -1);
-                cube.rotateZ((rotation.z * Math.PI) / 180);
+                // cube.rotateX(0 * -1);
+                // cube.rotateX((rotation.x * Math.PI) / 180);
+                // cube.rotateY(0 * -1);
+                // cube.rotateY((rotation.y * Math.PI) / 180);
+                // cube.rotateZ(0 * -1);
+                // cube.rotateZ((rotation.z * Math.PI) / 180);
+
+                cube.rotation.set(
+                    ((rotation.x - 90) * Math.PI) / 180,
+                    (rotation.y * Math.PI) / 180,
+                    (rotation.z * Math.PI) / 180
+                );
             }
             this.cacheList.push(cube);
             this.scene.add(cube);
@@ -980,9 +1096,9 @@ export default {
                     this.initContentToShow(
                         null,
                         {
-                            x: +item.sensorX,
-                            y: +item.sensorY,
-                            z: +item.sensorZ,
+                            x: Math.floor(item.sensorX / this.rate || 0),
+                            y: Math.floor(item.sensorY / this.rate || 0),
+                            z: Math.floor(item.sensorZ / this.rate || 0),
                         },
                         {
                             x: +item.sensorP,
@@ -994,9 +1110,9 @@ export default {
 
                     this.initSensor(
                         {
-                            x: +item.sensorX,
-                            y: +item.sensorY,
-                            z: +item.sensorZ,
+                            x: Math.floor(item.sensorX / this.rate || 0),
+                            y: Math.floor(item.sensorY / this.rate || 0),
+                            z: Math.floor(item.sensorZ / this.rate || 0),
                         },
                         type,
                         false
@@ -1006,6 +1122,9 @@ export default {
         },
         // 显示全部
         showAll() {
+            // 避免重复加载所有传感器
+            if (!this.canDrag) return;
+
             this.canDrag = false;
             if (this.cacheList.length > 0) {
                 this.scene.remove(...this.cacheList);
@@ -1027,6 +1146,8 @@ export default {
                 this.transformControls.detach();
             }
 
+            console.log(this.configList);
+
             this.showSensor(this.configList.camera, "camera");
             this.showSensor(this.configList.ogt, "ogt");
             this.showSensor(this.configList.lidar, "lidar");
@@ -1055,16 +1176,170 @@ export default {
                 obj.clear();
             arr = null;
         },
+        showTX() {
+            var pointsArr1 = [
+                // [5, -1, 33],
+                // [5, 11, 33],
+                // [-5, -1, 33],
+                // [-5, 11, 33],
+                [0, 0, 0],
+                [10, -11, 66],
+                [30, 31, 66],
+                [-20, -21, 66],
+                [-30, 21, 66],
+            ];
+
+            let x = 0,
+                y = 0,
+                z = 0;
+
+            let r = 60;
+            z = r;
+
+            let a = 120,
+                b = 40;
+
+            x = Math.tan(THREE.MathUtils.degToRad(a / 2)) * r;
+            y = Math.tan(THREE.MathUtils.degToRad(b / 2)) * r;
+
+            let x1 = 0,
+                y1 = 0,
+                z1 = 0;
+
+            let r1 = 20;
+            z1 = r1;
+
+            x1 = Math.tan(THREE.MathUtils.degToRad(a / 2)) * r1;
+            y1 = Math.tan(THREE.MathUtils.degToRad(b / 2)) * r1;
+
+            let pointsArr2 = [
+                [0, 0, 0],
+                // [-x1, y1, z1],
+                // [-x1, -y1, z1],
+                // [x1, y1, z1],
+                // [x1, -y1, z1],
+                [-x, y, z],
+                [-x, -y, z],
+                [x, y, z],
+                [x, -y, z],
+            ];
+
+            let x2 = 0,
+                y2 = 0,
+                z2 = 0,
+                x3 = 0,
+                y3 = 0,
+                z3 = 0,
+                x4 = 0,
+                y4 = 0,
+                z4 = 0,
+                x6 = 0,
+                y6 = 0,
+                z6 = 0,
+                r2 = 90,
+                a2 = 30,
+                b2 = 60,
+                c2 = 90,
+                d2 = 60;
+
+            x2 = Math.tan(THREE.MathUtils.degToRad(a2 / 2)) * r2;
+            y2 = Math.tan(THREE.MathUtils.degToRad(b2 / 2)) * r2;
+            x3 = Math.tan(THREE.MathUtils.degToRad(c2 / 2)) * r2;
+            y3 = Math.tan(THREE.MathUtils.degToRad(d2 / 2)) * r2;
+
+            let pointsArr3 = [
+                [0, 0, 0],
+                [-x2, y3, r2],
+                [-x3, -y3, r2],
+                [x2, y2, r2],
+                [x3, -y2, r2],
+            ];
+            console.log(pointsArr3);
+
+            let points = pointsArr3.map(
+                (d) => new THREE.Vector3(d[0], d[1], d[2])
+            );
+
+            let material = new THREE.MeshPhongMaterial({
+                color: 0x2c85e1,
+                shininess: 60,
+                specular: 0x2c85e1,
+                // opacity: 0.5,
+                // transparent: true,
+            });
+
+            let tixing = new ConvexGeometry(points);
+            //通过ConvexGeometry凸包绘制一个梯台,当然也可以使用ExtrudeGeometry挤压几何体,
+            let tixingMesh = new THREE.Mesh(tixing, material);
+            this.scene.add(tixingMesh);
+            console.log(tixingMesh);
+
+            /* let vertices = [];
+
+            vertices.push(-155, 24, 90);
+            vertices.push(-63, -24, 90);
+            vertices.push(155, 91, 90);
+            vertices.push(63, -90, 90);
+
+            const geometry1 = new THREE.BufferGeometry();
+            geometry1.setAttribute(
+                "position",
+                new THREE.Float32BufferAttribute(vertices, 3)
+            );
+
+            const material1 = new THREE.PointsMaterial({
+                color: 0xff0000,
+                size: 10,
+                fog: false,
+            });
+
+            const points1 = new THREE.Points(geometry1, material1);
+
+            this.scene.add(points1); */
+
+            // if (this.transformControls) {
+            //     this.transformControls.attach(tixingMesh);
+            // }
+
+            // const a = new THREE.Vector3(1, 1, 0);
+            // const b = new THREE.Vector3(0, 0, 0);
+            // const d = a.distanceTo(b);
+            // const e = a.angleTo(b);
+
+            // console.log(d, e, (e * 180) / Math.PI, THREE.MathUtils.radToDeg(e));
+
+            // (value * Math.PI) / 180;
+        },
     },
 
     mounted() {
         this.go();
-
-        if (!this.isAdd) {
-            setTimeout(() => {
-                this.initCar();
-            }, 270);
-        }
+        // this.showTX();
+
+        // console.log(THREE.Loader.Handlers.add);
+        // THREE.Loader.Handlers.add( /\.dds$/i, new DDSLoader() );
+
+        // console.log(new THREE.LoadingManager()); RGBELoader
+        // let dds = new THREE.LoadingManager();
+        // dds.addHandler(/\.dds$/i, new DDSLoader());
+        // dds.addHandler(/\.rgb$/i, new RGBELoader());
+        // console.log(dds);
+
+        THREE.DefaultLoadingManager.addHandler(/\.dds$/i, new DDSLoader());
+        // THREE.DefaultLoadingManager.addHandler(/\.rgb$/i, new RGBELoader());
+        // console.log(THREE.DefaultLoadingManager);
+
+        // console.log(THREE.LoadingManager.addHandler);
+        // THREE.Loader.Handlers.add( /\.dds$/i, new DDSLoader());
+        // THREE.Loader.addHandler(/\.dds$/i, new DDSLoader());
+        // THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
+        // THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
+
+        // if (!this.isAdd) {
+        //     setTimeout(() => {
+        //         this.initCar();
+        //     }, 270);
+        // }
     },
     destroyed() {
         window.removeEventListener("resize", this.onWindowResize);
@@ -1097,7 +1372,7 @@ export default {
         }
 
         if (this.cacheList.length > 0) {
-            this.scene.remove(...this.cacheList);
+            // this.scene.remove(...this.cacheList);
             this.cacheList = [];
         }
 

+ 262 - 0
src/views/modelLibrary/components/threeVehicleModel.vue

@@ -0,0 +1,262 @@
+<template>
+    <div>
+        <div id="containerThreeVehicleModel"></div>
+    </div>
+</template>
+
+<script>
+import * as THREE from "three";
+import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
+import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js";
+import {
+    showFullScreenLoading,
+    tryHideFullScreenLoading,
+} from "../../../axios/filter";
+
+export default {
+    name: "threeVehicleModel", // 车辆模型中的threeJS
+
+    data() {
+        return {
+            publicPath: process.env.BASE_URL,
+            scene: null,
+            camera: null,
+            renderer: null,
+            light: null,
+            geometryName: null,
+            controls: null,
+            mesh: null,
+            cube: null,
+            container: null,
+            car: null,
+            cubeTexture: null,
+            raf: null,
+            scale: 1.5, // 物体加载换算倍数
+        };
+    },
+
+    props: {
+        carModel: {
+            type: String,
+            default: "",
+        },
+    },
+
+    watch: {
+        carModel(newVal, oldVal) {
+            console.log(newVal, oldVal);
+            if (newVal != oldVal) {
+                this.initCar(newVal);
+            }
+        },
+    },
+
+    methods: {
+        // 场景
+        initScene() {
+            this.scene = new THREE.Scene();
+            const gridHelper = new THREE.GridHelper(1000, 100);
+            gridHelper.material.opacity = 0.25;
+            gridHelper.material.transparent = true;
+            this.scene.add(gridHelper);
+        },
+        // 相机
+        initCamera() {
+            this.camera = new THREE.PerspectiveCamera(
+                75,
+                this.container.clientWidth / this.container.clientHeight,
+                0.1,
+                1000
+            );
+            this.camera.position.set(200, 200, 200);
+            this.scene.add(this.camera);
+        },
+        // 渲染器
+        initRenderer() {
+            this.renderer = new THREE.WebGLRenderer({
+                antialias: true,
+                alpha: true,
+            });
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+            this.renderer.setClearColor("#272727");
+            this.container.appendChild(this.renderer.domElement);
+        },
+        // 初始化灯光
+        initLight() {
+            var hemiLight = new THREE.HemisphereLight(0xffffff, 0x444444);
+            hemiLight.position.set(0, 20, 0);
+            this.scene.add(hemiLight);
+
+            // 环境光会均匀的照亮场景中的所有物体
+            const light = new THREE.AmbientLight(0x5c5c5c, 0.4); // soft white light
+            this.scene.add(light);
+
+            // 平行光是沿着特定方向发射的光
+            const dirLight = new THREE.DirectionalLight(0xffffff, 0.5);
+            this.scene.add(dirLight);
+        },
+        // 初始化车模型
+        initCar(model) {
+            if (this.car) {
+                this.scene.remove(this.car);
+                this.removeObj(this.car);
+                this.car = null;
+            }
+
+            console.log(model);
+            if (!model) return;
+            if (!model.includes(".glb")) return;
+
+            let that = this;
+            let loader = new GLTFLoader();
+
+            // const loading = this.$loading({
+            //     lock: true,
+            //     text: "模型加载中,请稍等...",
+            //     // spinner: "el-icon-loading",
+            //     background: "rgba(0, 0, 0, 0.2)",
+            // });
+
+            showFullScreenLoading();
+
+            // model = `${this.publicPath}glb/AudiA6_10.glb`
+
+            loader.load(
+                model,
+                function (obj) {
+                    console.log(obj);
+                    // loading.close();
+                    tryHideFullScreenLoading();
+
+                    obj.scene.rotation.set(
+                        (-90 * Math.PI) / 180,
+                        0,
+                        (-180 * Math.PI) / 180
+                    );
+
+                    let scale = 30 * that.scale;
+                    obj.scene.scale.set(scale, scale, scale);
+                    that.scene.add(obj.scene);
+                    that.car = obj.scene;
+                },
+                (xhr) => {
+                    // console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
+                },
+                (error) => {
+                    // loading.close();
+                    tryHideFullScreenLoading();
+                    console.error(error);
+                }
+            );
+        },
+        // 初始化
+        init() {
+            this.initScene();
+            this.initCamera();
+            this.initRenderer();
+            this.initLight();
+            // this.initCar(this.carModel);
+
+            this.controls = new OrbitControls(
+                this.camera,
+                this.renderer.domElement
+            ); //创建控件对象
+            this.controls.update();
+        },
+        animate() {
+            this.raf = requestAnimationFrame(this.animate);
+            this.renderer.render(this.scene, this.camera);
+
+            this.controls.update();
+        },
+        onWindowResize() {
+            this.camera.aspect =
+                this.container.clientWidth / this.container.clientHeight;
+            this.camera.updateProjectionMatrix();
+            this.renderer.setSize(
+                this.container.clientWidth,
+                this.container.clientHeight
+            );
+        },
+        go() {
+            this.container = document.getElementById(
+                "containerThreeVehicleModel"
+            );
+            this.init();
+            this.animate();
+
+            window.addEventListener("resize", this.onWindowResize);
+        },
+        removeScene() {
+            this.clearScene();
+        },
+        clearCache(item) {
+            if (item.geometry && item.geometry.dispose) item.geometry.dispose();
+            if (item.material && item.material.dispose) item.material.dispose();
+        },
+        clearScene() {
+            this.removeObj(this.scene);
+        },
+        removeObj(obj) {
+            let arr = obj.children.filter((x) => x);
+            arr.forEach((item) => {
+                if (item.children.length) {
+                    this.removeObj(item);
+                } else {
+                    this.clearCache(item);
+                    item.clear();
+                }
+            }),
+                obj.clear();
+            arr = null;
+        },
+    },
+
+    mounted() {
+        this.go();
+    },
+    destroyed() {
+        window.removeEventListener("resize", this.onWindowResize);
+        cancelAnimationFrame(this.raf);
+
+        if (this.renderer) {
+            this.renderer.renderLists.dispose();
+            this.renderer.dispose();
+            this.renderer.forceContextLoss();
+            this.renderer.domElement = null;
+            this.renderer.content = null;
+            this.renderer = null;
+        }
+
+        if (this.controls) {
+            this.controls.dispose();
+            this.controls = null;
+        }
+
+        this.clearScene();
+
+        this.scene = null;
+        this.camera = null;
+        this.light = null;
+        this.geometryName = null;
+        this.mesh = null;
+        this.cube = null;
+        this.container = null;
+        this.car = null;
+        this.cubeTexture = null;
+        this.raf = null;
+
+        THREE.Cache.clear();
+    },
+};
+</script>
+
+<style lang="less" scoped>
+#containerThreeVehicleModel {
+    width: 100%;
+    height: calc(100vh - 180px);
+}
+</style>

+ 46 - 11
src/views/modelLibrary/sensorModel.vue

@@ -10,6 +10,7 @@
             @delOne="delOne"
             :curOne="curOne"
         ></model-list>
+
         <div class="contentPanel">
             <toolbarTab
                 :isConnect="true"
@@ -60,7 +61,7 @@
                 ></form-gps>
                 <div class="model">
                     <!-- <img :src="imgSrc" width="100%" /> -->
-                    <canvas-sensor
+                    <!-- <canvas-sensor
                         v-show="subPageActiveName != 2"
                         ref="canvasSensor"
                         :coordinate="coordinate"
@@ -69,7 +70,13 @@
                         v-show="subPageActiveName === 2"
                         ref="canvasSensorA"
                         :coordinate="coordinateA"
-                    ></canvas-sensorA>
+                    ></canvas-sensorA> -->
+                    <three-show
+                        ref="threeShow"
+                        :coordinate="coordinate"
+                        :coordinateA="coordinateA"
+                        :type="subPageActiveName"
+                    ></three-show>
                 </div>
             </div>
             <div class="btns">
@@ -109,6 +116,7 @@ import formMillimeterWaveRadar from "./components/formMillimeterWaveRadar.vue";
 import formGps from "./components/formGps.vue";
 import canvasSensor from "./components/canvasSensorModel.vue";
 import canvasSensorA from "./components/canvasSensorModelA.vue";
+import threeShow from "./components/threeSensorModel.vue";
 
 export default {
     name: "sensorModel", // 传感器模型
@@ -122,6 +130,7 @@ export default {
         formGps,
         canvasSensor,
         canvasSensorA,
+        threeShow,
     },
     data() {
         return {
@@ -131,8 +140,10 @@ export default {
             fromId: 1,
             imgSrc: require("@/assets/common/image/others/carTopView.png"),
             currentShare: "",
-            coordinate: { r: 0, h: 0, n: 0 }, // 存放传递给canvas的值
-            coordinateA: { r: 0, n: 0, hl: 0, hr: 0 }, // 存放传递给完美传感器canvas的值
+            coordinate: { f: 0, n: 0, h: 0, v: 0 }, // 存放传递给three的值
+            coordinateA: { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 }, // 存放传递给three的值
+            // coordinate: { r: 0, h: 0, n: 0 }, // 存放传递给canvas的值
+            // coordinateA: { r: 0, n: 0, hl: 0, hr: 0 }, // 存放传递给完美传感器canvas的值
             // 用于当前选中项的展示
             curOne: {
                 share: "",
@@ -198,8 +209,14 @@ export default {
                 this.getList();
             });
 
-            this.$refs.canvasSensor.clear();
-            this.$refs.canvasSensorA.clear();
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.canvasSensorA.clear();
+
+            this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
+            this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
 
             this.curOne = {
                 share: "",
@@ -220,8 +237,14 @@ export default {
         },
         cancel() {
             this.$refs[`form${this.subPageActiveName}`].cancel();
-            this.$refs.canvasSensor.clear();
-            this.$refs.canvasSensorA.clear();
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.canvasSensorA.clear();
+
+            this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
+            this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
         },
         changeShare(form) {
             // 根据form传回的share进行按钮展示
@@ -246,8 +269,14 @@ export default {
             this.$refs[`form${this.subPageActiveName}`].form.id = "";
 
             this.currentShare = "";
-            this.$refs.canvasSensor.clear();
-            this.$refs.canvasSensorA.clear();
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.canvasSensorA.clear();
+
+            this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
+            this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
 
             this.curOne = {
                 share: "",
@@ -292,9 +321,15 @@ export default {
         },
         inputChange(data) {
             this.coordinate = data;
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
         },
         inputChangeA(data) {
             this.coordinateA = data;
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
         },
     },
 
@@ -327,7 +362,7 @@ export default {
             min-width: 300px;
             // padding-left: 40px;
             padding-left: 6%;
-            padding-right: 3%;
+            // padding-right: 3%;
         }
     }
 

+ 343 - 0
src/views/modelLibrary/sensorModelCanvas.vue

@@ -0,0 +1,343 @@
+<template>
+    <div class="sensorModelPanel">
+        <model-list
+            ref="modelList"
+            class="modelList"
+            :getListApi="getListApi"
+            :showName="showName"
+            @showInfo="showInfo"
+            @addOne="addOne"
+            @delOne="delOne"
+            :curOne="curOne"
+        ></model-list>
+        <div class="contentPanel">
+            <toolbarTab
+                :isConnect="true"
+                position="top"
+                :subPageActiveName="subPageActiveName"
+                :toolbarItem="subPageList"
+                @toolbarClick="toolsControl"
+            >
+            </toolbarTab>
+
+            <div class="tabContent">
+                <form-camera
+                    v-show="subPageActiveName === 1"
+                    ref="form1"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @showDone="showDone"
+                    @clearForm="addOne"
+                    @inputChange="inputChange"
+                ></form-camera>
+                <form-perfect-sensor
+                    v-show="subPageActiveName === 2"
+                    ref="form2"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @showDone="showDone"
+                    @clearForm="addOne"
+                    @inputChange="inputChangeA"
+                ></form-perfect-sensor>
+                <form-laser-radar
+                    v-show="subPageActiveName === 3"
+                    ref="form3"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @showDone="showDone"
+                    @clearForm="addOne"
+                    @inputChange="inputChange"
+                ></form-laser-radar>
+                <!-- <form-millimeter-wave-radar
+                    v-show="subPageActiveName === 4"
+                ></form-millimeter-wave-radar> -->
+                <form-gps
+                    v-show="subPageActiveName === 4"
+                    ref="form4"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @clearForm="addOne"
+                ></form-gps>
+                <div class="model">
+                    <!-- <img :src="imgSrc" width="100%" /> -->
+                    <canvas-sensor
+                        v-show="subPageActiveName != 2"
+                        ref="canvasSensor"
+                        :coordinate="coordinate"
+                    ></canvas-sensor>
+                    <canvas-sensorA
+                        v-show="subPageActiveName === 2"
+                        ref="canvasSensorA"
+                        :coordinate="coordinateA"
+                    ></canvas-sensorA>
+                </div>
+            </div>
+            <div class="btns">
+                <el-button
+                    type="primary"
+                    v-show="currentShare === '0' || currentShare === ''"
+                    @click="save(false)"
+                    >保存</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-if="roleCode === '0' || roleCode === '1'"
+                    v-show="currentShare === '0'"
+                    @click="share"
+                    >分享</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-show="currentShare === '0' || currentShare === '1'"
+                    @click="save(true)"
+                    >另存为</el-button
+                >
+                <el-button type="primary" plain @click="cancel">取消</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import modelList from "./components/modelList.vue";
+import toolbarTab from "@/components/toolbar/toolbarTab";
+import { mapState } from "vuex";
+import formCamera from "./components/formCamera.vue";
+import formPerfectSensor from "./components/formPerfectSensor.vue";
+import formLaserRadar from "./components/formLaserRadar.vue";
+import formMillimeterWaveRadar from "./components/formMillimeterWaveRadar.vue";
+import formGps from "./components/formGps.vue";
+import canvasSensor from "./components/canvasSensorModel.vue";
+import canvasSensorA from "./components/canvasSensorModelA.vue";
+
+export default {
+    name: "sensorModel", // 传感器模型
+    components: {
+        modelList,
+        toolbarTab,
+        formCamera,
+        formPerfectSensor,
+        formLaserRadar,
+        formMillimeterWaveRadar,
+        formGps,
+        canvasSensor,
+        canvasSensorA,
+    },
+    data() {
+        return {
+            getListApi: this.$api.modelLibrary.getCameraList,
+            showName: "sensorName",
+            subPageActiveName: 1,
+            fromId: 1,
+            imgSrc: require("@/assets/common/image/others/carTopView.png"),
+            currentShare: "",
+            coordinate: { r: 0, h: 0, n: 0 }, // 存放传递给canvas的值
+            coordinateA: { r: 0, n: 0, hl: 0, hr: 0 }, // 存放传递给完美传感器canvas的值
+            // 用于当前选中项的展示
+            curOne: {
+                share: "",
+                id: "",
+            },
+        };
+    },
+
+    computed: {
+        ...mapState(["configTitleList", "roleCode"]),
+        subPageList() {
+            return [
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[0],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getCameraList,
+                    fromId: 1,
+                },
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[1],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getOgtList,
+                    fromId: 2,
+                },
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[2],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getLidarList,
+                    fromId: 3,
+                },
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[3],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getGpsList,
+                    fromId: 4,
+                },
+                // {
+                //     type: "primary",
+                //     plain: true,
+                //     title: this.configTitleList[4],
+                //     disabled: false,
+                //     fromId: 5,
+                // },
+            ];
+        },
+    },
+
+    methods: {
+        toolsControl(item) {
+            this.addOne();
+            this.getListApi = item.api;
+            this.subPageActiveName = item.fromId;
+
+            this.$nextTick(() => {
+                this.getList();
+            });
+
+            this.$refs.canvasSensor.clear();
+            this.$refs.canvasSensorA.clear();
+
+            this.curOne = {
+                share: "",
+                id: "",
+            };
+        },
+        showInfo(id) {
+            this.$refs[`form${this.subPageActiveName}`].showInfo(id);
+        },
+        getList() {
+            this.$refs.modelList.getList();
+        },
+        save(isAdd) {
+            this.$refs[`form${this.subPageActiveName}`].save(isAdd);
+        },
+        share() {
+            this.$refs[`form${this.subPageActiveName}`].share();
+        },
+        cancel() {
+            this.$refs[`form${this.subPageActiveName}`].cancel();
+            this.$refs.canvasSensor.clear();
+            this.$refs.canvasSensorA.clear();
+        },
+        changeShare(form) {
+            // 根据form传回的share进行按钮展示
+            this.currentShare = form.share;
+
+            // 确定当前展示哪条数据
+            this.curOne = {
+                share: form.share,
+                id: form.id,
+            };
+        },
+        showDone() {
+            this.$refs[`form${this.subPageActiveName}`].inputChange();
+        },
+        addOne() {
+            this.$refs[
+                `form${this.subPageActiveName}`
+            ].$refs.form.resetFields();
+
+            this.$refs[`form${this.subPageActiveName}`].form.sensorCode = "";
+            this.$refs[`form${this.subPageActiveName}`].form.share = "";
+            this.$refs[`form${this.subPageActiveName}`].form.id = "";
+
+            this.currentShare = "";
+            this.$refs.canvasSensor.clear();
+            this.$refs.canvasSensorA.clear();
+
+            this.curOne = {
+                share: "",
+                id: "",
+            };
+        },
+        delOne(id) {
+            const delApis = [
+                this.$api.modelLibrary.delCameraById,
+                this.$api.modelLibrary.delOgtById,
+                this.$api.modelLibrary.delLidarById,
+                this.$api.modelLibrary.delGpsById,
+            ];
+            this.$axios({
+                method: "post",
+                url: delApis[this.subPageActiveName - 1],
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200) {
+                    this.$message.success("删除成功");
+                    this.$refs.modelList.getList();
+                    if (
+                        this.$refs[`form${this.subPageActiveName}`].form.id &&
+                        id ===
+                            this.$refs[`form${this.subPageActiveName}`].form.id
+                    ) {
+                        this.addOne();
+                    }
+
+                    if (id === this.curOne.id) {
+                        this.curOne = {
+                            share: "",
+                            id: "",
+                        };
+                    }
+                } else {
+                    this.$message.error(res.message || "删除失败");
+                }
+            });
+        },
+        inputChange(data) {
+            this.coordinate = data;
+        },
+        inputChangeA(data) {
+            this.coordinateA = data;
+        },
+    },
+
+    // mounted() {},
+};
+</script>
+
+<style lang='less' scoped>
+.sensorModelPanel {
+    display: flex;
+    flex: 1;
+    padding: 15px 30px 30px;
+
+    .modelList /deep/ .listPanel {
+        min-height: 400px;
+        height: calc(100vh - 200px);
+    }
+
+    .contentPanel {
+        flex: 1;
+        padding-left: 30px;
+    }
+
+    .tabContent {
+        display: flex;
+        padding-top: 15px;
+
+        .model {
+            flex: 1;
+            min-width: 300px;
+            // padding-left: 40px;
+            padding-left: 6%;
+            padding-right: 3%;
+        }
+    }
+
+    .btns {
+        padding-top: 30px;
+        padding-left: 390px;
+    }
+
+    /deep/ .el-tabs__item {
+        width: 150px;
+    }
+}
+</style>

+ 378 - 0
src/views/modelLibrary/sensorModelThree.vue

@@ -0,0 +1,378 @@
+<template>
+    <div class="sensorModelPanel">
+        <model-list
+            ref="modelList"
+            class="modelList"
+            :getListApi="getListApi"
+            :showName="showName"
+            @showInfo="showInfo"
+            @addOne="addOne"
+            @delOne="delOne"
+            :curOne="curOne"
+        ></model-list>
+
+        <div class="contentPanel">
+            <toolbarTab
+                :isConnect="true"
+                position="top"
+                :subPageActiveName="subPageActiveName"
+                :toolbarItem="subPageList"
+                @toolbarClick="toolsControl"
+            >
+            </toolbarTab>
+
+            <div class="tabContent">
+                <form-camera
+                    v-show="subPageActiveName === 1"
+                    ref="form1"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @showDone="showDone"
+                    @clearForm="addOne"
+                    @inputChange="inputChange"
+                ></form-camera>
+                <form-perfect-sensor
+                    v-show="subPageActiveName === 2"
+                    ref="form2"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @showDone="showDone"
+                    @clearForm="addOne"
+                    @inputChange="inputChangeA"
+                ></form-perfect-sensor>
+                <form-laser-radar
+                    v-show="subPageActiveName === 3"
+                    ref="form3"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @showDone="showDone"
+                    @clearForm="addOne"
+                    @inputChange="inputChange"
+                ></form-laser-radar>
+                <!-- <form-millimeter-wave-radar
+                    v-show="subPageActiveName === 4"
+                ></form-millimeter-wave-radar> -->
+                <form-gps
+                    v-show="subPageActiveName === 4"
+                    ref="form4"
+                    @getList="getList"
+                    @changeShare="changeShare"
+                    @clearForm="addOne"
+                ></form-gps>
+                <div class="model">
+                    <!-- <img :src="imgSrc" width="100%" /> -->
+                    <!-- <canvas-sensor
+                        v-show="subPageActiveName != 2"
+                        ref="canvasSensor"
+                        :coordinate="coordinate"
+                    ></canvas-sensor>
+                    <canvas-sensorA
+                        v-show="subPageActiveName === 2"
+                        ref="canvasSensorA"
+                        :coordinate="coordinateA"
+                    ></canvas-sensorA> -->
+                    <three-show
+                        ref="threeShow"
+                        :coordinate="coordinate"
+                        :coordinateA="coordinateA"
+                        :type="subPageActiveName"
+                    ></three-show>
+                </div>
+            </div>
+            <div class="btns">
+                <el-button
+                    type="primary"
+                    v-show="currentShare === '0' || currentShare === ''"
+                    @click="save(false)"
+                    >保存</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-if="roleCode === '0' || roleCode === '1'"
+                    v-show="currentShare === '0'"
+                    @click="share"
+                    >分享</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-show="currentShare === '0' || currentShare === '1'"
+                    @click="save(true)"
+                    >另存为</el-button
+                >
+                <el-button type="primary" plain @click="cancel">取消</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import modelList from "./components/modelList.vue";
+import toolbarTab from "@/components/toolbar/toolbarTab";
+import { mapState } from "vuex";
+import formCamera from "./components/formCamera.vue";
+import formPerfectSensor from "./components/formPerfectSensor.vue";
+import formLaserRadar from "./components/formLaserRadar.vue";
+import formMillimeterWaveRadar from "./components/formMillimeterWaveRadar.vue";
+import formGps from "./components/formGps.vue";
+import canvasSensor from "./components/canvasSensorModel.vue";
+import canvasSensorA from "./components/canvasSensorModelA.vue";
+import threeShow from "./components/threeSensorModel.vue";
+
+export default {
+    name: "sensorModel", // 传感器模型
+    components: {
+        modelList,
+        toolbarTab,
+        formCamera,
+        formPerfectSensor,
+        formLaserRadar,
+        formMillimeterWaveRadar,
+        formGps,
+        canvasSensor,
+        canvasSensorA,
+        threeShow,
+    },
+    data() {
+        return {
+            getListApi: this.$api.modelLibrary.getCameraList,
+            showName: "sensorName",
+            subPageActiveName: 1,
+            fromId: 1,
+            imgSrc: require("@/assets/common/image/others/carTopView.png"),
+            currentShare: "",
+            coordinate: { f: 0, n: 0, h: 0, v: 0 }, // 存放传递给three的值
+            coordinateA: { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 }, // 存放传递给three的值
+            // coordinate: { r: 0, h: 0, n: 0 }, // 存放传递给canvas的值
+            // coordinateA: { r: 0, n: 0, hl: 0, hr: 0 }, // 存放传递给完美传感器canvas的值
+            // 用于当前选中项的展示
+            curOne: {
+                share: "",
+                id: "",
+            },
+        };
+    },
+
+    computed: {
+        ...mapState(["configTitleList", "roleCode"]),
+        subPageList() {
+            return [
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[0],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getCameraList,
+                    fromId: 1,
+                },
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[1],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getOgtList,
+                    fromId: 2,
+                },
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[2],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getLidarList,
+                    fromId: 3,
+                },
+                {
+                    type: "primary",
+                    plain: true,
+                    title: this.configTitleList[3],
+                    disabled: false,
+                    api: this.$api.modelLibrary.getGpsList,
+                    fromId: 4,
+                },
+                // {
+                //     type: "primary",
+                //     plain: true,
+                //     title: this.configTitleList[4],
+                //     disabled: false,
+                //     fromId: 5,
+                // },
+            ];
+        },
+    },
+
+    methods: {
+        toolsControl(item) {
+            this.addOne();
+            this.getListApi = item.api;
+            this.subPageActiveName = item.fromId;
+
+            this.$nextTick(() => {
+                this.getList();
+            });
+
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.canvasSensorA.clear();
+
+            this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
+            this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
+
+            this.curOne = {
+                share: "",
+                id: "",
+            };
+        },
+        showInfo(id) {
+            this.$refs[`form${this.subPageActiveName}`].showInfo(id);
+        },
+        getList() {
+            this.$refs.modelList.getList();
+        },
+        save(isAdd) {
+            this.$refs[`form${this.subPageActiveName}`].save(isAdd);
+        },
+        share() {
+            this.$refs[`form${this.subPageActiveName}`].share();
+        },
+        cancel() {
+            this.$refs[`form${this.subPageActiveName}`].cancel();
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.canvasSensorA.clear();
+
+            this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
+            this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
+        },
+        changeShare(form) {
+            // 根据form传回的share进行按钮展示
+            this.currentShare = form.share;
+
+            // 确定当前展示哪条数据
+            this.curOne = {
+                share: form.share,
+                id: form.id,
+            };
+        },
+        showDone() {
+            this.$refs[`form${this.subPageActiveName}`].inputChange();
+        },
+        addOne() {
+            this.$refs[
+                `form${this.subPageActiveName}`
+            ].$refs.form.resetFields();
+
+            this.$refs[`form${this.subPageActiveName}`].form.sensorCode = "";
+            this.$refs[`form${this.subPageActiveName}`].form.share = "";
+            this.$refs[`form${this.subPageActiveName}`].form.id = "";
+
+            this.currentShare = "";
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.canvasSensorA.clear();
+
+            this.coordinate = { f: 0, n: 0, h: 0, v: 0 };
+            this.coordinateA = { f: 0, n: 0, hl: 0, hr: 0, vt: 0, vb: 0 };
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
+
+            this.curOne = {
+                share: "",
+                id: "",
+            };
+        },
+        delOne(id) {
+            const delApis = [
+                this.$api.modelLibrary.delCameraById,
+                this.$api.modelLibrary.delOgtById,
+                this.$api.modelLibrary.delLidarById,
+                this.$api.modelLibrary.delGpsById,
+            ];
+            this.$axios({
+                method: "post",
+                url: delApis[this.subPageActiveName - 1],
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200) {
+                    this.$message.success("删除成功");
+                    this.$refs.modelList.getList();
+                    if (
+                        this.$refs[`form${this.subPageActiveName}`].form.id &&
+                        id ===
+                            this.$refs[`form${this.subPageActiveName}`].form.id
+                    ) {
+                        this.addOne();
+                    }
+
+                    if (id === this.curOne.id) {
+                        this.curOne = {
+                            share: "",
+                            id: "",
+                        };
+                    }
+                } else {
+                    this.$message.error(res.message || "删除失败");
+                }
+            });
+        },
+        inputChange(data) {
+            this.coordinate = data;
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
+        },
+        inputChangeA(data) {
+            this.coordinateA = data;
+            setTimeout(() => {
+                this.$refs.threeShow.initContent();
+            }, 0);
+        },
+    },
+
+    // mounted() {},
+};
+</script>
+
+<style lang='less' scoped>
+.sensorModelPanel {
+    display: flex;
+    flex: 1;
+    padding: 15px 30px 30px;
+
+    .modelList /deep/ .listPanel {
+        min-height: 400px;
+        height: calc(100vh - 200px);
+    }
+
+    .contentPanel {
+        flex: 1;
+        padding-left: 30px;
+    }
+
+    .tabContent {
+        display: flex;
+        padding-top: 15px;
+
+        .model {
+            flex: 1;
+            min-width: 300px;
+            // padding-left: 40px;
+            padding-left: 6%;
+            // padding-right: 3%;
+        }
+    }
+
+    .btns {
+        padding-top: 30px;
+        padding-left: 390px;
+    }
+
+    /deep/ .el-tabs__item {
+        width: 150px;
+    }
+}
+</style>

+ 514 - 220
src/views/modelLibrary/vehicleConfigurationDetail.vue

@@ -1,193 +1,314 @@
 <template>
     <div class="vehicleConfigurationDetailPanel">
         <el-form ref="form" :model="form" :rules="rules" label-width="108px">
-            <div class="inputBox flexBox">
-                <span class="label">配置ID</span>
-                <div>{{ form.configCode }}</div>
-            </div>
-            <div class="flexBox headBox">
-                <el-form-item label="配置名称:" prop="configName">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="60"
-                        v-autoTrim="{ obj: form, key: 'configName' }"
-                        v-model="form.configName"
+            <el-row class="rowBox">
+                <el-col :span="6">
+                    <el-form-item label="配置名称:" prop="configName">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="60"
+                            v-autoTrim="{ obj: form, key: 'configName' }"
+                            v-model="form.configName"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                    <el-form-item label="配置描述:" prop="configDescription">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="200"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'configDescription',
+                            }"
+                            v-model="form.configDescription"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                    <el-form-item label="车辆名称:" prop="vehicleId">
+                        <el-select
+                            v-model="form.vehicleId"
+                            @change="vehicleChange"
+                        >
+                            <el-option
+                                v-for="item in vehicleNameList"
+                                :label="item.vehicleName"
+                                :value="item.id"
+                                :key="item.id"
+                            ></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                    <el-form-item label="车辆描述:" prop="vehicleDescription">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="200"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'vehicleDescription',
+                            }"
+                            v-model="form.vehicleDescription"
+                            disabled
+                        >
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+
+        <div class="panelContent">
+            <!-- <div class="infoLeft"> -->
+            <div class="infoLeft contentBox">
+                <div
+                    class="handleList"
+                    v-bind:class="{
+                        handleListA: form.share === '0' || form.share === '1',
+                    }"
+                >
+                    <el-scrollbar
+                        id="elScrollBarA"
+                        ref="scrollbarA"
+                        view-class="scroll"
                     >
-                    </el-input>
-                </el-form-item>
-                <el-form-item label="配置描述:" prop="configDescription">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="200"
-                        v-autoTrim="{ obj: form, key: 'configDescription' }"
-                        v-model="form.configDescription"
+                        <handle-config-list
+                            ref="handleConfigList"
+                            @curItem="curItem"
+                            @addOne="addOne"
+                            @delOne="delOne"
+                            :configList="configList"
+                            :curOne="curOne"
+                        ></handle-config-list>
+                    </el-scrollbar>
+                </div>
+
+                <!-- <div class="btns">
+                    <el-button
+                        v-if="form.share === '0' || form.share === ''"
+                        type="primary"
+                        @click="save(false)"
+                        >保存</el-button
                     >
-                    </el-input>
-                </el-form-item>
-            </div>
-            <div class="flexBox headBox">
-                <el-form-item label="车辆名称:" prop="vehicleId">
-                    <el-select v-model="form.vehicleId" @change="vehicleChange">
-                        <el-option
-                            v-for="item in vehicleNameList"
-                            :label="item.vehicleName"
-                            :value="item.id"
-                            :key="item.id"
-                        ></el-option>
-                    </el-select>
-                </el-form-item>
-                <el-form-item label="车辆描述:" prop="vehicleDescription">
-                    <el-input
-                        placeholder="请输入"
-                        maxlength="200"
-                        v-autoTrim="{ obj: form, key: 'vehicleDescription' }"
-                        v-model="form.vehicleDescription"
-                        disabled
+                    <el-button
+                        v-if="form.share === '1'"
+                        type="primary"
+                        @click="save(true)"
+                        >另存为</el-button
                     >
-                    </el-input>
-                </el-form-item>
-            </div>
-        </el-form>
+                    <el-button type="primary" plain @click="cancel"
+                        >取消</el-button
+                    >
+                </div>
 
-        <div class="contentBox">
-            <div>
-                <handle-config-list
-                    ref="handleConfigList"
-                    @curItem="curItem"
-                    @addOne="addOne"
-                    @delOne="delOne"
-                    :configList="configList"
-                    :curOne="curOne"
-                ></handle-config-list>
-            </div>
+                <div class="btnsA" v-if="form.share === '0'">
+                    <el-button
+                        v-if="form.share === '0'"
+                        type="primary"
+                        @click="save(true)"
+                        >另存为</el-button
+                    >
+                </div> -->
+
+                <div class="btns">
+                    <el-button
+                        v-if="form.share === '0' || form.share === ''"
+                        type="primary"
+                        @click="save(false)"
+                        >保存</el-button
+                    >
+                    <el-button
+                        v-if="form.share === '0' || form.share === '1'"
+                        type="primary"
+                        @click="save(true)"
+                        >另存为</el-button
+                    >
+                    <el-button type="primary" plain @click="cancel"
+                        >取消</el-button
+                    >
+                </div>
 
-            <div
-                class="model"
-                v-bind:style="{ width: width + 'px', height: width / 2 + 'px' }"
-            >
-                <!-- <img :src="modelImgSrc" width="100%" /> -->
-                <canvas-sensor
+                <!-- <canvas-sensor
                     ref="canvasSensor"
                     :coordinate="coordinate"
                     :modelImgSrc="modelImgSrc"
                     :curOneName="curOne.name"
-                ></canvas-sensor>
+                ></canvas-sensor> -->
+                <!-- </div> -->
+                <!-- </div> -->
             </div>
-
-            <div class="conditions">
-                <div class="btnBox">
-                    <el-button type="primary" @click="saveConfig"
-                        >保存设置</el-button
-                    >
+            <div class="threeRight">
+                <div class="viewBox">
+                    <span
+                        class="view el-icon-view"
+                        @click="showAll"
+                        title="显示全部"
+                    ></span>
                 </div>
 
-                <el-form
-                    ref="formA"
-                    :model="formA"
-                    :rules="rulesA"
-                    label-width="108px"
-                >
-                    <div class="forms">
-                        <el-form-item label="X(mm):" prop="sensorX">
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="12"
-                                v-autoTrim="{ obj: formA, key: 'sensorX' }"
-                                v-model="formA.sensorX"
-                                @input="inputChange"
-                            >
-                            </el-input>
-                        </el-form-item>
-                        <el-form-item label="Y(mm):" prop="sensorY">
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="12"
-                                v-autoTrim="{ obj: formA, key: 'sensorY' }"
-                                v-model="formA.sensorY"
-                                @input="inputChange"
-                            >
-                            </el-input>
-                        </el-form-item>
-                        <el-form-item label="Z(mm):" prop="sensorZ">
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="12"
-                                v-autoTrim="{ obj: formA, key: 'sensorZ' }"
-                                v-model="formA.sensorZ"
-                            >
-                            </el-input>
-                        </el-form-item>
-                        <el-form-item label="横摆角(deg):" prop="sensorH">
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="12"
-                                v-autoTrim="{ obj: formA, key: 'sensorH' }"
-                                v-model="formA.sensorH"
-                                @input="inputChange"
-                            >
-                            </el-input>
-                        </el-form-item>
-                        <el-form-item label="俯仰角(deg):" prop="sensorP">
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="12"
-                                v-autoTrim="{ obj: formA, key: 'sensorP' }"
-                                v-model="formA.sensorP"
-                            >
-                            </el-input>
-                        </el-form-item>
-                        <el-form-item label="翻滚角(deg):" prop="sensorR">
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="12"
-                                v-autoTrim="{ obj: formA, key: 'sensorR' }"
-                                v-model="formA.sensorR"
+                <three-sensor
+                    ref="threeSensor"
+                    :xValue="coordinate.x"
+                    :yValue="coordinate.y"
+                    :zValue="coordinate.z"
+                    :hValue="coordinate.p"
+                    :pValue="coordinate.r"
+                    :rValue="coordinate.h"
+                    :carModel="carModel"
+                    :isAdd="!!$route.query.id"
+                    :configList="configList"
+                    @posChange="posChange"
+                ></three-sensor>
+
+                <div v-show="configBox" class="conditions">
+                    <el-form
+                        ref="formA"
+                        :model="formA"
+                        :rules="rulesA"
+                        label-width="99px"
+                    >
+                        <div class="forms">
+                            <el-form-item label="X(mm):" prop="sensorX">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorX',
+                                    }"
+                                    v-model="formA.sensorX"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="Y(mm):" prop="sensorY">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorY',
+                                    }"
+                                    v-model="formA.sensorY"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="Z(mm):" prop="sensorZ">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorZ',
+                                    }"
+                                    v-model="formA.sensorZ"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="横摆角(deg):" prop="sensorH">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorH',
+                                    }"
+                                    v-model="formA.sensorH"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="俯仰角(deg):" prop="sensorP">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorP',
+                                    }"
+                                    v-model="formA.sensorP"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="翻滚角(deg):" prop="sensorR">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorR',
+                                    }"
+                                    v-model="formA.sensorR"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item
+                                label="端口"
+                                prop="sensorPort"
+                                v-show="curOne.name === 'ogt'"
                             >
-                            </el-input>
-                        </el-form-item>
-                        <el-form-item
-                            label="端口"
-                            prop="sensorPort"
-                            v-show="curOne.name === 'ogt'"
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="9"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorPort',
+                                    }"
+                                    v-model="formA.sensorPort"
+                                >
+                                </el-input>
+                            </el-form-item>
+                        </div>
+                    </el-form>
+
+                    <div class="btnBox">
+                        <el-button
+                            type="primary"
+                            @click="saveConfig"
+                            size="small"
+                            >保存设置</el-button
+                        >
+                        <el-button
+                            type="primary"
+                            @click="closeConfig"
+                            plain
+                            size="small"
+                            >关闭</el-button
                         >
-                            <el-input
-                                placeholder="请输入"
-                                maxlength="9"
-                                v-autoTrim="{ obj: formA, key: 'sensorPort' }"
-                                v-model="formA.sensorPort"
-                            >
-                            </el-input>
-                        </el-form-item>
                     </div>
-                </el-form>
+                </div>
             </div>
         </div>
-        <div class="btns">
-            <el-button
-                v-if="form.share === '0' || form.share === ''"
-                type="primary"
-                @click="save(false)"
-                >保存</el-button
-            >
-            <el-button
-                v-if="form.share === '0' || form.share === '1'"
-                type="primary"
-                @click="save(true)"
-                >另存为</el-button
-            >
-            <el-button type="primary" plain @click="cancel">取消</el-button>
-        </div>
     </div>
 </template>
 
 <script>
 import handleConfigList from "./components/handleConfigList.vue";
 import canvasSensor from "./components/canvasVehicleConfiguration.vue";
+import threeSensor from "./components/threeVehicleConfiguration.vue";
 import { mapState } from "vuex";
 
 export default {
     name: "vehicleConfigurationDetail", // 车辆配置详情
-    components: { handleConfigList, canvasSensor },
+    components: { handleConfigList, canvasSensor, threeSensor },
     data() {
         // 校验最多4位小数
         let validateNum = (rule, value, callback) => {
@@ -252,7 +373,7 @@ export default {
                     },
                     {
                         validator: validateNum,
-                        message: "请输入最多带有4位小数的数字",
+                        message: "最多带有4位小数",
                         trigger: ["blur"],
                     },
                 ],
@@ -264,7 +385,7 @@ export default {
                     },
                     {
                         validator: validateNum,
-                        message: "请输入最多带有4位小数的数字",
+                        message: "最多带有4位小数",
                         trigger: ["blur"],
                     },
                 ],
@@ -276,7 +397,7 @@ export default {
                     },
                     {
                         validator: validateNum,
-                        message: "请输入最多带有4位小数的数字",
+                        message: "最多带有4位小数",
                         trigger: ["blur"],
                     },
                 ],
@@ -284,12 +405,12 @@ export default {
                     { required: true, message: "请输入", trigger: "blur" },
                     {
                         validator: validateNum,
-                        message: "请输入最多带有4位小数的数字",
+                        message: "最多带有4位小数",
                         trigger: ["blur"],
                     },
                     {
                         validator: validateNumC,
-                        message: "请输入不小于-180且不大于180的数字",
+                        message: "-180至180",
                         trigger: ["blur"],
                     },
                 ],
@@ -297,12 +418,12 @@ export default {
                     { required: true, message: "请输入", trigger: "blur" },
                     {
                         validator: validateNum,
-                        message: "请输入最多带有4位小数的数字",
+                        message: "最多带有4位小数",
                         trigger: ["blur"],
                     },
                     {
                         validator: validateNumC,
-                        message: "请输入不小于-180且不大于180的数字",
+                        message: "-180至180",
                         trigger: ["blur"],
                     },
                 ],
@@ -310,12 +431,12 @@ export default {
                     { required: true, message: "请输入", trigger: "blur" },
                     {
                         validator: validateNum,
-                        message: "请输入最多带有4位小数的数字",
+                        message: "最多带有4位小数",
                         trigger: ["blur"],
                     },
                     {
                         validator: validateNumC,
-                        message: "请输入不小于-180且不大于180的数字",
+                        message: "-180至180",
                         trigger: ["blur"],
                     },
                 ],
@@ -337,7 +458,15 @@ export default {
             },
             modelImgSrc: require("@/assets/common/image/others/carTopView.png"), // 车辆图片地址
             width: 300,
-            coordinate: { x: 0, y: 0 }, // 存放传递给canvas的值
+            // 若都是0,第一个input改变时,如果值为0,会出现值变了,
+            // 但传感器没有变化的问题,输入其他值和后续输入均无问题
+            // 每切换一次也有此问题,故不能给0
+            coordinate: { x: 0.1, y: 0.1, z: 0.1, h: 0.1, p: 0.1, r: 0.1 }, // 存放传递给展示的值
+            // coordinate: { x: 0, y: 0, z: 0, h: 0, p: 0, r: 0 }, // 存放传递给展示的值
+            // coordinate: { x: 20, y: 20, z: 20, h: 20, p: 20, r: 20 }, // 存放传递给展示的值
+            configBox: false, // 配置项box
+            carModel: "", // 车模型
+            rate: 20, // 坐标换算倍数
         };
     },
 
@@ -408,6 +537,8 @@ export default {
             });
         },
         getImgUrl(addr) {
+            if (!addr) return "";
+
             let url = "";
             if (process.env.VUE_APP_IS_DEV == "true") {
                 url = this.fileHost + this.fileUrl;
@@ -422,7 +553,13 @@ export default {
         vehicleChange(id) {
             let item = this.vehicleNameList.find((i) => i.id == id);
             this.form.vehicleDescription = item.description;
-            this.modelImgSrc = this.getImgUrl(item.vehicleTopView);
+            console.log(item);
+
+            // if (this.form.vehicleType[1]) {
+            //     this.carModel = this.form.vehicleType[1];
+            // }
+            // this.modelImgSrc = this.getImgUrl(item.vehicleTopView);
+            this.carModel = this.getImgUrl(item.vehicleFrontView);
             // this.$refs.canvasSensor.drawBg();
         },
         // 验证各传感器数组的每一项表单数据是否齐全
@@ -512,6 +649,7 @@ export default {
             }
             //若直接传info即this.form,可能是由于传入了响应式,造成数据会变动
             this.configList[type].unshift({ ...info });
+            this.configBox = true;
         },
         delOne(type, index) {
             this.configList[type].splice(index, 1);
@@ -535,6 +673,14 @@ export default {
                     this.formA,
                     this.configList[item.name][item.index]
                 ); */
+                this.coordinate = {
+                    x: 0.1,
+                    y: 0.1,
+                    z: 0.1,
+                    h: 0.1,
+                    p: 0.1,
+                    r: 0.1,
+                };
                 let formA = this.configList[item.name][item.index];
 
                 this.formA.sensorX = formA.sensorX;
@@ -547,8 +693,13 @@ export default {
                     this.formA.sensorPort = formA.sensorPort;
                 }
             }
-            this.$refs.canvasSensor.clear();
-            this.inputChange();
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.threeSensor.reset();
+            setTimeout(() => {
+                this.$refs.threeSensor.reset(this.curOne.name);
+            }, 0);
+            this.configBox = true;
+            // this.inputChange();
         },
         saveConfig() {
             if (this.curOne.index < 0) {
@@ -582,21 +733,53 @@ export default {
             if (
                 this.formA.sensorX === "" ||
                 this.formA.sensorY === "" ||
-                this.formA.sensorH === ""
+                this.formA.sensorZ === "" ||
+                this.formA.sensorH === "" ||
+                this.formA.sensorP === "" ||
+                this.formA.sensorR === ""
             )
                 return;
             this.coordinate = {
-                x: this.formA.sensorX,
-                y: this.formA.sensorY,
-                h: this.formA.sensorH,
+                x: Math.floor(this.formA.sensorX / this.rate),
+                y: Math.floor(this.formA.sensorY / this.rate),
+                z: Math.floor(this.formA.sensorZ / this.rate),
+                h: +this.formA.sensorH,
+                p: +this.formA.sensorP,
+                r: +this.formA.sensorR,
             };
         },
+        posChange(val, type) {
+            switch (type) {
+                case "x":
+                    this.coordinate.x = +val;
+                    this.formA.sensorX = Math.floor(val * this.rate);
+                    break;
+                case "y":
+                    this.coordinate.y = +val;
+                    this.formA.sensorY = Math.floor(val * this.rate);
+                    break;
+                case "z":
+                    this.coordinate.z = +val;
+                    this.formA.sensorZ = Math.floor(val * this.rate);
+                    break;
+                default:
+                    break;
+            }
+        },
+        closeConfig() {
+            this.configBox = false;
+        },
+        // 显示全部传感器
+        showAll() {
+            this.$refs.threeSensor.showAll();
+            this.closeConfig();
+        },
     },
 
     mounted() {
-        setTimeout(() => {
-            this.domHandle();
-        }, 0);
+        // setTimeout(() => {
+        //     this.domHandle();
+        // }, 0);
 
         this.getMyVehicleList();
 
@@ -605,26 +788,30 @@ export default {
             this.form.id = id = this.$route.query.id;
             this.form.share = this.$route.query.share;
 
-            if (id) {
-                this.$axios({
-                    method: "post",
-                    url: this.$api.modelLibrary.getConfigInfo,
-                    data: {
-                        id,
-                    },
-                }).then((res) => {
-                    if (res.code == 200 && res.info) {
-                        this.form = res.info;
-                        this.configList = res.info.configSensors;
-                        this.modelImgSrc = this.getImgUrl(
-                            res.info.vehicleTopView
-                        );
-                        // this.validateSensorsList();
-                    } else {
-                        this.$message.error(res.message || "获取信息失败");
-                    }
-                });
-            }
+            // if (id) {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getConfigInfo,
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.form = res.info;
+                    this.configList = res.info.configSensors;
+                    // this.modelImgSrc = this.getImgUrl(res.info.vehicleTopView);
+                    this.carModel = this.getImgUrl(res.info.vehicleFrontView);
+                    console.log(this.carModel);
+                    // this.$refs.threeSensor.initCar(
+                    //     this.getImgUrl(res.info.vehicleFrontView)
+                    // );
+                    // this.carModel = "1";
+                    // this.validateSensorsList();
+                } else {
+                    this.$message.error(res.message || "获取信息失败");
+                }
+            });
+            // }
         }
     },
 };
@@ -632,7 +819,20 @@ export default {
 
 <style lang='less' scoped>
 .vehicleConfigurationDetailPanel {
-    margin: 55px 50px 0;
+    margin: 20px 0 -60px 20px;
+}
+
+.rowBox {
+    padding-right: 20px;
+
+    .el-input,
+    .el-select {
+        width: 100%;
+    }
+
+    .el-select /deep/ .el-input {
+        width: 100%;
+    }
 }
 
 .headBox {
@@ -653,34 +853,128 @@ export default {
     }
 }
 
-.contentBox {
+#elScrollBarA {
+    width: 100%;
+    height: 100%;
+}
+
+/deep/ .el-scrollbar__wrap {
+    overflow: auto;
+}
+
+/deep/ .el-scrollbar__bar {
+    opacity: 0.3 !important;
+}
+/deep/ .el-scrollbar__bar:hover {
+    opacity: 0.6 !important;
+}
+
+.panelContent {
     display: flex;
-    justify-content: space-between;
-    margin-top: 30px;
+    // height: calc(100vh - 260px);
 
-    .model {
-        // width: 400px;
-        flex: 1;
-        margin: 0 10%;
-    }
+    .infoLeft {
+        width: 220px;
+        padding-right: 10px;
 
-    .conditions {
-        .btnBox {
-            padding: 0 0 30px;
+        // .contentBox {
+        // display: flex;
+        // justify-content: space-between;
+        margin-top: 30px;
+
+        .handleList {
+            width: 210px;
+            height: calc(100vh - 240px);
+            overflow-y: auto;
+
+            &.handleListA {
+                height: calc(100vh - 270px);
+            }
         }
 
-        .forms /deep/ .el-form-item__label {
-            width: 120px !important;
+        .model {
+            // width: 400px;
+            // flex: 1;
+            margin: 0 10%;
+        }
+        // }
+
+        .btns {
+            padding-top: 30px;
+            text-align: center;
+
+            .el-button {
+                min-width: 60px;
+                padding: 8px 10px;
+                margin-bottom: 10px;
+            }
         }
 
-        .forms /deep/ .el-form-item__content {
-            margin-left: 120px !important;
+        .btnsA {
+            padding-top: 10px;
+            text-align: center;
         }
     }
-}
 
-.btns {
-    padding-top: 30px;
-    text-align: center;
+    .threeRight {
+        position: relative;
+        flex: 1;
+        // height: calc(100vh - 360px);
+
+        .viewBox {
+            position: absolute;
+            left: 2px;
+            top: 2px;
+            width: 40px;
+            height: 40px;
+            line-height: 40px;
+            text-align: center;
+            background-color: #ffffff;
+            opacity: 0.75;
+
+            .view {
+                font-size: 18px;
+                cursor: pointer;
+                color: @themeColor;
+            }
+        }
+
+        .conditions {
+            position: absolute;
+            right: 2px;
+            top: 2px;
+            width: 219px;
+            height: 372px;
+            padding: 10px;
+            background-color: #ffffff;
+
+            .btnBox {
+                padding: 0 0 10px;
+                text-align: center;
+            }
+
+            .forms /deep/ .el-form-item__label {
+                // width: 120px !important;
+                padding-right: 10px;
+                line-height: 22px;
+                font-size: 12px;
+            }
+
+            .forms /deep/ .el-form-item__content {
+                // margin-left: 120px !important;
+                line-height: 22px;
+                font-size: 12px;
+            }
+
+            .el-input {
+                width: 100px;
+                font-size: 12px;
+            }
+
+            .forms /deep/ .el-input__inner {
+                height: 22px;
+            }
+        }
+    }
 }
 </style>

+ 980 - 0
src/views/modelLibrary/vehicleConfigurationDetailThree.vue

@@ -0,0 +1,980 @@
+<template>
+    <div class="vehicleConfigurationDetailPanel">
+        <el-form ref="form" :model="form" :rules="rules" label-width="108px">
+            <el-row class="rowBox">
+                <el-col :span="6">
+                    <el-form-item label="配置名称:" prop="configName">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="60"
+                            v-autoTrim="{ obj: form, key: 'configName' }"
+                            v-model="form.configName"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                    <el-form-item label="配置描述:" prop="configDescription">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="200"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'configDescription',
+                            }"
+                            v-model="form.configDescription"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                    <el-form-item label="车辆名称:" prop="vehicleId">
+                        <el-select
+                            v-model="form.vehicleId"
+                            @change="vehicleChange"
+                        >
+                            <el-option
+                                v-for="item in vehicleNameList"
+                                :label="item.vehicleName"
+                                :value="item.id"
+                                :key="item.id"
+                            ></el-option>
+                        </el-select>
+                    </el-form-item>
+                </el-col>
+                <el-col :span="6">
+                    <el-form-item label="车辆描述:" prop="vehicleDescription">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="200"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'vehicleDescription',
+                            }"
+                            v-model="form.vehicleDescription"
+                            disabled
+                        >
+                        </el-input>
+                    </el-form-item>
+                </el-col>
+            </el-row>
+        </el-form>
+
+        <div class="panelContent">
+            <!-- <div class="infoLeft"> -->
+            <div class="infoLeft contentBox">
+                <div
+                    class="handleList"
+                    v-bind:class="{
+                        handleListA: form.share === '0' || form.share === '1',
+                    }"
+                >
+                    <el-scrollbar
+                        id="elScrollBarA"
+                        ref="scrollbarA"
+                        view-class="scroll"
+                    >
+                        <handle-config-list
+                            ref="handleConfigList"
+                            @curItem="curItem"
+                            @addOne="addOne"
+                            @delOne="delOne"
+                            :configList="configList"
+                            :curOne="curOne"
+                        ></handle-config-list>
+                    </el-scrollbar>
+                </div>
+
+                <!-- <div class="btns">
+                    <el-button
+                        v-if="form.share === '0' || form.share === ''"
+                        type="primary"
+                        @click="save(false)"
+                        >保存</el-button
+                    >
+                    <el-button
+                        v-if="form.share === '1'"
+                        type="primary"
+                        @click="save(true)"
+                        >另存为</el-button
+                    >
+                    <el-button type="primary" plain @click="cancel"
+                        >取消</el-button
+                    >
+                </div>
+
+                <div class="btnsA" v-if="form.share === '0'">
+                    <el-button
+                        v-if="form.share === '0'"
+                        type="primary"
+                        @click="save(true)"
+                        >另存为</el-button
+                    >
+                </div> -->
+
+                <div class="btns">
+                    <el-button
+                        v-if="form.share === '0' || form.share === ''"
+                        type="primary"
+                        @click="save(false)"
+                        >保存</el-button
+                    >
+                    <el-button
+                        v-if="form.share === '0' || form.share === '1'"
+                        type="primary"
+                        @click="save(true)"
+                        >另存为</el-button
+                    >
+                    <el-button type="primary" plain @click="cancel"
+                        >取消</el-button
+                    >
+                </div>
+
+                <!-- <canvas-sensor
+                    ref="canvasSensor"
+                    :coordinate="coordinate"
+                    :modelImgSrc="modelImgSrc"
+                    :curOneName="curOne.name"
+                ></canvas-sensor> -->
+                <!-- </div> -->
+                <!-- </div> -->
+            </div>
+            <div class="threeRight">
+                <div class="viewBox">
+                    <span
+                        class="view el-icon-view"
+                        @click="showAll"
+                        title="显示全部"
+                    ></span>
+                </div>
+
+                <three-sensor
+                    ref="threeSensor"
+                    :xValue="coordinate.x"
+                    :yValue="coordinate.y"
+                    :zValue="coordinate.z"
+                    :hValue="coordinate.p"
+                    :pValue="coordinate.r"
+                    :rValue="coordinate.h"
+                    :carModel="carModel"
+                    :isAdd="!!$route.query.id"
+                    :configList="configList"
+                    @posChange="posChange"
+                ></three-sensor>
+
+                <div v-show="configBox" class="conditions">
+                    <el-form
+                        ref="formA"
+                        :model="formA"
+                        :rules="rulesA"
+                        label-width="99px"
+                    >
+                        <div class="forms">
+                            <el-form-item label="X(mm):" prop="sensorX">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorX',
+                                    }"
+                                    v-model="formA.sensorX"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="Y(mm):" prop="sensorY">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorY',
+                                    }"
+                                    v-model="formA.sensorY"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="Z(mm):" prop="sensorZ">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorZ',
+                                    }"
+                                    v-model="formA.sensorZ"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="横摆角(deg):" prop="sensorH">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorH',
+                                    }"
+                                    v-model="formA.sensorH"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="俯仰角(deg):" prop="sensorP">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorP',
+                                    }"
+                                    v-model="formA.sensorP"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item label="翻滚角(deg):" prop="sensorR">
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="12"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorR',
+                                    }"
+                                    v-model="formA.sensorR"
+                                    @input="inputChange"
+                                    type="number"
+                                    step="1"
+                                >
+                                </el-input>
+                            </el-form-item>
+                            <el-form-item
+                                label="端口"
+                                prop="sensorPort"
+                                v-show="curOne.name === 'ogt'"
+                            >
+                                <el-input
+                                    placeholder="请输入"
+                                    maxlength="9"
+                                    v-autoTrim="{
+                                        obj: formA,
+                                        key: 'sensorPort',
+                                    }"
+                                    v-model="formA.sensorPort"
+                                >
+                                </el-input>
+                            </el-form-item>
+                        </div>
+                    </el-form>
+
+                    <div class="btnBox">
+                        <el-button
+                            type="primary"
+                            @click="saveConfig"
+                            size="small"
+                            >保存设置</el-button
+                        >
+                        <el-button
+                            type="primary"
+                            @click="closeConfig"
+                            plain
+                            size="small"
+                            >关闭</el-button
+                        >
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import handleConfigList from "./components/handleConfigList.vue";
+import canvasSensor from "./components/canvasVehicleConfiguration.vue";
+import threeSensor from "./components/threeVehicleConfiguration.vue";
+import { mapState } from "vuex";
+
+export default {
+    name: "vehicleConfigurationDetail", // 车辆配置详情
+    components: { handleConfigList, canvasSensor, threeSensor },
+    data() {
+        // 校验最多4位小数
+        let validateNum = (rule, value, callback) => {
+            !/^(-?(0|[1-9][0-9]*))(\.\d{1,4})?$/.test(value) &&
+                callback(new Error(rule.message));
+            callback();
+        };
+        // 校验不大于180且不小于-180
+        let validateNumC = (rule, value, callback) => {
+            if (value < -180 || value > 180) {
+                callback(new Error(rule.message));
+                return;
+            }
+            callback();
+        };
+
+        return {
+            vehicleNameList: [],
+            form: {
+                id: "",
+                configCode: "", // 配置ID
+                configName: "", // 配置名称
+                configDescription: "", // 配置描述
+                vehicleName: "", // 车辆名称
+                vehicleId: "", // 车辆名称
+                vehicleDescription: "", // 车辆描述
+                share: "",
+            },
+            formA: {
+                sensorX: "", // 传感器横向偏移量(x轴)
+                sensorY: "", // 传感器横向偏移量(y轴)
+                sensorZ: "", // 传感器横向偏移量(z轴)
+                sensorH: "", // 横摆角
+                sensorP: "", // 俯仰角
+                sensorR: "", // 横滚角
+                sensorPort: "", // 端口
+            },
+            rules: {
+                configName: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                configDescription: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                vehicleId: [
+                    {
+                        required: true,
+                        message: "请选择",
+                        trigger: "change",
+                    },
+                ],
+                vehicleDescription: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+            },
+            rulesA: {
+                sensorX: [
+                    {
+                        required: true,
+                        message: "请输入",
+                        trigger: "blur",
+                    },
+                    {
+                        validator: validateNum,
+                        message: "最多带有4位小数",
+                        trigger: ["blur"],
+                    },
+                ],
+                sensorY: [
+                    {
+                        required: true,
+                        message: "请输入",
+                        trigger: "blur",
+                    },
+                    {
+                        validator: validateNum,
+                        message: "最多带有4位小数",
+                        trigger: ["blur"],
+                    },
+                ],
+                sensorZ: [
+                    {
+                        required: true,
+                        message: "请输入",
+                        trigger: "blur",
+                    },
+                    {
+                        validator: validateNum,
+                        message: "最多带有4位小数",
+                        trigger: ["blur"],
+                    },
+                ],
+                sensorH: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "最多带有4位小数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNumC,
+                        message: "-180至180",
+                        trigger: ["blur"],
+                    },
+                ],
+                sensorP: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "最多带有4位小数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNumC,
+                        message: "-180至180",
+                        trigger: ["blur"],
+                    },
+                ],
+                sensorR: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "最多带有4位小数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNumC,
+                        message: "-180至180",
+                        trigger: ["blur"],
+                    },
+                ],
+                sensorPort: [
+                    { required: false, message: "请输入", trigger: "blur" },
+                ],
+            },
+            // 传感器对象集合
+            configList: {
+                camera: [],
+                ogt: [],
+                lidar: [],
+                gps: [],
+            },
+            // 用于当前选中项的展示
+            curOne: {
+                name: "",
+                index: -1,
+            },
+            modelImgSrc: require("@/assets/common/image/others/carTopView.png"), // 车辆图片地址
+            width: 300,
+            // 若都是0,第一个input改变时,如果值为0,会出现值变了,
+            // 但传感器没有变化的问题,输入其他值和后续输入均无问题
+            // 每切换一次也有此问题,故不能给0
+            coordinate: { x: 0.1, y: 0.1, z: 0.1, h: 0.1, p: 0.1, r: 0.1 }, // 存放传递给展示的值
+            // coordinate: { x: 0, y: 0, z: 0, h: 0, p: 0, r: 0 }, // 存放传递给展示的值
+            // coordinate: { x: 20, y: 20, z: 20, h: 20, p: 20, r: 20 }, // 存放传递给展示的值
+            configBox: false, // 配置项box
+            carModel: "", // 车模型
+            rate: 20, // 坐标换算倍数
+        };
+    },
+
+    watch: {
+        curOne(val) {
+            if (val.name === "ogt") {
+                // 校验端口号
+                let validatePort = (rule, value, callback) => {
+                    !/^(\d{5})$/.test(value) &&
+                        callback(new Error(rule.message));
+
+                    if (value > 65535 || value < 62000) {
+                        callback(new Error(rule.message));
+                        return;
+                    }
+                    callback();
+                };
+
+                let validatePortNoRepeat = (rule, value, callback) => {
+                    let ports = this.configList.ogt.map(
+                        (i) => i.sensorPort + ""
+                    );
+                    ports.splice(this.curOne.index, 1);
+
+                    if (ports.includes(value)) {
+                        callback(new Error(rule.message));
+                        return;
+                    } else {
+                        callback();
+                    }
+                };
+
+                this.rulesA.sensorPort[0].required = true;
+                this.rulesA.sensorPort[1] = {
+                    validator: validatePort,
+                    message: "请输入62000至65535之间的5位数字",
+                    trigger: ["blur"],
+                };
+                this.rulesA.sensorPort[2] = {
+                    validator: validatePortNoRepeat,
+                    message: "端口号重复",
+                    trigger: ["blur"],
+                };
+            } else {
+                this.rulesA.sensorPort[0].required = false;
+                this.rulesA.sensorPort.length = 1;
+            }
+        },
+    },
+
+    computed: {
+        ...mapState(["fileHost", "fileUrl"]),
+    },
+
+    methods: {
+        // 获取车辆名称私有下拉列表
+        async getMyVehicleList() {
+            await this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getMyVehicleList,
+                data: {},
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.vehicleNameList = res.info;
+                } else {
+                    this.$message.error(res.message || "获取信息失败");
+                }
+            });
+        },
+        getImgUrl(addr) {
+            if (!addr) return "";
+
+            let url = "";
+            if (process.env.VUE_APP_IS_DEV == "true") {
+                url = this.fileHost + this.fileUrl;
+            } else {
+                url = this.fileUrl;
+            }
+
+            let token = localStorage.getItem("Authorization").split(" ")[1];
+            let src = `${url}?objectName=${addr}&access_token=${token}`;
+            return src;
+        },
+        vehicleChange(id) {
+            let item = this.vehicleNameList.find((i) => i.id == id);
+            this.form.vehicleDescription = item.description;
+            console.log(item);
+
+            // if (this.form.vehicleType[1]) {
+            //     this.carModel = this.form.vehicleType[1];
+            // }
+            // this.modelImgSrc = this.getImgUrl(item.vehicleTopView);
+            this.carModel = this.getImgUrl(item.vehicleFrontView);
+            // this.$refs.canvasSensor.drawBg();
+        },
+        // 验证各传感器数组的每一项表单数据是否齐全
+        validateSensorsList() {
+            // console.log(Object.keys(this.configList));
+            // console.log(Object.values(this.configList));
+            // console.log(Object.entries(this.configList));
+
+            let lists = Object.entries(this.configList);
+
+            for (let index = 0; index < lists.length; index++) {
+                const element = lists[index];
+                let oneIndex = element[1].findIndex(
+                    (i) => i.sensorX == null && i.sensorY == null
+                );
+
+                if (oneIndex > -1) {
+                    this.$refs.handleConfigList.configHandle(
+                        element[0],
+                        oneIndex
+                    );
+
+                    // 如果对应项未展开 让其展开
+                    if (
+                        element[0] === "camera" &&
+                        !this.$refs.handleConfigList.isActiveA
+                    ) {
+                        this.$refs.handleConfigList.isActiveA = true;
+                    } else if (
+                        element[0] === "ogt" &&
+                        !this.$refs.handleConfigList.isActiveB
+                    ) {
+                        this.$refs.handleConfigList.isActiveB = true;
+                    } else if (
+                        element[0] === "lidar" &&
+                        !this.$refs.handleConfigList.isActiveC
+                    ) {
+                        this.$refs.handleConfigList.isActiveC = true;
+                    } else if (
+                        element[0] === "gps" &&
+                        !this.$refs.handleConfigList.isActiveE
+                    ) {
+                        this.$refs.handleConfigList.isActiveE = true;
+                    }
+
+                    this.$message.error("请先设置数据并保存");
+                    return false;
+                }
+            }
+            return true;
+        },
+        save(isAdd) {
+            this.$refs.form.validate((valid) => {
+                if (valid) {
+                    if (isAdd) {
+                        this.form.id = "";
+                    }
+
+                    if (!this.validateSensorsList()) return;
+
+                    let data = Object.assign({}, this.form, {
+                        configSensors: this.configList,
+                    });
+                    this.$axios({
+                        method: "post",
+                        url: this.$api.modelLibrary.saveConfig,
+                        data,
+                    }).then((res) => {
+                        if (res.code == 200) {
+                            this.$message.success("保存成功");
+                            this.cancel();
+                        } else {
+                            this.$message.error(res.message || "保存失败");
+                        }
+                    });
+                }
+            });
+        },
+        /**
+         * info 新增传感器信息
+         * type 传感器类型
+         */
+        addOne(info, type) {
+            // 如果列表里没有对应类型的数组则需手动添加
+            if (!this.configList[type]) {
+                this.configList[type] = [];
+            }
+            //若直接传info即this.form,可能是由于传入了响应式,造成数据会变动
+            this.configList[type].unshift({ ...info });
+            this.configBox = true;
+        },
+        delOne(type, index) {
+            this.configList[type].splice(index, 1);
+        },
+        curItem(item, isAdd) {
+            this.$refs.formA.resetFields();
+            this.curOne = item;
+            if (isAdd) {
+                this.formA.sensorX = "0";
+                this.formA.sensorY = "0";
+                this.formA.sensorZ = "0";
+                this.formA.sensorH = "0";
+                this.formA.sensorP = "0";
+                this.formA.sensorR = "0";
+                if (this.curOne.name === "ogt") {
+                    this.formA.sensorPort = "62000";
+                }
+            } else {
+                // 此写法因formA包含太多信息,直接覆盖式的传后续会有问题
+                /* Object.assign(
+                    this.formA,
+                    this.configList[item.name][item.index]
+                ); */
+                this.coordinate = {
+                    x: 0.1,
+                    y: 0.1,
+                    z: 0.1,
+                    h: 0.1,
+                    p: 0.1,
+                    r: 0.1,
+                };
+                let formA = this.configList[item.name][item.index];
+
+                this.formA.sensorX = formA.sensorX;
+                this.formA.sensorY = formA.sensorY;
+                this.formA.sensorZ = formA.sensorZ;
+                this.formA.sensorH = formA.sensorH;
+                this.formA.sensorP = formA.sensorP;
+                this.formA.sensorR = formA.sensorR;
+                if (this.curOne.name === "ogt") {
+                    this.formA.sensorPort = formA.sensorPort;
+                }
+            }
+            // this.$refs.canvasSensor.clear();
+            // this.$refs.threeSensor.reset();
+            setTimeout(() => {
+                this.$refs.threeSensor.reset(this.curOne.name);
+            }, 0);
+            this.configBox = true;
+            // this.inputChange();
+        },
+        saveConfig() {
+            if (this.curOne.index < 0) {
+                this.$message.info("请先选择传感器");
+                return;
+            }
+
+            this.$refs.formA.validate((valid) => {
+                if (valid) {
+                    Object.assign(
+                        this.configList[this.curOne.name][this.curOne.index],
+                        this.formA
+                    );
+                    this.$message.success("保存设置成功");
+                }
+            });
+        },
+        cancel() {
+            this.$router.replace({ path: "/vehicleConfigurationList" });
+        },
+        // 给canvas的外层容器赋值
+        domHandle() {
+            $(document).ready(() => {
+                this.width = $(
+                    ".vehicleConfigurationDetailPanel .model"
+                ).outerWidth();
+            });
+        },
+        inputChange() {
+            if (this.curOne.index < 0) return;
+            if (
+                this.formA.sensorX === "" ||
+                this.formA.sensorY === "" ||
+                this.formA.sensorZ === "" ||
+                this.formA.sensorH === "" ||
+                this.formA.sensorP === "" ||
+                this.formA.sensorR === ""
+            )
+                return;
+            this.coordinate = {
+                x: Math.floor(this.formA.sensorX / this.rate),
+                y: Math.floor(this.formA.sensorY / this.rate),
+                z: Math.floor(this.formA.sensorZ / this.rate),
+                h: +this.formA.sensorH,
+                p: +this.formA.sensorP,
+                r: +this.formA.sensorR,
+            };
+        },
+        posChange(val, type) {
+            switch (type) {
+                case "x":
+                    this.coordinate.x = +val;
+                    this.formA.sensorX = Math.floor(val * this.rate);
+                    break;
+                case "y":
+                    this.coordinate.y = +val;
+                    this.formA.sensorY = Math.floor(val * this.rate);
+                    break;
+                case "z":
+                    this.coordinate.z = +val;
+                    this.formA.sensorZ = Math.floor(val * this.rate);
+                    break;
+                default:
+                    break;
+            }
+        },
+        closeConfig() {
+            this.configBox = false;
+        },
+        // 显示全部传感器
+        showAll() {
+            this.$refs.threeSensor.showAll();
+            this.closeConfig();
+        },
+    },
+
+    mounted() {
+        // setTimeout(() => {
+        //     this.domHandle();
+        // }, 0);
+
+        this.getMyVehicleList();
+
+        if (this.$route.query.id) {
+            let id = "";
+            this.form.id = id = this.$route.query.id;
+            this.form.share = this.$route.query.share;
+
+            // if (id) {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getConfigInfo,
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.form = res.info;
+                    this.configList = res.info.configSensors;
+                    // this.modelImgSrc = this.getImgUrl(res.info.vehicleTopView);
+                    this.carModel = this.getImgUrl(res.info.vehicleFrontView);
+                    console.log(this.carModel);
+                    // this.$refs.threeSensor.initCar(
+                    //     this.getImgUrl(res.info.vehicleFrontView)
+                    // );
+                    // this.carModel = "1";
+                    // this.validateSensorsList();
+                } else {
+                    this.$message.error(res.message || "获取信息失败");
+                }
+            });
+            // }
+        }
+    },
+};
+</script>
+
+<style lang='less' scoped>
+.vehicleConfigurationDetailPanel {
+    margin: 20px 0 -60px 20px;
+}
+
+.rowBox {
+    padding-right: 20px;
+
+    .el-input,
+    .el-select {
+        width: 100%;
+    }
+
+    .el-select /deep/ .el-input {
+        width: 100%;
+    }
+}
+
+.headBox {
+    .el-form-item {
+        margin-right: 20px;
+    }
+}
+
+.inputBox.flexBox {
+    margin-bottom: 22px;
+
+    .label {
+        width: 88px;
+    }
+
+    div {
+        line-height: 32px;
+    }
+}
+
+#elScrollBarA {
+    width: 100%;
+    height: 100%;
+}
+
+/deep/ .el-scrollbar__wrap {
+    overflow: auto;
+}
+
+/deep/ .el-scrollbar__bar {
+    opacity: 0.3 !important;
+}
+/deep/ .el-scrollbar__bar:hover {
+    opacity: 0.6 !important;
+}
+
+.panelContent {
+    display: flex;
+    // height: calc(100vh - 260px);
+
+    .infoLeft {
+        width: 220px;
+        padding-right: 10px;
+
+        // .contentBox {
+        // display: flex;
+        // justify-content: space-between;
+        margin-top: 30px;
+
+        .handleList {
+            width: 210px;
+            height: calc(100vh - 240px);
+            overflow-y: auto;
+
+            &.handleListA {
+                height: calc(100vh - 270px);
+            }
+        }
+
+        .model {
+            // width: 400px;
+            // flex: 1;
+            margin: 0 10%;
+        }
+        // }
+
+        .btns {
+            padding-top: 30px;
+            text-align: center;
+
+            .el-button {
+                min-width: 60px;
+                padding: 8px 10px;
+                margin-bottom: 10px;
+            }
+        }
+
+        .btnsA {
+            padding-top: 10px;
+            text-align: center;
+        }
+    }
+
+    .threeRight {
+        position: relative;
+        flex: 1;
+        // height: calc(100vh - 360px);
+
+        .viewBox {
+            position: absolute;
+            left: 2px;
+            top: 2px;
+            width: 40px;
+            height: 40px;
+            line-height: 40px;
+            text-align: center;
+            background-color: #ffffff;
+            opacity: 0.75;
+
+            .view {
+                font-size: 18px;
+                cursor: pointer;
+                color: @themeColor;
+            }
+        }
+
+        .conditions {
+            position: absolute;
+            right: 2px;
+            top: 2px;
+            width: 219px;
+            height: 372px;
+            padding: 10px;
+            background-color: #ffffff;
+
+            .btnBox {
+                padding: 0 0 10px;
+                text-align: center;
+            }
+
+            .forms /deep/ .el-form-item__label {
+                // width: 120px !important;
+                padding-right: 10px;
+                line-height: 22px;
+                font-size: 12px;
+            }
+
+            .forms /deep/ .el-form-item__content {
+                // margin-left: 120px !important;
+                line-height: 22px;
+                font-size: 12px;
+            }
+
+            .el-input {
+                width: 100px;
+                font-size: 12px;
+            }
+
+            .forms /deep/ .el-input__inner {
+                height: 22px;
+            }
+        }
+    }
+}
+</style>

+ 28 - 7
src/views/modelLibrary/vehicleModel.vue

@@ -258,10 +258,15 @@
                 </div>
 
                 <div class="titlePanel">
-                    <div class="titlePanelBor">视图</div>
+                    <div class="titlePanelBor">3D视图</div>
                 </div>
+                
                 <div class="modelBox">
-                    <img v-show="modelImgSrc" :src="modelImgSrc" width="100%" />
+                    <!-- <img v-show="modelImgSrc" :src="modelImgSrc" width="100%" /> -->
+                    <three-show
+                        ref="threeShow"
+                        :carModel="carModel"
+                    ></three-show>
                 </div>
             </el-form>
 
@@ -297,10 +302,11 @@
 <script>
 import modelList from "./components/modelList.vue";
 import { mapState } from "vuex";
+import threeShow from "./components/threeVehicleModel.vue";
 
 export default {
     name: "vehicleModel", // 车辆模型
-    components: { modelList },
+    components: { modelList, threeShow },
     data() {
         // 校验非负且最多4位小数
         let validateNum = (rule, value, callback) => {
@@ -536,6 +542,7 @@ export default {
                 share: "",
                 id: "",
             },
+            carModel: "",
         };
     },
 
@@ -556,11 +563,18 @@ export default {
                     this.$refs.form.clearValidate();
                     this.form = res.info;
                     if (res.info.vehicleFrontView) {
-                        this.modelImgSrc = this.getImgUrl(
+                        // this.modelImgSrc = this.getImgUrl(
+                        //     res.info.vehicleFrontView
+                        // );
+                        this.carModel = this.getImgUrl(
                             res.info.vehicleFrontView
                         );
                     }
 
+                    // if (this.form.vehicleType[1]) {
+                    //     this.carModel = this.form.vehicleType[1];
+                    // }
+
                     this.curOne = {
                         share: res.info.share,
                         id: res.info.id,
@@ -611,6 +625,7 @@ export default {
             this.form.share = "";
             this.form.id = "";
             this.modelImgSrc = "";
+            this.carModel = "";
 
             this.curOne = {
                 share: "",
@@ -686,7 +701,13 @@ export default {
                 this.$refs.cascader.getCheckedNodes(true)[0].data.vo
             );
 
-            this.modelImgSrc = this.getImgUrl(this.form.vehicleFrontView);
+            console.log(this.form);
+            console.log(this.form.vehicleFrontView);
+
+            // this.carModel = this.form.vehicleType[1];
+
+            // this.modelImgSrc = this.getImgUrl(this.form.vehicleFrontView);
+            this.carModel = this.getImgUrl(this.form.vehicleFrontView);
         },
         async getModelLabelList() {
             await this.$axios({
@@ -718,7 +739,7 @@ export default {
 .vehicleModelPanel {
     display: flex;
     flex: 1;
-    padding: 15px 30px 30px;
+    padding: 15px 30px 10px;
 
     .modelList /deep/ .listPanel {
         min-height: 400px;
@@ -752,7 +773,7 @@ export default {
     }
 
     .modelBox {
-        width: 60%;
+        // width: 60%;
         min-width: 810px;
     }
 }

+ 759 - 0
src/views/modelLibrary/vehicleModelCanvas.vue

@@ -0,0 +1,759 @@
+<template>
+    <div class="vehicleModelPanel">
+        <model-list
+            ref="modelList"
+            class="modelList"
+            @showInfo="showInfo"
+            @addOne="addOne"
+            @delOne="delOne"
+            :getListApi="getListApi"
+            :showName="showName"
+            :curOne="curOne"
+        ></model-list>
+
+        <div class="contentPanel">
+            <el-form
+                ref="form"
+                :model="form"
+                :rules="rules"
+                label-width="160px"
+            >
+                <div class="inputBox flexBox">
+                    <span class="label">车辆ID</span>
+                    <div>{{ form.vehicleCode }}</div>
+                </div>
+
+                <div class="flexBox">
+                    <el-form-item label="车辆名称:" prop="vehicleName">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="60"
+                            v-autoTrim="{ obj: form, key: 'vehicleName' }"
+                            v-model="form.vehicleName"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车辆描述:" prop="description">
+                        <el-input
+                            v-autoTrim="{ obj: form, key: 'description' }"
+                            v-model="form.description"
+                            placeholder="请输入"
+                            maxlength="200"
+                        ></el-input>
+                    </el-form-item>
+                    <el-form-item label="车辆模型:" prop="vehicleType">
+                        <el-cascader
+                            ref="cascader"
+                            v-model="form.vehicleType"
+                            :options="modelLabelList"
+                            :props="props"
+                            @change="modelLabelChange"
+                        ></el-cascader>
+                    </el-form-item>
+                    <el-form-item label="车辆模型标识:" prop="modelLabel">
+                        <el-input
+                            placeholder="请输入"
+                            v-autoTrim="{ obj: form, key: 'modelLabel' }"
+                            v-model="form.modelLabel"
+                            disabled
+                        >
+                        </el-input>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">控制参数</div>
+                </div>
+                <div class="flexBox">
+                    <el-form-item label="最大速度(km/h):" prop="maxSpeed">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'maxSpeed' }"
+                            v-model="form.maxSpeed"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="发动机功率(kW):" prop="enginePower">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'enginePower' }"
+                            v-model="form.enginePower"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="最大减速度(m/s²):"
+                        prop="maxDeceleration"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'maxDeceleration' }"
+                            v-model="form.maxDeceleration"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="最大转向角度(rad):"
+                        prop="maxSteeringAngle"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'maxSteeringAngle' }"
+                            v-model="form.maxSteeringAngle"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="驱动方式:" prop="wheelDrive">
+                        <el-select v-model="form.wheelDrive">
+                            <el-option
+                                v-for="item in wheelDriveList"
+                                :label="item.caption"
+                                :value="item.code"
+                                :key="item.code"
+                            ></el-option>
+                        </el-select>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">效率参数</div>
+                </div>
+                <div class="flexBox">
+                    <el-form-item label="总效率:" prop="overallEfficiency">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="9"
+                            v-autoTrim="{ obj: form, key: 'overallEfficiency' }"
+                            v-model="form.overallEfficiency"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="前表面有效面积(㎡):"
+                        prop="frontSurfaceEffective"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'frontSurfaceEffective',
+                            }"
+                            v-model="form.frontSurfaceEffective"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="空气阻力系数:"
+                        prop="airDragCoefficient"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="9"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'airDragCoefficient',
+                            }"
+                            v-model="form.airDragCoefficient"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="滚动阻力系数:"
+                        prop="rollingResistanceCoefficient"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="9"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'rollingResistanceCoefficient',
+                            }"
+                            v-model="form.rollingResistanceCoefficient"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">几何参数</div>
+                </div>
+                <div class="flexBox">
+                    <el-form-item label="车轮直径(m):" prop="wheelDiameter">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'wheelDiameter' }"
+                            v-model="form.wheelDiameter"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车前距(m):" prop="frontDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'frontDistance' }"
+                            v-model="form.frontDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车后距(m):" prop="rearDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'rearDistance' }"
+                            v-model="form.rearDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车左距(m):" prop="leftDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'leftDistance' }"
+                            v-model="form.leftDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车右距(m):" prop="rightDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'rightDistance' }"
+                            v-model="form.rightDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车高(m):" prop="heightDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'heightDistance' }"
+                            v-model="form.heightDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="轴距(m):" prop="wheelbase">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'wheelbase' }"
+                            v-model="form.wheelbase"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="质量(kg):" prop="mass">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'mass' }"
+                            v-model="form.mass"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">视图</div>
+                </div>
+                <div class="modelBox">
+                    <img v-show="modelImgSrc" :src="modelImgSrc" width="100%" />
+                </div>
+            </el-form>
+
+            <div class="btns">
+                <!-- 新增和私有才能修改和保存 -->
+                <!-- 私有才能分享 -->
+                <!-- share有值 才能另存为 -->
+                <el-button
+                    type="primary"
+                    v-show="form.share === '0' || form.share === ''"
+                    @click="save(false)"
+                    >保存</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-if="roleCode === '0' || roleCode === '1'"
+                    v-show="form.share === '0'"
+                    @click="share"
+                    >分享</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-show="form.share === '0' || form.share === '1'"
+                    @click="save(true)"
+                    >另存为</el-button
+                >
+                <el-button type="primary" plain @click="cancel">取消</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import modelList from "./components/modelList.vue";
+import { mapState } from "vuex";
+
+export default {
+    name: "vehicleModel", // 车辆模型
+    components: { modelList },
+    data() {
+        // 校验非负且最多4位小数
+        let validateNum = (rule, value, callback) => {
+            !/^(0|[1-9][0-9]*)(\.\d{1,4})?$/.test(value) &&
+                callback(new Error(rule.message));
+            callback();
+        };
+        // 校验不大于1
+        let validateNoMore1 = (rule, value, callback) => {
+            if (value > 1) {
+                callback(new Error(rule.message));
+                return;
+            }
+            callback();
+        };
+
+        return {
+            getListApi: this.$api.modelLibrary.getVehicleList, // 获取列表api
+            showName: "vehicleName",
+            typeList: [],
+            // imgSrc: require("@/assets/common/image/car.png"),
+            form: {
+                vehicleCode: "", // 车辆ID
+                vehicleName: "", // 车辆名称
+                description: "", // 车辆描述
+                // vehicleType: "", // 车辆类型
+                // vehicleModel: "", // 车辆型号
+                // vehicleColour: "", // 车辆颜色
+                vehicleType: [], // 车辆模型
+                modelLabel: "", // 车辆模型标识
+                maxSpeed: "", // 最大速度
+                enginePower: "", // 发动机功率
+                maxDeceleration: "", // 最大减速度
+                maxSteeringAngle: "", // 最大转角
+                wheelDrive: "", // 驱动方式
+                overallEfficiency: "", // 总效率
+                frontSurfaceEffective: "", // 前表面有效面积
+                airDragCoefficient: "", // 空气阻力系数
+                rollingResistanceCoefficient: "", // 滚动阻力系数
+                wheelDiameter: "", // 车轮直径
+                frontDistance: "", // 车前距
+                rearDistance: "", // 车后距
+                leftDistance: "", // 车左距
+                rightDistance: "", // 车右距
+                heightDistance: "", // 车高
+                wheelbase: "", // 轴距
+                mass: "", // 质量
+                share: "", // 分享
+                // vehicleImage: "", // 对应的图片相对地址
+                id: "",
+                vehicleFrontView: "", // 对应的图片相对地址
+                vehicleTopView: "", // 对应的图片相对地址
+            },
+            vehicleTypeList: [],
+            vehicleModelList: [],
+            vehicleColourList: [],
+            wheelDriveList: [],
+            rules: {
+                vehicleName: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                description: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                // vehicleModel: [
+                //     { required: true, message: "请输入", trigger: "blur" },
+                // ],
+                // vehicleColour: [
+                //     { required: true, message: "请输入", trigger: "blur" },
+                // ],
+                modelLabel: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                maxSpeed: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                enginePower: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                maxDeceleration: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                maxSteeringAngle: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                overallEfficiency: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNoMore1,
+                        message: "请输入不大于1的非负数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                frontSurfaceEffective: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                airDragCoefficient: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNoMore1,
+                        message: "请输入不大于1的非负数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                rollingResistanceCoefficient: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNoMore1,
+                        message: "请输入不大于1的非负数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                wheelDiameter: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                frontDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                rearDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                leftDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                rightDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                heightDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                wheelbase: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                mass: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                wheelDrive: [
+                    { required: true, message: "请选择", trigger: "change" },
+                ],
+                vehicleType: [
+                    { required: true, message: "请选择", trigger: "change" },
+                ],
+            },
+            props: {
+                multiple: false,
+                label: "name",
+                value: "code",
+            },
+            modelLabelList: [],
+            modelImgSrc: "",
+            // 用于当前选中项的展示
+            curOne: {
+                share: "",
+                id: "",
+            },
+        };
+    },
+
+    computed: {
+        ...mapState(["fileHost", "fileUrl", "roleCode"]),
+    },
+
+    methods: {
+        showInfo(id) {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getVehicleInfo,
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.$refs.form.clearValidate();
+                    this.form = res.info;
+                    if (res.info.vehicleFrontView) {
+                        this.modelImgSrc = this.getImgUrl(
+                            res.info.vehicleFrontView
+                        );
+                    }
+
+                    this.curOne = {
+                        share: res.info.share,
+                        id: res.info.id,
+                    };
+                } else {
+                    this.$message.error(res.message || "获取失败");
+                }
+            });
+        },
+        save(isAdd) {
+            this.$refs.form.validate((valid) => {
+                if (valid) {
+                    // 判断是否新增
+                    if (isAdd || this.form.share === "") {
+                        this.form.id = "";
+                    }
+
+                    this.$axios({
+                        method: "post",
+                        url: this.$api.modelLibrary.saveVehicle,
+                        data: {
+                            ...this.form,
+                        },
+                    }).then((res) => {
+                        if (res.code == 200 && res.info) {
+                            this.form.id = res.info.id;
+                            this.form.share = res.info.share;
+                            this.form.vehicleCode = res.info.vehicleCode;
+                            this.$message.success("保存成功");
+                            this.$refs.modelList.getList();
+
+                            this.curOne = {
+                                share: res.info.share,
+                                id: res.info.id,
+                            };
+                        } else {
+                            this.$message.error(res.message || "保存失败");
+                        }
+                    });
+                }
+            });
+        },
+        addOne() {
+            this.$refs.form.resetFields();
+            this.form.vehicleCode = "";
+            this.form.vehicleFrontView = "";
+            this.form.vehicleTopView = "";
+            this.form.share = "";
+            this.form.id = "";
+            this.modelImgSrc = "";
+
+            this.curOne = {
+                share: "",
+                id: "",
+            };
+        },
+        delOne(id) {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.delVehicleById,
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200) {
+                    this.$message.success("删除成功");
+                    this.$refs.modelList.getList();
+                    if (this.form.id && id === this.form.id) {
+                        this.addOne();
+                    }
+
+                    if (id === this.curOne.id) {
+                        this.curOne = {
+                            share: "",
+                            id: "",
+                        };
+                    }
+                } else {
+                    this.$message.error(res.message || "删除失败");
+                }
+            });
+        },
+        share() {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.shareVehicle,
+                data: {
+                    ...this.form,
+                },
+            }).then((res) => {
+                if (res.code == 200) {
+                    this.$message.success("分享成功");
+                    this.$refs.modelList.getList();
+                } else {
+                    this.$message.error(res.message || "分享失败");
+                }
+            });
+        },
+        cancel() {
+            if (this.form.id) {
+                // 取消时有id则重新请求表单
+                this.showInfo(this.form.id);
+            } else {
+                // 清空
+                this.addOne();
+            }
+        },
+        getImgUrl(addr) {
+            let url = "";
+            if (process.env.VUE_APP_IS_DEV == "true") {
+                url = this.fileHost + this.fileUrl;
+            } else {
+                url = this.fileUrl;
+            }
+
+            let token = localStorage.getItem("Authorization").split(" ")[1];
+            let src = `${url}?objectName=${addr}&access_token=${token}`;
+            return src;
+        },
+        modelLabelChange() {
+            Object.assign(
+                this.form,
+                this.$refs.cascader.getCheckedNodes(true)[0].data.vo
+            );
+
+            this.modelImgSrc = this.getImgUrl(this.form.vehicleFrontView);
+        },
+        async getModelLabelList() {
+            await this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getVehicleTempTree,
+                data: {},
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.modelLabelList = res.info;
+                } else {
+                    this.$message.error(res.message || "获取车辆模型列表失败");
+                }
+            });
+        },
+    },
+
+    async mounted() {
+        await this.$dicsListsInit({
+            vehicleTypeList: "vehicleType",
+            wheelDriveList: "driveType",
+        });
+
+        this.getModelLabelList();
+    },
+};
+</script>
+
+<style lang='less' scoped>
+.vehicleModelPanel {
+    display: flex;
+    flex: 1;
+    padding: 15px 30px 30px;
+
+    .modelList /deep/ .listPanel {
+        min-height: 400px;
+        height: calc(100vh - 120px);
+    }
+
+    .contentPanel {
+        flex: 1;
+        padding-left: 30px;
+    }
+
+    .inputBox.flexBox {
+        margin-bottom: 22px;
+
+        .label {
+            width: 138px;
+        }
+
+        div {
+            line-height: 32px;
+        }
+    }
+
+    .titlePanel {
+        padding: 22px 0;
+    }
+
+    .btns {
+        padding-top: 30px;
+        text-align: center;
+    }
+
+    .modelBox {
+        width: 60%;
+        min-width: 810px;
+    }
+}
+</style>

+ 780 - 0
src/views/modelLibrary/vehicleModelThree.vue

@@ -0,0 +1,780 @@
+<template>
+    <div class="vehicleModelPanel">
+        <model-list
+            ref="modelList"
+            class="modelList"
+            @showInfo="showInfo"
+            @addOne="addOne"
+            @delOne="delOne"
+            :getListApi="getListApi"
+            :showName="showName"
+            :curOne="curOne"
+        ></model-list>
+
+        <div class="contentPanel">
+            <el-form
+                ref="form"
+                :model="form"
+                :rules="rules"
+                label-width="160px"
+            >
+                <div class="inputBox flexBox">
+                    <span class="label">车辆ID</span>
+                    <div>{{ form.vehicleCode }}</div>
+                </div>
+
+                <div class="flexBox">
+                    <el-form-item label="车辆名称:" prop="vehicleName">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="60"
+                            v-autoTrim="{ obj: form, key: 'vehicleName' }"
+                            v-model="form.vehicleName"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车辆描述:" prop="description">
+                        <el-input
+                            v-autoTrim="{ obj: form, key: 'description' }"
+                            v-model="form.description"
+                            placeholder="请输入"
+                            maxlength="200"
+                        ></el-input>
+                    </el-form-item>
+                    <el-form-item label="车辆模型:" prop="vehicleType">
+                        <el-cascader
+                            ref="cascader"
+                            v-model="form.vehicleType"
+                            :options="modelLabelList"
+                            :props="props"
+                            @change="modelLabelChange"
+                        ></el-cascader>
+                    </el-form-item>
+                    <el-form-item label="车辆模型标识:" prop="modelLabel">
+                        <el-input
+                            placeholder="请输入"
+                            v-autoTrim="{ obj: form, key: 'modelLabel' }"
+                            v-model="form.modelLabel"
+                            disabled
+                        >
+                        </el-input>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">控制参数</div>
+                </div>
+                <div class="flexBox">
+                    <el-form-item label="最大速度(km/h):" prop="maxSpeed">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'maxSpeed' }"
+                            v-model="form.maxSpeed"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="发动机功率(kW):" prop="enginePower">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'enginePower' }"
+                            v-model="form.enginePower"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="最大减速度(m/s²):"
+                        prop="maxDeceleration"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'maxDeceleration' }"
+                            v-model="form.maxDeceleration"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="最大转向角度(rad):"
+                        prop="maxSteeringAngle"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'maxSteeringAngle' }"
+                            v-model="form.maxSteeringAngle"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="驱动方式:" prop="wheelDrive">
+                        <el-select v-model="form.wheelDrive">
+                            <el-option
+                                v-for="item in wheelDriveList"
+                                :label="item.caption"
+                                :value="item.code"
+                                :key="item.code"
+                            ></el-option>
+                        </el-select>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">效率参数</div>
+                </div>
+                <div class="flexBox">
+                    <el-form-item label="总效率:" prop="overallEfficiency">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="9"
+                            v-autoTrim="{ obj: form, key: 'overallEfficiency' }"
+                            v-model="form.overallEfficiency"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="前表面有效面积(㎡):"
+                        prop="frontSurfaceEffective"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'frontSurfaceEffective',
+                            }"
+                            v-model="form.frontSurfaceEffective"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="空气阻力系数:"
+                        prop="airDragCoefficient"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="9"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'airDragCoefficient',
+                            }"
+                            v-model="form.airDragCoefficient"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item
+                        label="滚动阻力系数:"
+                        prop="rollingResistanceCoefficient"
+                    >
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="9"
+                            v-autoTrim="{
+                                obj: form,
+                                key: 'rollingResistanceCoefficient',
+                            }"
+                            v-model="form.rollingResistanceCoefficient"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">几何参数</div>
+                </div>
+                <div class="flexBox">
+                    <el-form-item label="车轮直径(m):" prop="wheelDiameter">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'wheelDiameter' }"
+                            v-model="form.wheelDiameter"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车前距(m):" prop="frontDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'frontDistance' }"
+                            v-model="form.frontDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车后距(m):" prop="rearDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'rearDistance' }"
+                            v-model="form.rearDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车左距(m):" prop="leftDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'leftDistance' }"
+                            v-model="form.leftDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车右距(m):" prop="rightDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'rightDistance' }"
+                            v-model="form.rightDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="车高(m):" prop="heightDistance">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'heightDistance' }"
+                            v-model="form.heightDistance"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="轴距(m):" prop="wheelbase">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'wheelbase' }"
+                            v-model="form.wheelbase"
+                        >
+                        </el-input>
+                    </el-form-item>
+                    <el-form-item label="质量(kg):" prop="mass">
+                        <el-input
+                            placeholder="请输入"
+                            maxlength="12"
+                            v-autoTrim="{ obj: form, key: 'mass' }"
+                            v-model="form.mass"
+                        >
+                        </el-input>
+                    </el-form-item>
+                </div>
+
+                <div class="titlePanel">
+                    <div class="titlePanelBor">3D视图</div>
+                </div>
+                
+                <div class="modelBox">
+                    <!-- <img v-show="modelImgSrc" :src="modelImgSrc" width="100%" /> -->
+                    <three-show
+                        ref="threeShow"
+                        :carModel="carModel"
+                    ></three-show>
+                </div>
+            </el-form>
+
+            <div class="btns">
+                <!-- 新增和私有才能修改和保存 -->
+                <!-- 私有才能分享 -->
+                <!-- share有值 才能另存为 -->
+                <el-button
+                    type="primary"
+                    v-show="form.share === '0' || form.share === ''"
+                    @click="save(false)"
+                    >保存</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-if="roleCode === '0' || roleCode === '1'"
+                    v-show="form.share === '0'"
+                    @click="share"
+                    >分享</el-button
+                >
+                <el-button
+                    type="primary"
+                    v-show="form.share === '0' || form.share === '1'"
+                    @click="save(true)"
+                    >另存为</el-button
+                >
+                <el-button type="primary" plain @click="cancel">取消</el-button>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+import modelList from "./components/modelList.vue";
+import { mapState } from "vuex";
+import threeShow from "./components/threeVehicleModel.vue";
+
+export default {
+    name: "vehicleModel", // 车辆模型
+    components: { modelList, threeShow },
+    data() {
+        // 校验非负且最多4位小数
+        let validateNum = (rule, value, callback) => {
+            !/^(0|[1-9][0-9]*)(\.\d{1,4})?$/.test(value) &&
+                callback(new Error(rule.message));
+            callback();
+        };
+        // 校验不大于1
+        let validateNoMore1 = (rule, value, callback) => {
+            if (value > 1) {
+                callback(new Error(rule.message));
+                return;
+            }
+            callback();
+        };
+
+        return {
+            getListApi: this.$api.modelLibrary.getVehicleList, // 获取列表api
+            showName: "vehicleName",
+            typeList: [],
+            // imgSrc: require("@/assets/common/image/car.png"),
+            form: {
+                vehicleCode: "", // 车辆ID
+                vehicleName: "", // 车辆名称
+                description: "", // 车辆描述
+                // vehicleType: "", // 车辆类型
+                // vehicleModel: "", // 车辆型号
+                // vehicleColour: "", // 车辆颜色
+                vehicleType: [], // 车辆模型
+                modelLabel: "", // 车辆模型标识
+                maxSpeed: "", // 最大速度
+                enginePower: "", // 发动机功率
+                maxDeceleration: "", // 最大减速度
+                maxSteeringAngle: "", // 最大转角
+                wheelDrive: "", // 驱动方式
+                overallEfficiency: "", // 总效率
+                frontSurfaceEffective: "", // 前表面有效面积
+                airDragCoefficient: "", // 空气阻力系数
+                rollingResistanceCoefficient: "", // 滚动阻力系数
+                wheelDiameter: "", // 车轮直径
+                frontDistance: "", // 车前距
+                rearDistance: "", // 车后距
+                leftDistance: "", // 车左距
+                rightDistance: "", // 车右距
+                heightDistance: "", // 车高
+                wheelbase: "", // 轴距
+                mass: "", // 质量
+                share: "", // 分享
+                // vehicleImage: "", // 对应的图片相对地址
+                id: "",
+                vehicleFrontView: "", // 对应的图片相对地址
+                vehicleTopView: "", // 对应的图片相对地址
+            },
+            vehicleTypeList: [],
+            vehicleModelList: [],
+            vehicleColourList: [],
+            wheelDriveList: [],
+            rules: {
+                vehicleName: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                description: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                // vehicleModel: [
+                //     { required: true, message: "请输入", trigger: "blur" },
+                // ],
+                // vehicleColour: [
+                //     { required: true, message: "请输入", trigger: "blur" },
+                // ],
+                modelLabel: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                ],
+                maxSpeed: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                enginePower: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                maxDeceleration: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                maxSteeringAngle: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                overallEfficiency: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNoMore1,
+                        message: "请输入不大于1的非负数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                frontSurfaceEffective: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                airDragCoefficient: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNoMore1,
+                        message: "请输入不大于1的非负数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                rollingResistanceCoefficient: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNoMore1,
+                        message: "请输入不大于1的非负数",
+                        trigger: ["blur"],
+                    },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                wheelDiameter: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                frontDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                rearDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                leftDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                rightDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                heightDistance: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                wheelbase: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                mass: [
+                    { required: true, message: "请输入", trigger: "blur" },
+                    {
+                        validator: validateNum,
+                        message: "请输入最多带有4位小数的非负数",
+                        trigger: ["blur"],
+                    },
+                ],
+                wheelDrive: [
+                    { required: true, message: "请选择", trigger: "change" },
+                ],
+                vehicleType: [
+                    { required: true, message: "请选择", trigger: "change" },
+                ],
+            },
+            props: {
+                multiple: false,
+                label: "name",
+                value: "code",
+            },
+            modelLabelList: [],
+            modelImgSrc: "",
+            // 用于当前选中项的展示
+            curOne: {
+                share: "",
+                id: "",
+            },
+            carModel: "",
+        };
+    },
+
+    computed: {
+        ...mapState(["fileHost", "fileUrl", "roleCode"]),
+    },
+
+    methods: {
+        showInfo(id) {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getVehicleInfo,
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.$refs.form.clearValidate();
+                    this.form = res.info;
+                    if (res.info.vehicleFrontView) {
+                        // this.modelImgSrc = this.getImgUrl(
+                        //     res.info.vehicleFrontView
+                        // );
+                        this.carModel = this.getImgUrl(
+                            res.info.vehicleFrontView
+                        );
+                    }
+
+                    // if (this.form.vehicleType[1]) {
+                    //     this.carModel = this.form.vehicleType[1];
+                    // }
+
+                    this.curOne = {
+                        share: res.info.share,
+                        id: res.info.id,
+                    };
+                } else {
+                    this.$message.error(res.message || "获取失败");
+                }
+            });
+        },
+        save(isAdd) {
+            this.$refs.form.validate((valid) => {
+                if (valid) {
+                    // 判断是否新增
+                    if (isAdd || this.form.share === "") {
+                        this.form.id = "";
+                    }
+
+                    this.$axios({
+                        method: "post",
+                        url: this.$api.modelLibrary.saveVehicle,
+                        data: {
+                            ...this.form,
+                        },
+                    }).then((res) => {
+                        if (res.code == 200 && res.info) {
+                            this.form.id = res.info.id;
+                            this.form.share = res.info.share;
+                            this.form.vehicleCode = res.info.vehicleCode;
+                            this.$message.success("保存成功");
+                            this.$refs.modelList.getList();
+
+                            this.curOne = {
+                                share: res.info.share,
+                                id: res.info.id,
+                            };
+                        } else {
+                            this.$message.error(res.message || "保存失败");
+                        }
+                    });
+                }
+            });
+        },
+        addOne() {
+            this.$refs.form.resetFields();
+            this.form.vehicleCode = "";
+            this.form.vehicleFrontView = "";
+            this.form.vehicleTopView = "";
+            this.form.share = "";
+            this.form.id = "";
+            this.modelImgSrc = "";
+            this.carModel = "";
+
+            this.curOne = {
+                share: "",
+                id: "",
+            };
+        },
+        delOne(id) {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.delVehicleById,
+                data: {
+                    id,
+                },
+            }).then((res) => {
+                if (res.code == 200) {
+                    this.$message.success("删除成功");
+                    this.$refs.modelList.getList();
+                    if (this.form.id && id === this.form.id) {
+                        this.addOne();
+                    }
+
+                    if (id === this.curOne.id) {
+                        this.curOne = {
+                            share: "",
+                            id: "",
+                        };
+                    }
+                } else {
+                    this.$message.error(res.message || "删除失败");
+                }
+            });
+        },
+        share() {
+            this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.shareVehicle,
+                data: {
+                    ...this.form,
+                },
+            }).then((res) => {
+                if (res.code == 200) {
+                    this.$message.success("分享成功");
+                    this.$refs.modelList.getList();
+                } else {
+                    this.$message.error(res.message || "分享失败");
+                }
+            });
+        },
+        cancel() {
+            if (this.form.id) {
+                // 取消时有id则重新请求表单
+                this.showInfo(this.form.id);
+            } else {
+                // 清空
+                this.addOne();
+            }
+        },
+        getImgUrl(addr) {
+            let url = "";
+            if (process.env.VUE_APP_IS_DEV == "true") {
+                url = this.fileHost + this.fileUrl;
+            } else {
+                url = this.fileUrl;
+            }
+
+            let token = localStorage.getItem("Authorization").split(" ")[1];
+            let src = `${url}?objectName=${addr}&access_token=${token}`;
+            return src;
+        },
+        modelLabelChange() {
+            Object.assign(
+                this.form,
+                this.$refs.cascader.getCheckedNodes(true)[0].data.vo
+            );
+
+            console.log(this.form);
+            console.log(this.form.vehicleFrontView);
+
+            // this.carModel = this.form.vehicleType[1];
+
+            // this.modelImgSrc = this.getImgUrl(this.form.vehicleFrontView);
+            this.carModel = this.getImgUrl(this.form.vehicleFrontView);
+        },
+        async getModelLabelList() {
+            await this.$axios({
+                method: "post",
+                url: this.$api.modelLibrary.getVehicleTempTree,
+                data: {},
+            }).then((res) => {
+                if (res.code == 200 && res.info) {
+                    this.modelLabelList = res.info;
+                } else {
+                    this.$message.error(res.message || "获取车辆模型列表失败");
+                }
+            });
+        },
+    },
+
+    async mounted() {
+        await this.$dicsListsInit({
+            vehicleTypeList: "vehicleType",
+            wheelDriveList: "driveType",
+        });
+
+        this.getModelLabelList();
+    },
+};
+</script>
+
+<style lang='less' scoped>
+.vehicleModelPanel {
+    display: flex;
+    flex: 1;
+    padding: 15px 30px 10px;
+
+    .modelList /deep/ .listPanel {
+        min-height: 400px;
+        height: calc(100vh - 120px);
+    }
+
+    .contentPanel {
+        flex: 1;
+        padding-left: 30px;
+    }
+
+    .inputBox.flexBox {
+        margin-bottom: 22px;
+
+        .label {
+            width: 138px;
+        }
+
+        div {
+            line-height: 32px;
+        }
+    }
+
+    .titlePanel {
+        padding: 22px 0;
+    }
+
+    .btns {
+        padding-top: 30px;
+        text-align: center;
+    }
+
+    .modelBox {
+        // width: 60%;
+        min-width: 810px;
+    }
+}
+</style>