vue 在线聊天实战范例(含选择发送表情、图片、视频、音频,自定义右键快捷菜单,一键复制,左右聊天气泡)

news2025/1/17 13:57:12

最终效果

在这里插入图片描述

完整代码

index.vue

<template>
  <div class="page">
    <div class="leftBox">
      <h1>访客</h1>
      <div class="chatBox">
        <div class="chatRecordBox">
          <div v-for="(item, index) in chatRecordList" :key="index">
            <RightRole
              v-if="item.role === '访客'"
              :type="item.type"
              :content="item.content"
              :avatarURL="visitorAvatarURL"
              @openMenu="openMenu($event, index, true)"
            />
            <LeftRole
              v-if="item.role === '客服'"
              :type="item.type"
              :content="item.content"
              :avatarURL="serviceAvatarURL"
              @openMenu="openMenu($event, index)"
            />
          </div>
        </div>
        <div class="toolBox">
          <ChooseEmotion @getEmo="chooseEmo($event, 'visitor')" />
          <img
            @click="chooseImg('visitor')"
            class="imgIcon"
            src="@/assets/images/图片.svg"
          />
          <img
            @click="chooseVideo('visitor')"
            class="videoIcon"
            src="@/assets/images/视频.svg"
          />
          <img
            @click="chooseAudio('visitor')"
            class="audioIcon"
            src="@/assets/images/音频.svg"
          />
        </div>
        <div
          ref="visitorSendContentBox_Ref"
          contenteditable="true"
          class="sendContentBox"
        ></div>
        <div class="btnBox">
          <el-button @click="visitorSend" size="small">发送</el-button>
        </div>
      </div>
    </div>
    <div class="rightBox">
      <h1>客服</h1>
      <div class="chatBox">
        <div class="chatRecordBox">
          <div v-for="(item, index) in chatRecordList" :key="index">
            <LeftRole
              v-if="item.role === '访客'"
              :type="item.type"
              :content="item.content"
              :avatarURL="visitorAvatarURL"
              @openMenu="openMenu($event, index)"
            />
            <RightRole
              v-if="item.role === '客服'"
              :type="item.type"
              :content="item.content"
              :avatarURL="serviceAvatarURL"
              @openMenu="openMenu($event, index, true)"
            />
          </div>
        </div>
        <div class="toolBox">
          <ChooseEmotion @getEmo="chooseEmo($event, 'service')" />
          <img
            @click="chooseImg('service')"
            class="imgIcon"
            src="@/assets/images/图片.svg"
          />
          <img
            @click="chooseVideo('service')"
            class="videoIcon"
            src="@/assets/images/视频.svg"
          />
          <img
            @click="chooseAudio('service')"
            class="audioIcon"
            src="@/assets/images/音频.svg"
          />
        </div>
        <div
          ref="serviceSendContentBox_Ref"
          contenteditable="true"
          class="sendContentBox"
        ></div>
        <div class="btnBox">
          <el-button @click="serviceSend" size="small">发送</el-button>
        </div>
      </div>

      <el-dialog
        title="图片预览"
        :visible.sync="showPreviewImgWin"
        style="text-align: center"
      >
        <el-image
          style="height: 400px"
          :preview-src-list="[imgSrc]"
          :src="imgSrc"
          fit="contain"
        />
      </el-dialog>

      <!-- 右键快捷菜单 -->
      <ul
        v-show="quickMenuVisible"
        :style="{ left: quickMenu_left + 'px', top: quickMenu_top + 'px' }"
        class="contextmenu"
      >
        <li v-show="ifCan_rollBack" @click="rollBack">撤回</li>
        <li @click="copy">复制</li>
      </ul>
    </div>
  </div>
</template>

<script>
import LeftRole from "./components/leftRole.vue";
import RightRole from "./components/rightRole.vue";
import ChooseEmotion from "./components/chooseEmotion.vue";
import visitorAvatarURL from "@/assets/images/访客.svg";
import serviceAvatarURL from "@/assets/images/客服.svg";

