vue 简单实现豆包ai的输入框

85 阅读1分钟

豆包ai的输入框。在输入框中有其他输入框

使用vue简单实现了下。

<template>
  <div class="page-container">
    <div class="input" contenteditable="true" id="inner">
      <template v-for="item in conArr" :key="item.id">
        <span v-if="item.type === 'text'">{{ item.con }}</span>
        <span
          class="input-con"
          contenteditable="false"
          v-if="item.type === 'input'"
        >
          <span
            :ref="(el) => { if (el) inputref.push(el as HTMLElement) }"
            contenteditable="true"
            class="my-input"
            :data-id="item.id"
            :style="{ position: item.isShowPla ? 'absolute' : 'relative' }"
          >
            {{ item.con }}
          </span>
          <span v-if="item.isShowPla" class="my-inputpla">{{
            item.placeholder
          }}</span>
        </span>
      </template>
    </div>
    <button @click="printData">打印数据</button>
  </div>
</template>

<script setup lang="ts">
import { onMounted, ref } from "vue";
// 声明为数组引用
const inputref = ref<HTMLElement[]>([]);

const conArr = ref([
  { id: "1", type: "text", con: "我是一个" },
  {
    id: "2",
    type: "input",
    isShowPla: false,
    con: "博主",
    placeholder: "[输入职业]",
  },
  { id: "3", type: "text", con: ",帮我写一篇关于" },
  {
    id: "4",
    type: "input",
    isShowPla: true,
    con: "",
    placeholder: "[输入主题]",
  },
  { id: "5", type: "text", con: "的" },
  {
    id: "6",
    type: "input",
    isShowPla: true,
    con: "",
    placeholder: "[输入平台:如公众号、知乎、头条等]",
  },
  { id: "7", type: "text", con: "文章,需要符合该平台写作风格。" },
]);

//添加监听事件
const handleObserver = (ele: HTMLElement) => {
  const observer = new MutationObserver(function (mutations) {
    mutations.forEach((mutation) => {
      if (mutation.type === "childList" || mutation.type === "characterData") {
        const id = ele.dataset.id;
        const findItem = conArr.value.find((item) => item.id === id);
        if (findItem) {
          findItem.con = ele.textContent ?? "";
          if (!findItem.con) {
            findItem.isShowPla = true;
          } else {
            findItem.isShowPla = false;
          }
        }
      }
    });
  });
  // 开始观察内层元素及其子树的变化
  observer.observe(ele, {
    childList: true,
    subtree: true,
    characterData: true,
  });
};

const printData = () => {
  let str = "";
  conArr.value.forEach((item) => {
    str += item.con;
  });
  console.log(str);
};

onMounted(() => {
  inputref.value.forEach((ele) => {
    handleObserver(ele);
  });
});
</script>

<style scoped>
.page-container {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: center;

  .input {
    width: 500px;
    height: 100px;
    border: 1px solid #ccc;
    border-radius: 5px;
    margin: 0;
    padding: 5px;

    .input-con {
      position: relative;
      white-space: nowrap;

      .my-input {
        position: absolute;
        width: 100%;
        height: 100%;
        top: 0;
        left: 0;
        border-radius: 5px;
        padding: 0 5px;
        background-color: rgba(0, 87, 255, 0.06);
        color: rgb(0, 87, 255);
        z-index: 1;
      }

      .my-inputpla {
        color: rgba(0, 87, 255, 0.5);
        background-color: rgba(0, 87, 255, 0.06);
        border-radius: 5px;
        padding: 0 5px;
        z-index: -1;
      }
    }
  }
}
</style>