vue vite+three在线编辑模型导入导出

news2024/9/24 5:33:02

文章目录

  • 一、1.0.0版本
    • 1.新增
    • 2.编辑
    • 3.导出
    • 4.导入
  • 二、2.0.0版本
    • 1. 修复模型垂直方向放置时 模型会重合
    • 4. 修复了导出导入功能 现在是1:1导出导入
    • 5. 新增一个地面 视角看不到地下 设置了禁止编辑地面 地面设置为圆形
    • 6. 新增功能 可选择基本圆形 方形 圆柱形等模型以及可放置自己的模型文件
    • 7. 优化面板样式
  • 总结

要实现一个类似于数字孪生的场景 可以在线、新增、删除模型 、以及编辑模型的颜色、长宽高
然后还要实现 编辑完后 保存为json数据 记录模型数据 既可以导入也可以导出

一、1.0.0版本

1.新增

先拿建议的立方体来代替模型
点击新增按钮就新增一个立方体
在这里插入图片描述

2.编辑

点击编辑按钮可以修改坐标 长宽高 颜色等等信息
在这里插入图片描述

3.导出

点击导出按钮 可以导出为json数据格式
在这里插入图片描述

在这里插入图片描述

4.导入

选择导入刚才的json文件
在这里插入图片描述
有一个bug 就是导入后颜色丢失了 点击模型 信息面板的颜色显示正常 渲染颜色丢失
在这里插入图片描述


源码

<template>
  <div id="app" @click="onAppClick">
    <div id="info">
      <button @click.stop="addBuilding">新增</button>
      <button @click.stop="showEditor">编辑</button>
      <button @click.stop="exportModelData">导出</button>
      <input type="file" @change="importModelData" ref="fileInput" />
    </div>
    <div id="editor" v-if="editorVisible" @click.stop>
      <h3>Edit Building</h3>
      <label for="color">Color:</label>
      <input type="color" id="color" v-model="selectedObjectProps.color" /><br />
      <label for="posX">Position X:</label>
      <input
        type="number"
        id="posX"
        v-model="selectedObjectProps.posX"
        step="0.1"
      /><br />
      <label for="posY">Position Y:</label>
      <input
        type="number"
        id="posY"
        v-model="selectedObjectProps.posY"
        step="0.1"
      /><br />
      <label for="posZ">Position Z:</label>
      <input
        type="number"
        id="posZ"
        v-model="selectedObjectProps.posZ"
        step="0.1"
      /><br />
      <label for="scaleX">Scale X:</label>
      <input
        type="number"
        id="scaleX"
        v-model="selectedObjectProps.scaleX"
        step="0.1"
      /><br />
      <label for="scaleY">Scale Y:</label>
      <input
        type="number"
        id="scaleY"
        v-model="selectedObjectProps.scaleY"
        step="0.1"
      /><br />
      <label for="scaleZ">Scale Z:</label>
      <input
        type="number"
        id="scaleZ"
        v-model="selectedObjectProps.scaleZ"
        step="0.1"
      /><br />
      <label for="rotX">Rotation X:</label>
      <input
        type="number"
        id="rotX"
        v-model="selectedObjectProps.rotX"
        step="0.1"
      /><br />
      <label for="rotY">Rotation Y:</label>
      <input
        type="number"
        id="rotY"
        v-model="selectedObjectProps.rotY"
        step="0.1"
      /><br />
      <label for="rotZ">Rotation Z:</label>
      <input
        type="number"
        id="rotZ"
        v-model="selectedObjectProps.rotZ"
        step="0.1"
      /><br />
      <button @click="applyEdit">保存</button>
      <button @click="deleteBuilding">删除</button>
    </div>
    <div ref="canvasContainer" style="width: 100vw; height: 100vh"></div>
  </div>
</template>

<script>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";