export default {
  watch: {
    quickMenuVisible(value) {
      if (value) {
        document.body.addEventListener("click", this.closeMenu);
      } else {
        document.body.removeEventListener("click", this.closeMenu);
      }
    },
  },
  components: { LeftRole, RightRole, ChooseEmotion },
  mounted() {
    let that = this;

    // 点击图片放大预览
    window.addEventListener("click", function (e) {
      let { target } = e;
      let nodeName = target.nodeName;

      if (nodeName === "IMG") {
        // 获取自定义的属性 preview
        let preview = target.getAttribute("preview");
        // 无 preview 属性的图片,不支持点击放大预览
        if (!preview) {
          return;
        }
        that.imgSrc = target.currentSrc;
        that.showPreviewImgWin = true;
      }
    });
  },
  data() {
    return {
      ifCan_rollBack: false,
      index: null,
      quickMenu_left: 0,
      quickMenu_top: 0,
      quickMenuVisible: false,
      imgSrc: "",
      showPreviewImgWin: false,
      visitorAvatarURL,
      serviceAvatarURL,
      chatRecordList: [],
    };
  },
  methods: {
    // 撤回消息
    rollBack() {
      this.chatRecordList.splice(this.index, 1);
    },
    // js 点击复制到剪贴板函数
    copy() {
      let content = this.chatRecordList[this.index].content;

      if (window.clipboardData) {
        window.clipboardData.setData("text", content);
      } else {
        (function (content) {
          document.oncopy = function (e) {
            e.clipboardData.setData("text", content);
            e.preventDefault();
            document.oncopy = null;
          };
        })(content);
        document.execCommand("Copy");
      }
    },
    // 显示右键快捷菜单
    openMenu(e, index, ifCan_rollBack) {
      this.ifCan_rollBack = ifCan_rollBack;
      this.index = index;
      this.quickMenu_top = e.pageY;
      this.quickMenu_left = e.pageX;
      this.quickMenuVisible = true;
    },
    // 隐藏右键快捷菜单
    closeMenu() {
      this.quickMenuVisible = false;
    },
    // 根据角色,确定编辑框
    getSendContentBox_Ref(role) {
      if (role === "visitor") {
        return "visitorSendContentBox_Ref";
      }
      if (role === "service") {
        return "serviceSendContentBox_Ref";
      }
    },
    // 在编辑框中插入内容
    sendContentBox_insert(role, insertContent) {
      let sendContentBox_Ref = this.getSendContentBox_Ref(role);
      let content = JSON.parse(
        JSON.stringify(this.$refs[sendContentBox_Ref].innerHTML)
      );

      let newContent = "";

      // 光标在编辑框内时
      if (window.getSelection().anchorNode) {
        // 获取光标在编辑框中的下标
        let startIndex = window.getSelection().anchorOffset;
        let ednIndex = window.getSelection().focusOffset;

        newContent =
          content.substring(0, startIndex) +
          insertContent +
          content.substring(ednIndex);
      } else {
        // 光标不在编辑框内时
        newContent = content + insertContent;
      }

      this.$refs[sendContentBox_Ref].innerHTML = newContent;
    },
    chooseEmo(emo, role) {
      this.sendContentBox_insert(role, emo);
    },
    // 访客发送消息
    visitorSend() {
      let content = this.$refs.visitorSendContentBox_Ref.innerHTML;
      if (!content) {
        this.$message({
          message: "请输入发送内容!",
          type: "warning",
        });
        return;
      }

      this.chatRecordList.push({
        role: "访客",
        content: content,
        type: "text",
      });

      // 发送后,清空发送框的内容
      this.$refs.visitorSendContentBox_Ref.innerHTML = "";
    },
    // 客服发送消息
    serviceSend() {
      let content = this.$refs.serviceSendContentBox_Ref.innerHTML;
      if (!content) {
        this.$message({
          message: "请输入发送内容!",
          type: "warning",
        });
        return;
      }

      this.chatRecordList.push({
        role: "客服",
        content: content,
        type: "text",
      });

      // 发送后,清空发送框的内容
      this.$refs.serviceSendContentBox_Ref.innerHTML = "";
    },
    // 选择图片
    chooseImg(role) {
      let that = this;
      let input = document.createElement("input");
      input.setAttribute("type", "file");
      // 支持多选
      input.setAttribute("multiple", "multiple");
      input.accept = "image/*";
      input.addEventListener("change", (e) => {
        let file = e.path[0].files[0];
        // 浏览器兼容性处理(有的浏览器仅存在 Window.URL)
        const windowURL = window.URL || window.webkitURL;
        // createObjectURL 函数会根据传入的参数创建一个指向该参数对象的URL
        let filePath = windowURL.createObjectURL(file);

        let tmp_imgDom = document.createElement("img");
        tmp_imgDom.setAttribute("src", filePath);
        tmp_imgDom.setAttribute("height", 30);
        tmp_imgDom.setAttribute("preview", true);
        tmp_imgDom.style.cursor = "pointer";

        let tmp_divDom = document.createElement("div");
        tmp_divDom.appendChild(tmp_imgDom);

        that.chatRecordList.push({
          role: role === "visitor" ? "访客" : "客服",
          content: tmp_divDom.innerHTML,
          type: "img",
        });
      });
      input.click();
    },
    // 选择视频
    chooseVideo(role) {
      let that = this;
      let input = document.createElement("input");
      input.setAttribute("type", "file");
      // 支持多选
      input.setAttribute("multiple", "multiple");
      input.accept = "video/*";
      input.addEventListener("change", (e) => {
        let file = e.path[0].files[0];
        // 浏览器兼容性处理(有的浏览器仅存在 Window.URL)
        const windowURL = window.URL || window.webkitURL;
        // createObjectURL 函数会根据传入的参数创建一个指向该参数对象的URL
        let filePath = windowURL.createObjectURL(file);

        let tmp_videoDom = document.createElement("video");

        tmp_videoDom.setAttribute("src", filePath);
        tmp_videoDom.setAttribute("height", 100);
        tmp_videoDom.setAttribute("controls", "controls");
        tmp_videoDom.style.cursor = "pointer";

        let tmp_divDom = document.createElement("div");
        tmp_divDom.appendChild(tmp_videoDom);

        that.chatRecordList.push({
          role: role === "visitor" ? "访客" : "客服",
          content: tmp_divDom.innerHTML,
          type: "video",
        });
      });
      input.click();
    },

    // 选择音频
    chooseAudio(role) {
      let that = this;
      let input = document.createElement("input");
      input.setAttribute("type", "file");
      // 支持多选
      input.setAttribute("multiple", "multiple");
      input.accept = "audio/*";
      input.addEventListener("change", (e) => {
        let file = e.path[0].files[0];
        // 浏览器兼容性处理(有的浏览器仅存在 Window.URL)
        const windowURL = window.URL || window.webkitURL;
        // createObjectURL 函数会根据传入的参数创建一个指向该参数对象的URL
        let filePath = windowURL.createObjectURL(file);

        let tmp_audioDom = document.createElement("audio");

        tmp_audioDom.setAttribute("src", filePath);
        tmp_audioDom.setAttribute("height", 30);
        tmp_audioDom.setAttribute("controls", "controls");
        tmp_audioDom.style.cursor = "pointer";
        let tmp_divDom = document.createElement("div");
        tmp_divDom.appendChild(tmp_audioDom);

        that.chatRecordList.push({
          role: role === "visitor" ? "访客" : "客服",
          content: tmp_divDom.innerHTML,
          type: "audio",
        });
      });
      input.click();
    },
  },
};
</script>