export default {
  data() {
    return {
      editorVisible: false,
      selectedObject: null,
      selectedObjectProps: {
        color: "#00ff00",
        posX: 0,
        posY: 0,
        posZ: 0,
        scaleX: 1,
        scaleY: 1,
        scaleZ: 1,
        rotX: 0,
        rotY: 0,
        rotZ: 0,
      },
      raycaster: null,
    };
  },
  mounted() {
    this.init();
    this.animate();
    window.addEventListener("resize", this.onWindowResize, false);
    this.loadModelData(); // Load saved model data on page load
  },
  methods: {
    init() {
      console.log("Initializing Three.js");

      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color(0xcccccc);
      this.camera = new THREE.PerspectiveCamera(
        60,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      this.camera.position.set(0, 10, 20);
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.$refs.canvasContainer.appendChild(this.renderer.domElement);
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);

      const light = new THREE.DirectionalLight(0xffffff, 1);
      light.position.set(5, 10, 7.5);
      this.scene.add(light);

      this.raycaster = new THREE.Raycaster();

      const geometry = new THREE.BoxGeometry(1, 1, 1);
      const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
      this.cube = new THREE.Mesh(geometry, material);
      this.scene.add(this.cube);
    },
    onWindowResize() {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
    },
    onAppClick(event) {
      const mouse = new THREE.Vector2();
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      this.raycaster.setFromCamera(mouse, this.camera);
      const intersects = this.raycaster.intersectObjects(this.scene.children, true);
      if (intersects.length > 0) {
        this.selectedObject = intersects[0].object;
        console.log("Object selected:", this.selectedObject);
        this.showEditor();
      }
    },
    addBuilding() {
      const geometry = new THREE.BoxGeometry(1, 1, 1);
      const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
      const building = new THREE.Mesh(geometry, material);
      building.position.set(Math.random() * 10 - 5, 0.5, Math.random() * 10 - 5);
      this.scene.add(building);
    },
    showEditor() {
      if (this.selectedObject) {
        this.editorVisible = true;
        this.updateEditor(this.selectedObject);
      }
    },
    updateEditor(object) {
      this.selectedObjectProps.color = `#${object.material.color.getHexString()}`;
      this.selectedObjectProps.posX = object.position.x;
      this.selectedObjectProps.posY = object.position.y;
      this.selectedObjectProps.posZ = object.position.z;
      this.selectedObjectProps.scaleX = object.scale.x;
      this.selectedObjectProps.scaleY = object.scale.y;
      this.selectedObjectProps.scaleZ = object.scale.z;
      this.selectedObjectProps.rotX = object.rotation.x;
      this.selectedObjectProps.rotY = object.rotation.y;
      this.selectedObjectProps.rotZ = object.rotation.z;
    },
    applyEdit() {
      if (this.selectedObject) {
        const color = this.selectedObjectProps.color;
        this.selectedObject.material.color.set(color);
        this.selectedObject.position.set(
          parseFloat(this.selectedObjectProps.posX),
          parseFloat(this.selectedObjectProps.posY),
          parseFloat(this.selectedObjectProps.posZ)
        );
        this.selectedObject.scale.set(
          parseFloat(this.selectedObjectProps.scaleX),
          parseFloat(this.selectedObjectProps.scaleY),
          parseFloat(this.selectedObjectProps.scaleZ)
        );
        this.selectedObject.rotation.set(
          parseFloat(this.selectedObjectProps.rotX),
          parseFloat(this.selectedObjectProps.rotY),
          parseFloat(this.selectedObjectProps.rotZ)
        );
      }
    },
    deleteBuilding() {
      if (this.selectedObject) {
        this.scene.remove(this.selectedObject);
        this.selectedObject = null;
        this.editorVisible = false;
      }
    },
    animate() {
      requestAnimationFrame(this.animate);
      this.renderer.render(this.scene, this.camera);
      this.controls.update();
    },
    exportModelData() {
      const modelData = {
        objects: this.scene.children
          .filter((obj) => obj instanceof THREE.Mesh) // 过滤出是 Mesh 对象的物体
          .map((obj) => ({
            position: obj.position.toArray(),
            scale: obj.scale.toArray(),
            rotation: obj.rotation.toArray(),
            color: `#${obj.material.color.getHexString()}`,
          })),
      };
      const jsonData = JSON.stringify(modelData);
      const blob = new Blob([jsonData], { type: "application/json" });
      const url = URL.createObjectURL(blob);
      const a = document.createElement("a");
      a.style.display = "none";
      a.href = url;
      a.download = "model_data.json";
      document.body.appendChild(a);
      a.click();
      URL.revokeObjectURL(url);
      document.body.removeChild(a);
    },
    importModelData(event) {
      const file = event.target.files[0];
      if (file) {
        const reader = new FileReader();
        reader.onload = () => {
          try {
            const data = JSON.parse(reader.result);
            console.log("Imported data:", data); // 输出导入的完整数据,确保格式和内容正确

            this.clearScene();
            data.objects.forEach((objData, index) => {
              const geometry = new THREE.BoxGeometry();

              // 设置默认颜色为红色
              const color = new THREE.Color(0xff0000); // 红色

              // 如果数据中有颜色字段并且是合法的颜色值,则使用数据中的颜色
              if (objData.color && typeof objData.color === "string") {
                try {
                  color.set(objData.color);
                } catch (error) {
                  console.error(`Error parsing color for object ${index}:`, error);
                }
              } else {
                console.warn(`Invalid color value for object ${index}:`, objData.color);
              }

              const material = new THREE.MeshStandardMaterial({
                color: color,
                metalness: 0.5, // 示例中的金属度设置为0.5,可以根据需求调整
                roughness: 0.8, // 示例中的粗糙度设置为0.8,可以根据需求调整
              });
              const object = new THREE.Mesh(geometry, material);
              object.position.fromArray(objData.position);
              object.scale.fromArray(objData.scale);
              object.rotation.fromArray(objData.rotation);
              this.scene.add(object);
            });
          } catch (error) {
            console.error("Error importing model data:", error);
          }
        };
        reader.readAsText(file);
      }
    },
    clearScene() {
      while (this.scene.children.length > 0) {
        this.scene.remove(this.scene.children[0]);
      }
    },
    saveModelData() {
      const modelData = {
        objects: this.scene.children.map((obj) => ({
          position: obj.position.toArray(),
          scale: obj.scale.toArray(),
          rotation: obj.rotation.toArray(),
          color: `#${obj.material.color.getHexString()}`,
        })),
      };
      localStorage.setItem("modelData", JSON.stringify(modelData));
    },
    loadModelData() {
      const savedData = localStorage.getItem("modelData");
      if (savedData) {
        try {
          const data = JSON.parse(savedData);
          this.clearScene();
          data.objects.forEach((objData) => {
            const geometry = new THREE.BoxGeometry();
            const material = new THREE.MeshStandardMaterial({
              color: parseInt(objData.color.replace("#", "0x"), 16),
            });
            const object = new THREE.Mesh(geometry, material);
            object.position.fromArray(objData.position);
            object.scale.fromArray(objData.scale);
            object.rotation.fromArray(objData.rotation);
            this.scene.add(object);
          });
        } catch (error) {
          console.error("Error loading model data from localStorage:", error);
        }
      }
    },
  },
};
</script>

<style>
body {
  margin: 0;
  overflow: hidden;
}

canvas {
  display: block;
}

#info {
  position: absolute;
  top: 10px;
  left: 10px;
  background: rgba(255, 255, 255, 0.8);
  padding: 10px;
}

#editor {
  position: absolute;
  top: 100px;
  left: 10px;
  background: rgba(255, 255, 255, 0.8);
  padding: 10px;
}
</style>

二、2.0.0版本

在这里插入图片描述

1. 修复模型垂直方向放置时 模型会重合

4. 修复了导出导入功能 现在是1:1导出导入

5. 新增一个地面 视角看不到地下 设置了禁止编辑地面 地面设置为圆形

6. 新增功能 可选择基本圆形 方形 圆柱形等模型以及可放置自己的模型文件

7. 优化面板样式

<template>
  <div id="app" @click="onAppClick">
    <div id="info">
      <button @click.stop="toggleBuildingMode">
        {{ buildingMode ? "关闭建造模式" : "开启建造模式" }}
      </button>
      <button @click.stop="showEditor">编辑所选模型</button>
      <button @click.stop="exportModelData">导出模型数据</button>
      <input type="file" @change="importModelData" ref="fileInput" />
      <input type="file" @change="importCustomModel" ref="customModelInput" />
      <label for="modelType">模型类型:</label>
      <select v-model="selectedModelType">
        <option value="box">立方体</option>
        <option value="sphere">球体</option>
        <option value="cylinder">圆柱体</option>
        <option value="custom">自定义模型</option>
      </select>
    </div>
    <div id="editor" v-if="editorVisible" @click.stop>
      <h3>编辑模型</h3>
      <div class="form-group">
        <label for="color">颜色:</label>
        <input type="color" id="color" v-model="selectedObjectProps.color" /><br />
      </div>
      <div class="form-group">
        <label for="posX">位置 X:</label>
        <input type="number" id="posX" v-model="selectedObjectProps.posX" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="posY">位置 Y:</label>
        <input type="number" id="posY" v-model="selectedObjectProps.posY" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="posZ">位置 Z:</label>
        <input type="number" id="posZ" v-model="selectedObjectProps.posZ" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="scaleX">缩放 X:</label>
        <input type="number" id="scaleX" v-model="selectedObjectProps.scaleX" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="scaleY">缩放 Y:</label>
        <input type="number" id="scaleY" v-model="selectedObjectProps.scaleY" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="scaleZ">缩放 Z:</label>
        <input type="number" id="scaleZ" v-model="selectedObjectProps.scaleZ" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="rotX">旋转 X:</label>
        <input type="number" id="rotX" v-model="selectedObjectProps.rotX" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="rotY">旋转 Y:</label>
        <input type="number" id="rotY" v-model="selectedObjectProps.rotY" step="0.1" /><br />
      </div>
      <div class="form-group">
        <label for="rotZ">旋转 Z:</label>
        <input type="number" id="rotZ" v-model="selectedObjectProps.rotZ" step="0.1" /><br />
      </div>
      <button @click="applyEdit">应用</button>
      <button @click="deleteBuilding">删除</button>
    </div>
    <div ref="canvasContainer" style="width: 100vw; height: 100vh"></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";