<style scoped>
.page {
  display: flex;
  justify-content: space-around;
}
.chatBox {
  width: 400px;
  padding: 10px;
  background: #409eff;
  border-radius: 10px;
}
.chatRecordBox {
  padding: 10px;
  height: 400px;
  border-radius: 10px;
  background: white;
  overflow: auto;
}

.sendContentBox {
  height: 100px;
  padding: 10px;
  background: white;
  overflow: auto;
  border: 1px solid rgb(195, 187, 187);
}
.btnBox {
  padding-top: 10px;
  text-align: right;
}

h1 {
  line-height: 40px;
  font-weight: bold;
}
.toolBox {
  padding: 4px;
  background: white;
  margin-top: 10px;
  display: flex;
  align-items: center;
}

img {
  cursor: pointer !important;
  display: inline-block;
}
.imgIcon {
  margin-left: 4px;
  height: 20px;
  cursor: pointer;
}
.videoIcon {
  width: 22px;
  cursor: pointer;
  margin-left: 4px;
}
.audioIcon {
  width: 18px;
  cursor: pointer;
  margin-left: 4px;
  border-radius: 4px;
}
/* 右键快捷菜单的样式 */
.contextmenu {
  margin: 0;
  background: #fff;
  z-index: 3000;
  position: absolute;
  list-style-type: none;
  padding: 5px 0;
  border-radius: 4px;
  font-size: 12px;
  font-weight: 400;
  color: #333;
  box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
}

.contextmenu li {
  margin: 0;
  padding: 7px 16px;
  cursor: pointer;
}