export default {
  data() {
    return {
      editorVisible: false,
      selectedObject: null,
      selectedObjectProps: {
        color: "#000",
        posX: 0,
        posY: 0,
        posZ: 0,
        scaleX: 1,
        scaleY: 1,
        scaleZ: 1,
        rotX: 0,
        rotY: 0,
        rotZ: 0,
      },
      raycaster: null,
      buildingMode: false,
      selectedModelType: "box",
      customModel: null,
    };
  },
  mounted() {
    this.init();
    this.animate();
    window.addEventListener("resize", this.onWindowResize, false);
  },
  methods: {
    animate() {
      requestAnimationFrame(this.animate);
      this.renderer.render(this.scene, this.camera);
      this.controls.update();
    },
    init() {
      console.log("Initializing Three.js");

      this.scene = new THREE.Scene();
      this.scene.background = new THREE.Color('0xcccccc');
      this.camera = new THREE.PerspectiveCamera(
        60,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      this.camera.position.set(0, 10, 20);
      this.renderer = new THREE.WebGLRenderer({ antialias: true });
      this.renderer.setSize(window.innerWidth, window.innerHeight);
      this.$refs.canvasContainer.appendChild(this.renderer.domElement);
      this.controls = new OrbitControls(this.camera, this.renderer.domElement);

      this.controls.minDistance = 10;
      this.controls.maxDistance = 50;
      this.controls.maxPolarAngle = Math.PI / 2;

      const planeGeometry = new THREE.CircleGeometry(100, 32);
      const planeMaterial = new THREE.MeshStandardMaterial({ color: 0x999999 });
      const plane = new THREE.Mesh(planeGeometry, planeMaterial);
      plane.rotation.x = -Math.PI / 2;
      plane.userData.isGround = true;
      this.scene.add(plane);

      const light = new THREE.DirectionalLight(0xffffff, 1);
      light.position.set(5, 10, 7.5);
      this.scene.add(light);

      this.raycaster = new THREE.Raycaster();
    },
    onWindowResize() {
      this.camera.aspect = window.innerWidth / window.innerHeight;
      this.camera.updateProjectionMatrix();
      this.renderer.setSize(window.innerWidth, window.innerHeight);
    },
    onAppClick(event) {
      const mouse = new THREE.Vector2();
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
      this.raycaster.setFromCamera(mouse, this.camera);
      const intersects = this.raycaster.intersectObjects(this.scene.children, true);

      if (this.buildingMode && intersects.length > 0) {
        const intersect = intersects[0];
        const point = intersect.point;
        if (intersect.object.userData.isGround) {
          if (this.isOverlapping(point.x, point.z)) {
            this.stackBuilding(point.x, point.z);
          } else {
            this.addBuilding(point.x, 0, point.z);
          }
        } else {
          const stackHeight = intersect.object.position.y + intersect.object.scale.y;
          this.addBuilding(intersect.object.position.x, stackHeight, intersect.object.position.z);
        }
      } else if (intersects.length > 0) {
        this.selectedObject = intersects[0].object;
        console.log("Object selected:", this.selectedObject);
        this.showEditor();
      }
    },
    isOverlapping(x, z) {
      const threshold = 1;
      for (let obj of this.scene.children) {
        if (
          Math.abs(obj.position.x - x) < threshold &&
          Math.abs(obj.position.z - z) < threshold &&
          !obj.userData.isGround
        ) {
          return true;
        }
      }
      return false;
    },
    stackBuilding(x, z) {
      let maxY = 0;
      this.scene.children.forEach((obj) => {
        if (
          Math.abs(obj.position.x - x) < 1 &&
          Math.abs(obj.position.z - z) < 1 &&
          !obj.userData.isGround &&
          obj.position.y + obj.scale.y > maxY
        ) {
          maxY = obj.position.y + obj.scale.y;
        }
      });
      this.addBuilding(x, maxY, z);
    },
    addBuilding(x, y, z) {
      let geometry;
      switch (this.selectedModelType) {
        case "sphere":
          geometry = new THREE.SphereGeometry(0.5, 32, 32);
          break;
        case "cylinder":
          geometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);
          break;
        case "custom":
          if (this.customModel) {
            this.loadCustomModel(x, y, z);
            return;
          }
          break;
        case "box":
        default:
          geometry = new THREE.BoxGeometry(1, 1, 1);
          break;
      }

      if (geometry) {
        const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 });
        const building = new THREE.Mesh(geometry, material);
        building.position.set(x, y, z);
        this.scene.add(building);
      }
    },
    loadCustomModel(x, y, z) {
      const loader = new GLTFLoader();
      loader.load(
        this.customModel,
        (gltf) => {
          const object = gltf.scene;
          object.position.set(x, y, z);
          this.scene.add(object);
        },
        undefined,
        (error) => {
          console.error("An error happened while loading the custom model", error);
        }
      );
    },
    importCustomModel(event) {
      const file = event.target.files[0];
      this.customModel = URL.createObjectURL(file);
    },
    showEditor() {
      if (this.selectedObject) {
        this.selectedObjectProps.color = "#" + this.selectedObject.material.color.getHexString();
        this.selectedObjectProps.posX = this.selectedObject.position.x;
        this.selectedObjectProps.posY = this.selectedObject.position.y;
        this.selectedObjectProps.posZ = this.selectedObject.position.z;
        this.selectedObjectProps.scaleX = this.selectedObject.scale.x;
        this.selectedObjectProps.scaleY = this.selectedObject.scale.y;
        this.selectedObjectProps.scaleZ = this.selectedObject.scale.z;
        this.selectedObjectProps.rotX = this.selectedObject.rotation.x;
        this.selectedObjectProps.rotY = this.selectedObject.rotation.y;
        this.selectedObjectProps.rotZ = this.selectedObject.rotation.z;
      }
      this.editorVisible = true;
    },
    applyEdit() {
      if (this.selectedObject) {
        this.selectedObject.material.color.set(this.selectedObjectProps.color);
        this.selectedObject.position.set(
          this.selectedObjectProps.posX,
          this.selectedObjectProps.posY,
          this.selectedObjectProps.posZ
        );
        this.selectedObject.scale.set(
          this.selectedObjectProps.scaleX,
          this.selectedObjectProps.scaleY,
          this.selectedObjectProps.scaleZ
        );
        this.selectedObject.rotation.set(
          this.selectedObjectProps.rotX,
          this.selectedObjectProps.rotY,
          this.selectedObjectProps.rotZ
        );
      }
      this.editorVisible = false;
    },
    deleteBuilding() {
      if (this.selectedObject) {
        this.scene.remove(this.selectedObject);
        this.selectedObject.geometry.dispose();
        this.selectedObject.material.dispose();
        this.selectedObject = null;
        this.editorVisible = false;
      }
    },
    toggleBuildingMode() {
      this.buildingMode = !this.buildingMode;
    },
    exportModelData() {
      const modelData = this.scene.children
        .filter((obj) => obj.type === "Mesh" && !obj.userData.isGround)
        .map((obj) => ({
          type: obj.geometry.type,
          position: obj.position,
          rotation: obj.rotation,
          scale: obj.scale,
          color: obj.material.color.getHex(),
        }));
      const blob = new Blob([JSON.stringify(modelData)], { type: "application/json" });
      const link = document.createElement("a");
      link.href = URL.createObjectURL(blob);
      link.download = "modelData.json";
      link.click();
    },
    importModelData(event) {
      const file = event.target.files[0];
      const reader = new FileReader();
      reader.onload = (e) => {
        const modelData = JSON.parse(e.target.result);
        this.loadModelData(modelData);
      };
      reader.readAsText(file);
    },
    loadModelData(modelData = null) {
      if (!modelData) {
        return;
      }
      modelData.forEach((data) => {
        let geometry;
        switch (data.type) {
          case "SphereGeometry":
            geometry = new THREE.SphereGeometry(0.5, 32, 32);
            break;
          case "CylinderGeometry":
            geometry = new THREE.CylinderGeometry(0.5, 0.5, 1, 32);
            break;
          case "BoxGeometry":
          default:
            geometry = new THREE.BoxGeometry(1, 1, 1);
            break;
        }
        const material = new THREE.MeshStandardMaterial({ color: data.color });
        const object = new THREE.Mesh(geometry, material);
        object.position.copy(data.position);
        object.rotation.copy(data.rotation);
        object.scale.copy(data.scale);
        this.scene.add(object);
      });
    },
  },
};
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialias;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
}

#info {
  position: absolute;
  top: 10px;
  left: 10px;
  background: rgba(255, 255, 255, 0.8);
  padding: 10px;
  border-radius: 5px;
}

#editor {
  position: absolute;
  top: 50px;
  right: 10px;
  background: rgba(255, 255, 255, 0.9);
  padding: 10px;
  border-radius: 5px;
  z-index: 1000;
  width: 200px;
}

#editor .form-group {
  margin-bottom: 10px;
}

#editor label {
  display: block;
  margin-bottom: 5px;
}

#editor input {
  width: 100%;
}
</style>

总结

未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1920991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每天五分钟深度学习:向量化技术在神经网络中的应用

本文重点 向量化技术,简而言之,就是利用矩阵运算(而非传统的for循环)来执行大规模的计算任务。这种技术依赖于单指令多数据(SIMD)架构,允许一个指令同时对多个数据元素执行相同的操作。例如,在向量化加法中,不再需要逐个元素进行加法操作,而是可以一次性对整个向量执…

防御课第一次作业第一天笔记整理

网络安全概述 网络安全&#xff08;Cyber Security&#xff09;是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不因偶然的或者恶意的原因而遭受到破坏、更改、泄露&#xff0c;系统连续可靠正常地运行&#xff0c;网络服务不中断 中国网络安全市场近年来只增不…