.contextmenu li:hover {
  background: #eee;
}
</style> 

components/leftRole.vue

<template>
  <div class="leftRoleBox">
    <div>
      <img class="avatar" :src="avatarURL" />
    </div>
    <!-- 左侧的聊天气泡尖角 -->
    <div v-if="type === 'text'" class="triangle-top-left">
      <div class="triangle-top-left2"></div>
    </div>
    <!-- 聊天气泡 -->
    <div
      @contextmenu.prevent="openMenu($event)"
      v-html="content"
      :class="type === 'text' ? 'bubble-left' : 'multimediaBox'"
    ></div>
  </div>
</template>

<script>
export default {
  props: {
    type: String,
    content: String,
    avatarURL: String,
  },
  methods: {
    openMenu(e) {
      this.$emit("openMenu", e);
    },
  },
};
</script>

<style scoped>
.leftRoleBox {
  display: flex;
  align-items: flex-start;
}

.avatar {
  width: 30px;
}

.triangle-top-left {
  position: relative;
  border-top: 4px solid #409eff;
  border-left: 4px solid transparent;
  margin-top: 20px;
  margin-left: 2px;
}

.triangle-top-left2 {
  top: -2px;
  left: 0px;
  position: absolute;
  border-top: 4px solid #ffffff;
  border-left: 4px solid transparent;
}

.bubble-left {
  text-align: justify;
  border-radius: 4px;
  padding: 4px;
  border: 2px solid #409eff;
  font-size: 12px;
  line-height: 16px;
  margin-top: 10px;
}

.multimediaBox {
  padding: 10px;
}
</style>

components/rightRole.vue

<template>
  <div class="rightRoleBox">
    <div>
      <img class="avatar" :src="avatarURL" />
    </div>

    <!-- 左侧的聊天气泡尖角 -->
    <div v-if="type === 'text'" class="triangle-top-right">
      <div class="triangle-top-right2"></div>
    </div>
    <!-- 聊天气泡 -->
    <div
      @contextmenu.prevent="openMenu($event)"
      v-html="content"
      :class="type === 'text' ? 'bubble-right' : 'multimediaBox'"
    ></div>
  </div>
</template>

<script>
export default {
  props: {
    type: String,
    content: String,
    avatarURL: String,
  },
  methods: {
    openMenu(e) {
      this.$emit("openMenu", e);
    },
  },
};
</script>

<style scoped>
.rightRoleBox {
  display: flex;
  align-items: flex-start;
  flex-direction: row-reverse;
}

.avatar {
  width: 30px;
}

.triangle-top-right {
  position: relative;
  border-top: 4px solid #409eff;
  border-right: 4px solid transparent;
  margin-top: 20px;
  margin-right: 2px;
}

.triangle-top-right2 {
  top: -2px;
  right: 0px;
  position: absolute;
  border-top: 4px solid #ffffff;
  border-right: 4px solid transparent;
}

.bubble-right {
  text-align: justify;
  border-radius: 4px;
  padding: 4px;
  border: 2px solid #409eff;
  font-size: 12px;
  line-height: 16px;
  margin-top: 10px;
}
.multimediaBox {
  padding: 10px;
}
</style>

components/chooseEmotion.vue

<template>
  <div class="chatIcon">
    <el-popover placement="top-start" width="400" trigger="hover">
      <div class="emotionList">
        <a
          href="javascript:void(0);"
          @click="getEmo(index)"
          v-for="(item, index) in faceList"
          :key="index"
          class="emotionItem"
          >{{ item }}</a
        >
      </div>
      <img height="20" slot="reference" src="@/assets/images/表情.svg" />
    </el-popover>
  </div>
</template>
<script>
import emojiData from "@/assets/data/emoji.json";
export default {
  mounted() {
    for (let i in emojiData) {
      this.faceList.push(emojiData[i].char);
    }
  },
  data() {
    return {
      faceList: [],
    };
  },
  methods: {
    getEmo(index) {
      this.$emit("getEmo", this.faceList[index]);
    },
  },
};
</script>

  
<style scoped>
.chatIcon {
  font-size: 25px;
}
.emotionList {
  display: flex;
  flex-wrap: wrap;
  padding: 5px;
}