【微信小程序知识点】自定义构建npm

在实际开发中&#xff0c;随着项目的功能越来越多&#xff0c;项目越来越复杂&#xff0c;文件目录也变得很繁琐&#xff0c;为了方便进行项目的开发&#xff0c;开发人员通常会对目录结构进行优化调整&#xff0c;例如&#xff1a;将小程序源码放到miniprogram目录下。 &…

探索最佳海外代理服务商!你知道哪些?

近期收到很多读者回复&#xff0c;咨询我有没有好用的海外代理&#xff0c;许多业务会用到海外代理&#xff0c;给大家整理了几个亲测好用的代理&#xff0c;如果有需要可以去试一试。 一、711Proxy 711Proxy的覆盖范围广&#xff0c;住宅IP质量高&#xff0c;基本上爬虫业务…

【测开能力提升-fastapi框架】fastapi路由分发

1.7 路由分发 apps/app01.py from fastapi import APIRouterapp01 APIRouter()app01.get("/food") async def shop_food():return {"shop": "food"}app01.get("/bed") async def shop_food():return {"shop": "bed&…

华贝甄选干细胞科技,揭秘生命修复的奥秘

在探索生命奥秘的漫漫征途中&#xff0c;华贝甄选凭借干细胞科技的神奇力量&#xff0c;为您点亮健康与活力的希望之光。 我们深知&#xff0c;细胞是生命的基石&#xff0c;而干细胞则是这基石中蕴含的无限潜能。华贝甄选精心打造的干细胞疗法&#xff0c;如同神奇的魔法&…

网络编程学习之tcp

按下*&#xff08;星号&#xff09;可以搜索当前光标下的单词。 Tcp编程的过程 打开网络设备 Bind&#xff1a;给服务地址把ip号和端口号连接进去 Tcp是有状态的 Listen是进入监听状态&#xff0c;看有没有客户端来连接服务器 Tcp比udp消耗过多资源 Upd类似于半双工&#…

Excel第30享:基于辅助列的条件求和

1、需求描述 如下图所示&#xff0c;现要统计2022年YTD&#xff08;Year To Date&#xff1a;年初至今日&#xff09;各个人员的“上班工时&#xff08;a2&#xff09;”。 下图为系统直接导出的工时数据明细样例。 2、解决思路 Step1&#xff1a;确定逻辑。“从日期中提取出…

工厂人员定位为何如此重要?它有怎样的方案优势?

在工厂中安全性是最重要的一项指标&#xff0c;因它安全问题涉及到很多方面&#xff0c;不仅有经济损失还又人员伤亡&#xff0c;所以为了解决厂区安全隐患问题&#xff0c;就必须要用到工厂人员定位系统。它不仅可以降低安全隐患而且方便了日常管理&#xff0c;提升了厂区工作…

如何理解内容营销?与传统营销对比,内容营销有哪些特点?

在数字化浪潮的推动下&#xff0c;内容营销已经从一种新兴的营销手段成长为企业与消费者沟通的重要桥梁。它不仅仅是一种策略&#xff0c;更是一种艺术&#xff0c;一种通过分享有价值的信息来吸引、教育并留住目标受众的艺术。在这个信息爆炸、注意力稀缺的时代&#xff0c;内…

2024年适合开发人员使用的12个最佳API测试工具

什么是API&#xff1f; API是一个软件解决方案&#xff0c;作为中介&#xff0c;使两个应用程序能够相互交互。以下一些特征让API变得更加有用和有价值&#xff1a; 遵守REST和HTTP等易于访问、广泛理解和开发人员友好的标准。API不仅仅是几行代码&#xff1b;这些是为移动开…

ubuntu16.04安装低版本cmake(安装cmake安装)

文章目录 ubuntu16.04安装低版本cmake&#xff08;安装cmake安装&#xff09;1. **下载并解压CMake压缩文件**&#xff1a;- 首先&#xff0c;你需要从CMake的官方网站或其他可靠来源下载cmake-2.8.9-Linux-i386.tar.gz文件。- 然后在终端中使用以下命令解压文件&#xff1a; 2…

批量给图片添加水印

1 办公痛点 1. 为了维护作者版权&#xff0c;需要给文章中照片添加文字水印 2. 网上虽有添加水印的软件&#xff0c;但几乎都需要付费&#xff0c;且没法按照自己要求定制&#xff0c;像公众号、知乎等添加的文字水印的格式都是固定不可修改的 3. 如果需要批量添加&#xff0c…

CentOS7使用yum命令报错

目录结构 前言使用yum命令&#xff0c;报错信息问题排查解决方案参考文章 前言 安装CentOS 7 虚拟机&#xff0c;使用yum命令报错&#xff0c;调查整理如下&#xff1a; 使用yum命令&#xff0c;报错信息 [rootlocalhost ~]# sudo yum install net-tools 已加载插件&#xff…

手机数据恢复照片的2个小妙招,让美好回忆存留

当手机的相册突然变得空空如也&#xff0c;那些昔日的美好回忆仿佛被一阵龙卷风席卷而去&#xff0c;你是否感到惊慌失措&#xff1f;别担心&#xff0c;今天我要为你揭示2个神奇的手机数据恢复照片的小妙招&#xff0c;无需复杂的技术知识&#xff0c;只需跟随步骤&#xff0c…

智能酒精壁炉与会所大厅的氛围搭配

智能酒精壁炉与会所大厅的氛围搭配可以创造出现代、高雅且舒适的环境&#xff0c;提升客人的整体体验。 以下是如何将智能酒精壁炉与会所大厅氛围相协调的几点建议&#xff1a; 现代化和高品位感&#xff1a; 智能酒精壁炉具有现代化的设计和技术特点&#xff0c;能够与会所大…

【育儿心得】让孩子乖乖按时睡觉的6个妙招

大家好&#xff0c;我是小2&#xff0c;一个程序员&#xff0c;也是一个奶爸~ 最近公司项目比较忙&#xff0c;经常加班到晚上12点才下班&#xff0c; 可是下班后发现我家娃还在“加班”玩耍&#xff0c;这个时候又要哄睡、陪玩&#xff0c;往往弄到1-2点钟才可以睡觉。着实一…

软件架构之嵌入式系统设计(2)

软件架构之嵌入式系统设计&#xff08;2&#xff09; 12.4 嵌入式网络系统12.4.1 现场总线网12.4.2 家庭信息网11.4.3 无线数据通信网12.4.4 嵌入式 Internet 12.5 嵌入式数据库管理系统12.5.1 使用环境的特点12.5.2 系统组成与关键技术 12.6 实时系统与嵌入式操作系统12.6.1 嵌…

Python UDP编程之实时聊天与网络监控详解

概要 UDP(User Datagram Protocol,用户数据报协议)是网络协议中的一种,主要用于快速、简单的通信场景。与TCP相比,UDP没有连接、确认、重传等机制,因此传输效率高,但也不保证数据的可靠性和顺序。本文将详细介绍Python中如何使用UDP协议进行网络通信,并包含相应的示例…

优化 Java 数据结构选择与使用,提升程序性能与可维护性

优化 Java 数据结构选择与使用&#xff0c;提升程序性能与可维护性 引言 在软件开发中&#xff0c;数据结构的选择是影响程序性能、内存使用以及代码可维护性的关键因素之一。Java 作为一门广泛使用的编程语言&#xff0c;提供了丰富的内置数据结构&#xff0c;如数组、链表、…