.emotionItem {
  width: 10%;
  font-size: 20px;
  text-align: center;
}
/*包含以下四种的链接*/
.emotionItem {
  text-decoration: none;
}
/*正常的未被访问过的链接*/
.emotionItem:link {
  text-decoration: none;
}
/*已经访问过的链接*/
.emotionItem:visited {
  text-decoration: none;
}
/*鼠标划过(停留)的链接*/
.emotionItem:hover {
  text-decoration: none;
}
/* 正在点击的链接*/
.emotionItem:active {
  text-decoration: none;
}
</style>

src\assets\data\emoji.json

[
  {
    "codes": "1F600",
    "char": "😀",
    "name": "grinning face"
  },
  {
    "codes": "1F603",
    "char": "😃",
    "name": "grinning face with big eyes"
  },
  {
    "codes": "1F604",
    "char": "😄",
    "name": "grinning face with smiling eyes"
  },
  {
    "codes": "1F601",
    "char": "😁",
    "name": "beaming face with smiling eyes"
  },
  {
    "codes": "1F606",
    "char": "😆",
    "name": "grinning squinting face"
  },
  {
    "codes": "1F605",
    "char": "😅",
    "name": "grinning face with sweat"
  },
  {
    "codes": "1F923",
    "char": "🤣",
    "name": "rolling on the floor laughing"
  },
  {
    "codes": "1F602",
    "char": "😂",
    "name": "face with tears of joy"
  },
  {
    "codes": "1F642",
    "char": "🙂",
    "name": "slightly smiling face"
  },
  {
    "codes": "1F643",
    "char": "🙃",
    "name": "upside-down face"
  },
  {
    "codes": "1F609",
    "char": "😉",
    "name": "winking face"
  },
  {
    "codes": "1F60A",
    "char": "😊",
    "name": "smiling face with smiling eyes"
  },
  {
    "codes": "1F607",
    "char": "😇",
    "name": "smiling face with halo"
  },
  {
    "codes": "1F970",
    "char": "🥰",
    "name": "smiling face with hearts"
  },
  {
    "codes": "1F60D",
    "char": "😍",
    "name": "smiling face with heart-eyes"
  },
  {
    "codes": "1F929",
    "char": "🤩",
    "name": "star-struck"
  },
  {
    "codes": "1F618",
    "char": "😘",
    "name": "face blowing a kiss"
  },
  {
    "codes": "1F617",
    "char": "😗",
    "name": "kissing face"
  },
  {
    "codes": "1F61A",
    "char": "😚",
    "name": "kissing face with closed eyes"
  },
  {
    "codes": "1F619",
    "char": "😙",
    "name": "kissing face with smiling eyes"
  },
  {
    "codes": "1F44B",
    "char": "👋",
    "name": "waving hand"
  },
  {
    "codes": "1F91A",
    "char": "🤚",
    "name": "raised back of hand"
  },
  {
    "codes": "1F590",
    "char": "🖐",
    "name": "hand with fingers splayed"
  },
  {
    "codes": "270B",
    "char": "✋",
    "name": "raised hand"
  },
  {
    "codes": "1F596",
    "char": "🖖",
    "name": "vulcan salute"
  },
  {
    "codes": "1F44C",
    "char": "👌",
    "name": "OK hand"
  },
  {
    "codes": "1F90F",
    "char": "🤏",
    "name": "pinching hand"
  },
  {
    "codes": "270C",
    "char": "✌",
    "name": "victory hand"
  },
  {
    "codes": "1F91E",
    "char": "🤞",
    "name": "crossed fingers"
  },
  {
    "codes": "1F91F",
    "char": "🤟",
    "name": "love-you gesture"
  },
  {
    "codes": "1F918",
    "char": "🤘",
    "name": "sign of the horns"
  },
  {
    "codes": "1F919",
    "char": "🤙",
    "name": "call me hand"
  },
  {
    "codes": "1F448",
    "char": "👈",
    "name": "backhand index pointing left"
  },
  {
    "codes": "1F449",
    "char": "👉",
    "name": "backhand index pointing right"
  },
  {
    "codes": "1F446",
    "char": "👆",
    "name": "backhand index pointing up"
  },
  {
    "codes": "1F595",
    "char": "🖕",
    "name": "middle finger"
  },
  {
    "codes": "1F447",
    "char": "👇",
    "name": "backhand index pointing down"
  },
  {
    "codes": "261D FE0F",
    "char": "☝️",
    "name": "index pointing up"
  },
  {
    "codes": "1F44D",
    "char": "👍",
    "name": "thumbs up"
  },
  {
    "codes": "1F44E",
    "char": "👎",
    "name": "thumbs down"
  },
  {
    "codes": "270A",
    "char": "✊",
    "name": "raised fist"
  },
  {
    "codes": "1F44A",
    "char": "👊",
    "name": "oncoming fist"
  },
  {
    "codes": "1F91B",
    "char": "🤛",
    "name": "left-facing fist"
  },
  {
    "codes": "1F91C",
    "char": "🤜",
    "name": "right-facing fist"
  }
]

配套图片素材下载

链接:https://pan.baidu.com/s/170pb-MJlMxG2nRj_3Y2VFw?pwd=oknr
提取码:oknr

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

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

相关文章

DBCO-COOH分子量:305.3,CAS:1353016-70-2,二苯基环辛炔-羧基;类似有DBCO-NH2、SH、MAL、NHS等等

中文名称&#xff1a;二苯基环辛炔-羧基 英文名称&#xff1a;DBCO-acid 英文别称&#xff1a;DBCO-COOH cas: 1353016-70-2 分子式&#xff1a;C19H15NO3 分子量&#xff1a;305.3 DBCO-COOH是DBCO 衍生化的常用构件&#xff0c;在EDC、DCC和HATU等活化剂存在下&#xf…

linux kernel menuconfig kconfig makefile

概述 menuconfig是Linux平台用于管理代码工程、模块及功能的实用工具。 menuconfig的使用方式通常是在编译系统之前在系统源代码根目录下执行make menuconfig命令从而打开一个图形化配置界面&#xff0c;再通过对各项的值按需配置从而达到影响系统编译结果的目的。 Nuttx的me…

Spring Boot 数据库操作Druid和HikariDataSource

目录 Spring Boot 数据库操作 应用实例-需求 创建测试数据库和表 进行数据库开发&#xff0c; 在pom.xml 引入data-jdbc starter 参考官方文档 需要在pom.xml 指定导入数据库驱动 在application.yml 配置操作数据源的信息 创建bean\Furn.java 测试结果 整合Druid 到…

六、easyUI中的window(窗口)组件

1.window&#xff08;窗口&#xff09;组件的概述 窗口控件是一个浮动和可拖拽的面板&#xff0c;它可以用作应用程序窗口。默认情况下&#xff0c;窗口可以移动&#xff0c;调整大小和关闭。它的内容也可以被定义为静态HTML或要么通过Ajax动态加载 2.window&#xff08;窗口&…

MySQL-备份+日志:介质故障与数据库恢复

MySQL-备份日志&#xff1a;介质故障与数据库恢复 第1关&#xff1a;备份与恢复任务描述相关知识MySQL的恢复机制MySQL的备份与恢复工具编程要求代码参考 第2关 备份日志&#xff1a;介质故障的发生与数据库的恢复任务描述相关知识编程要求测试说明代码参考 第1关&#xff1a;备…

《分布式微服务电商源码》-项目简介

1.常见的电商模式 市面上有 5 种常见的电商模式 B2B、B2C、C2B、C2C、O2O 1.1.B2B 模式 B2B(Business to Business)&#xff0c;是指商家和商家建立的商业关系&#xff0c;如阿里巴巴. 1.2.B2C 模式 B2C(Business to Consumer) 就是我们经常看到的供应商直接把商品买个用户&a…

057:cesium设置纯颜色材质

第057个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中设置纯颜色的材质,颜色的表达方式可以参考这篇文章 Cesium七种方法设置颜色 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共87行)相关API参考:专…

用友BIP新零售产品发布,与零售企业共创新未来

随着数智化时代的到来&#xff0c;零售企业不断面临着更多的挑战和机遇。为了满足消费者多元化的需求&#xff0c;零售企业需要采用多种方式来提高竞争力&#xff0c;如多渠道销售、线上线下融合、数智化运营、个性化营销和无缝化体验等。用友BIP新零售为零售企业提供了数智化转…

Jenkins的持续集成与持续部署

系统总体架构 Gitlab Gitlab是一款主要用于代码管理的工具&#xff0c;相较于GitHub&#xff0c;可以自己搭建服务器&#xff0c;这可以避免因为网络速度慢导致部署效率低下&#xff0c;同时&#xff0c;自己搭建服务器&#xff0c;安全性更高。Jenkins Jenkins主要用于管理版本…

改图片分辨率:提升图像质量的关键步骤

无论您是一名摄影师、设计师&#xff0c;还是一个爱好者&#xff0c;改变图片分辨率都是必不可少的工作。在本文中&#xff0c;我们将介绍改图片分辨率的作用以及它在哪些场景下使用。 改图片分辨率介绍 图片分辨率是指图像中所包含的像素数量&#xff0c;通常以“每英寸像素…

threejs 相机OrbitControls常用方法及属性

相机控件OrbitControls 通过OrbitControls可以对三维场景进行缩放、平移、旋转&#xff0c;本质上改变的不是场景&#xff0c;而是相机的参数&#xff0c;相机的位置角度不同&#xff0c;同一个场景的渲染效果是不一样&#xff0c;比如相机围绕着一个场景旋转&#xff0c;就像…

有哪些工具软件一旦用了就离不开?

&#x1f496;前言 目前&#xff0c;随着科技的快速发展&#xff0c;电脑已经进入了许许多多人的生活 &#xff0c;在平日的学习、工作和生活里&#xff0c;我们会用的各种各样的强大软件。市面上除了某些大公司开发在强大软件&#xff0c;还有各路大神开发具有某些功能的强大…

nodejs+vue网上课程在线教学网站平台a53y0

(1) vue引入elementui 1.使用npm安装element-ui npm i element-ui -S 2.在main.js中导入使用vue import element-ui/lib/theme-chalk/index.css //别忘了导入样式 import ElementUI from element-ui Vue.use(ElementUI) 后端&#xff1a;java(springbootssm)/python/php/node…

MySQL入门到精通——运维篇(基础篇——进阶篇——运维篇)本文以MySQL8.0版本以上为例

文章目录 前言MySQL——运维篇一、日志1.日志-错误日志2.日志-二进制日志3.日志-查询日志4.日志-慢查询日志 二、主从复制1.主从复制-概述2.主从复制-原理3.主从复制-搭建3.1.主从复制-搭建-主库配置3.2.主从复制-搭建-从库配置3.2.主从复制-搭建-测试 三、分库分表1.分库分表-…

Nacos简介和安装

1.Nacos简介 1.1.为什么叫Nacos 前四个字母分别为Naming和Configuration的前两个字母&#xff0c;最后的s为Service。 1.2.是什么 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。Nacos: Dynamic Naming and Configuration ServiceNacos就是注册中心&am…

Fiddler 到底能做什么?一篇文章给你详细介绍

Fiddler是检查 Http 流量的最流行的工具之一。此工具可帮助我们非常轻松地测试 REST API / SOAP Web 请求。 题主的问题&#xff1a;Fiddler 到底能做什么。仔细思考&#xff0c;其实有两层意思&#xff1a; 第一层&#xff1a;Fiddler工具的应用范围 第二层&#xff1a;Fiddl…

手搓哈希表

手搓哈希 哈希概念哈希冲突为什么会有哈希冲突的产生?常见的哈希函数 如何解决哈希冲突&#xff1f;闭散列闭散列的简单模拟实现插入查找删除 总代码开散列开散列的模拟实现插入删除查找 存在的问题迭代器 开散列总的代码 哈希概念 顺序结构以及平衡树中&#xff0c;元素关键码…

在研制带处理器的电子产品时,如何提高抗干扰能力和电磁兼容性?

一、下面的一些系统要特别注意抗电磁干扰 1、微控制器时钟频率特别高&#xff0c;总线周期特别快的系统。 2、系统含有大功率&#xff0c;大电流驱动电路&#xff0c;如产生火花的继电器&#xff0c;大电流开关等。 3、含微弱模拟信号电路以及高精度A/D变换电路的系统。 二、…

GPU物理位置查询:从nvidia编号到物理位置

supermicro AS -4125GS-TNRT 服务器GPU出现问题&#xff0c;有几张GPU速度较慢。需要确定其物理位置,具体流程如下&#xff1a; 1 GPU编号与BUS_ID对应关系 使用nvidia-smi即可查看&#xff0c;如下所示&#xff1a;![在这里插入图片描述](https://img-blog.csdnimg.cn/3807d…

分布式锁解决方案_基于Redisson实现的分布式锁实现

Redisson介绍&#xff1a; https://github.com/redisson/redisson/wiki Redisson - 是一个高级的分布式协调Redis客服端&#xff0c;能帮助用户在分布式环境中轻松实现一些Java的对象&#xff0c;Redisson、Jedis、Lettuce是三个不同的操作 Redis 的客户端&#xff0c;Jedis、…