在豆包的输入框中有这么一个功能,是输入框内存在输入框和下拉菜单的组件,图片所示,暂时模拟了一个一样在输入框组件
在这里插入图片描述
在这里插入图片描述

<template>
  <div class="rich-input-container">
    <div
      ref="editor"
      class="rich-input-editor"
      contenteditable="true"
      @keydown="handleKeydown"
      @input="handleInput"
      @click="handleClick"
      @compositionstart="isComposing = true"
      @compositionend="isComposing = false"
    ></div>
  </div>
</template>

<script>
export default {
  name: "RichInputWithDropdown",
  data() {
    return {
      isEmpty: true,
      dropdownId: 0,
      inputBoxId: 0,
      isComposing: false,
      debugContent: ""
    };
  },
  methods: {
    handleInput(e) {
      const editor = this.$refs.editor;
      this.isEmpty =
        !editor.textContent.trim() &&
        editor.querySelectorAll(".dropdown-wrapper, .input-box-wrapper")
          .length === 0;
      this.updateDebugContent();
    },

    handleKeydown(e) {
      // 输入法组合中不处理
      if (this.isComposing) return;

      // 处理 Backspace 删除
      if (e.key === "Backspace") {
        const selection = window.getSelection();
        if (!selection.rangeCount) return;

        const range = selection.getRangeAt(0);

        // 情况1: 选区不是折叠的(有选中内容),让浏览器默认处理
        if (!range.collapsed) return;

        const editor = this.$refs.editor;

        // 情况2: 光标在编辑器最开头
        if (range.startOffset === 0 && range.startContainer === editor) {
          const firstChild = editor.firstChild;
          if (firstChild && this.isCustomComponent(firstChild)) {
            e.preventDefault();
            firstChild.remove();
            this.handleInput(e);
            return;
          }
        }

        // 情况3: 光标在文本节点开头
        if (
          range.startContainer.nodeType === Node.TEXT_NODE &&
          range.startOffset === 0
        ) {
          const textNode = range.startContainer;
          const prevSibling = textNode.previousSibling;

          if (prevSibling && this.isCustomComponent(prevSibling)) {
            e.preventDefault();
            prevSibling.remove();
            this.handleInput(e);
            return;
          }
        }

        // 情况4: 光标紧跟在组件后面的元素节点
        if (range.startContainer.nodeType === Node.ELEMENT_NODE) {
          const container = range.startContainer;
          const offset = range.startOffset;

          if (offset > 0) {
            const prevNode = container.childNodes[offset - 1];
            if (prevNode && this.isCustomComponent(prevNode)) {
              e.preventDefault();
              prevNode.remove();
              this.handleInput(e);
              return;
            }
          }
        }
      }

      // Delete 键向前删除
      if (e.key === "Delete") {
        const selection = window.getSelection();
        if (!selection.rangeCount) return;

        const range = selection.getRangeAt(0);
        if (!range.collapsed) return;

        // 光标在文本节点末尾
        if (range.startContainer.nodeType === Node.TEXT_NODE) {
          const textNode = range.startContainer;
          if (range.startOffset === textNode.length) {
            const nextSibling = textNode.nextSibling;
            if (nextSibling && this.isCustomComponent(nextSibling)) {
              e.preventDefault();
              nextSibling.remove();
              this.handleInput(e);
              return;
            }
          }
        }

        // 光标在元素节点中
        if (range.startContainer.nodeType === Node.ELEMENT_NODE) {
          const container = range.startContainer;
          const offset = range.startOffset;
          const nextNode = container.childNodes[offset];

          if (nextNode && this.isCustomComponent(nextNode)) {
            e.preventDefault();
            nextNode.remove();
            this.handleInput(e);
            return;
          }
        }
      }
    },

    handleClick(e) {
      // 阻止组件内部点击冒泡
      if (e.target.tagName === "SELECT" || e.target.tagName === "INPUT") {
        e.stopPropagation();
      }

      // 处理自定义下拉菜单的点击
      const editor = this.$refs.editor;
      const dropdownWrapper = e.target.closest(".dropdown-wrapper");
      if (dropdownWrapper) {
        const dropdownButton = dropdownWrapper.querySelector(
          ".dropdown-button"
        );
        const dropdownMenu = dropdownWrapper.querySelector(".dropdown-menu");

        // 如果点击的是下拉按钮
        if (
          dropdownButton &&
          (e.target === dropdownButton || dropdownButton.contains(e.target))
        ) {
          e.stopPropagation();
          // 关闭其他所有下拉菜单
          editor.querySelectorAll(".dropdown-menu.open").forEach(menu => {
            if (menu !== dropdownMenu) {
              menu.classList.remove("open");
              // 重置箭头状态
              const otherWrapper = menu.closest(".dropdown-wrapper");
              if (otherWrapper) {
                const otherArrow = otherWrapper.querySelector(
                  ".dropdown-arrow"
                );
                if (otherArrow) {
                  otherArrow.style.transform = "rotate(0deg)";
                }
              }
            }
          });
          // 切换当前下拉菜单
          if (dropdownMenu) {
            const isOpen = dropdownMenu.classList.toggle("open");
            // 更新箭头旋转状态
            const arrow = dropdownWrapper.querySelector(".dropdown-arrow");
            if (arrow) {
              arrow.style.transform = isOpen
                ? "rotate(180deg)"
                : "rotate(0deg)";
            }
            // 如果打开菜单,动态计算位置
            if (isOpen) {
              this.updateDropdownPosition(dropdownButton, dropdownMenu);
            }
          }
        }
        // 如果点击的是下拉菜单选项
        else if (dropdownMenu && dropdownMenu.contains(e.target)) {
          e.stopPropagation();
        }
        // 如果点击的是下拉菜单外部,关闭菜单
        else if (dropdownMenu && dropdownMenu.classList.contains("open")) {
          dropdownMenu.classList.remove("open");
          // 重置箭头状态
          const arrow = dropdownWrapper.querySelector(".dropdown-arrow");
          if (arrow) {
            arrow.style.transform = "rotate(0deg)";
          }
        }
      } else {
        // 点击编辑器其他区域,关闭所有下拉菜单
        editor.querySelectorAll(".dropdown-menu.open").forEach(menu => {
          menu.classList.remove("open");
          // 重置箭头状态
          const wrapper = menu.closest(".dropdown-wrapper");
          if (wrapper) {
            const arrow = wrapper.querySelector(".dropdown-arrow");
            if (arrow) {
              arrow.style.transform = "rotate(0deg)";
            }
          }
        });
      }
    },

    // 判断是否是自定义组件(下拉框或输入框)
    isCustomComponent(node) {
      return (
        node &&
        node.classList &&
        (node.classList.contains("dropdown-wrapper") ||
          node.classList.contains("input-box-wrapper"))
      );
    },

    // 更新下拉菜单位置(使用 fixed 定位避免被遮挡)
    updateDropdownPosition(button, menu) {
      // 使用 nextTick 确保菜单已渲染
      this.$nextTick(() => {
        const buttonRect = button.getBoundingClientRect();
        const menuHeight = menu.offsetHeight || 200; // 实际高度
        const viewportHeight = window.innerHeight;

        // 计算位置
        let top = buttonRect.bottom + 6; // 按钮底部 + 间距
        let left = buttonRect.left;

        // 如果下方空间不足,显示在上方
        if (top + menuHeight > viewportHeight && buttonRect.top > menuHeight) {
          top = buttonRect.top - menuHeight - 6;
        }

        // 如果右侧超出视口,调整位置
        const menuWidth = menu.offsetWidth || 118;
        if (left + menuWidth > window.innerWidth) {
          left = window.innerWidth - menuWidth - 10;
        }

        // 确保不超出左侧
        if (left < 10) {
          left = 10;
        }

        // 应用位置
        menu.style.position = "fixed";
        menu.style.top = `${top}px`;
        menu.style.left = `${left}px`;
        menu.style.width = `${Math.max(menuWidth, buttonRect.width)}px`;
      });
    },

    insertDropdown(options = null) {
      const editor = this.$refs.editor;
      editor.focus();

      const selection = window.getSelection();
      let range;

      if (selection.rangeCount > 0) {
        range = selection.getRangeAt(0);
      } else {
        range = document.createRange();
        range.selectNodeContents(editor);
        range.collapse(false);
        selection.removeAllRanges();
        selection.addRange(range);
      }

      // 创建下拉框包装器
      const wrapper = document.createElement("span");
      wrapper.className = "dropdown-wrapper";
      wrapper.contentEditable = "false";
      wrapper.setAttribute("data-id", `dropdown-${this.dropdownId++}`);
      wrapper.setAttribute("data-type", "dropdown");

      // 使用传入的options,如果没有则使用默认值
      const dropdownOptions = options || [
        { value: "1:1", text: "1:1 正方形,头像" },
        { value: "3:2", text: "3:2 比例" },
        { value: "16:9", text: "16:9 横屏" },
        { value: "9:16", text: "9:16 竖屏" }
      ];

      // 创建下拉按钮
      const button = document.createElement("button");
      button.className = "dropdown-button";
      button.type = "button";
      button.setAttribute("data-value", dropdownOptions[0].value);
      button.appendChild(document.createTextNode(dropdownOptions[0].text));

      // 添加下拉箭头图标
      const arrow = document.createElement("span");
      arrow.className = "dropdown-arrow";
      arrow.innerHTML = "▼";
      button.appendChild(arrow);

      // 创建下拉菜单
      const menu = document.createElement("div");
      menu.className = "dropdown-menu";

      dropdownOptions.forEach((opt, index) => {
        const menuItem = document.createElement("div");
        menuItem.className = "dropdown-menu-item";
        menuItem.setAttribute("data-value", opt.value);
        menuItem.textContent = opt.text;

        // 默认选中第一个
        if (index === 0) {
          menuItem.classList.add("selected");
        }

        // 点击选项时更新按钮文本和值
        menuItem.addEventListener("click", e => {
          e.stopPropagation();
          const value = menuItem.getAttribute("data-value");
          const text = menuItem.textContent;

          // 更新按钮
          button.setAttribute("data-value", value);
          // 更新文本(保留箭头节点)
          const textNode = Array.from(button.childNodes).find(
            node => node.nodeType === Node.TEXT_NODE
          );
          if (textNode) {
            textNode.textContent = text;
          } else {
            // 如果没有文本节点,在箭头前插入文本
            const arrow = button.querySelector(".dropdown-arrow");
            if (arrow) {
              button.insertBefore(document.createTextNode(text), arrow);
            } else {
              button.textContent = text;
              const newArrow = document.createElement("span");
              newArrow.className = "dropdown-arrow";
              newArrow.innerHTML = "▼";
              button.appendChild(newArrow);
            }
          }

          // 更新选中状态
          menu.querySelectorAll(".dropdown-menu-item").forEach(item => {
            item.classList.remove("selected");
          });
          menuItem.classList.add("selected");

          // 关闭菜单
          menu.classList.remove("open");

          this.updateDebugContent();
        });

        // hover效果
        menuItem.addEventListener("mouseenter", () => {
          menuItem.classList.add("hover");
        });
        menuItem.addEventListener("mouseleave", () => {
          menuItem.classList.remove("hover");
        });

        menu.appendChild(menuItem);
      });

      wrapper.appendChild(button);
      wrapper.appendChild(menu);

      this.insertComponent(wrapper, range, selection);
    },

    insertInputBox(placeholder = "某某购人", value = "") {
      const editor = this.$refs.editor;
      editor.focus();

      const selection = window.getSelection();
      let range;

      if (selection.rangeCount > 0) {
        range = selection.getRangeAt(0);
      } else {
        range = document.createRange();
        range.selectNodeContents(editor);
        range.collapse(false);
        selection.removeAllRanges();
        selection.addRange(range);
      }

      // 创建输入框包装器
      const wrapper = document.createElement("span");
      wrapper.className = "input-box-wrapper";
      wrapper.contentEditable = "false";
      wrapper.setAttribute("data-id", `inputbox-${this.inputBoxId++}`);
      wrapper.setAttribute("data-type", "inputbox");

      // 创建输入框
      const input = document.createElement("input");
      input.type = "text";
      input.className = "inline-input";
      input.placeholder = placeholder;
      input.value = value;

      // 自动调整输入框宽度
      input.addEventListener("input", e => {
        const target = e.target;
        const minWidth = 60;
        const maxWidth = 200;
        // 创建临时span测量文本宽度
        const temp = document.createElement("span");
        temp.style.visibility = "hidden";
        temp.style.position = "absolute";
        temp.style.whiteSpace = "pre";
        temp.style.font = window.getComputedStyle(target).font;
        temp.textContent = target.value || target.placeholder;
        document.body.appendChild(temp);
        const width = Math.min(
          Math.max(temp.offsetWidth + 20, minWidth),
          maxWidth
        );
        document.body.removeChild(temp);
        target.style.width = width + "px";
      });

      // 初始化宽度
      setTimeout(() => {
        input.dispatchEvent(new Event("input"));
      }, 0);

      wrapper.appendChild(input);

      this.insertComponent(wrapper, range, selection);

      // 聚焦到输入框
      setTimeout(() => {
        input.focus();
      }, 10);
    },

    // 统一的组件插入方法
    insertComponent(wrapper, range, selection) {
      // 插入组件
      range.deleteContents();
      range.insertNode(wrapper);

      // 在组件后添加零宽空格
      const space = document.createTextNode("\u200B");
      if (wrapper.nextSibling) {
        wrapper.parentNode.insertBefore(space, wrapper.nextSibling);
      } else {
        wrapper.parentNode.appendChild(space);
      }

      // 移动光标到空格后
      range.setStartAfter(space);
      range.collapse(true);
      selection.removeAllRanges();
      selection.addRange(range);

      this.isEmpty = false;
      this.updateDebugContent();
    },

    getValue() {
      const editor = this.$refs.editor;
      let result = "";

      const traverse = node => {
        if (node.nodeType === Node.TEXT_NODE) {
          // 过滤零宽字符
          result += node.textContent.replace(/\u200B/g, "");
        } else if (
          node.classList &&
          node.classList.contains("dropdown-wrapper")
        ) {
          const button = node.querySelector(".dropdown-button");
          if (button) {
            const value = button.getAttribute("data-value");
            result += `[下拉:${value}]`;
          }
        } else if (
          node.classList &&
          node.classList.contains("input-box-wrapper")
        ) {
          const input = node.querySelector("input");
          if (input) {
            result += `[输入:${input.value || input.placeholder}]`;
          }
        } else if (node.childNodes) {
          node.childNodes.forEach(child => traverse(child));
        }
      };

      traverse(editor);

      const finalResult = result.trim();
      console.log("输入框内容:", finalResult);
      alert("输入框内容: " + finalResult);
      return finalResult;
    },

    updateDebugContent() {
      const editor = this.$refs.editor;
      let result = "";

      const traverse = node => {
        if (node.nodeType === Node.TEXT_NODE) {
          result += node.textContent.replace(/\u200B/g, "");
        } else if (
          node.classList &&
          node.classList.contains("dropdown-wrapper")
        ) {
          const button = node.querySelector(".dropdown-button");
          if (button) {
            const value = button.getAttribute("data-value");
            result += `[下拉:${value}]`;
          }
        } else if (
          node.classList &&
          node.classList.contains("input-box-wrapper")
        ) {
          const input = node.querySelector("input");
          if (input) {
            result += `[输入:${input.value || "空"}]`;
          }
        } else if (node.childNodes) {
          node.childNodes.forEach(child => traverse(child));
        }
      };

      traverse(editor);
      this.debugContent = result.trim();
    },

    /**
     * 设置编辑器内容
     * @param {Array} content - 内容数组,每个元素格式:
     *   { type: 'text', value: '文本内容' }
     *   { type: 'input', placeholder: '占位符', value: '初始值' }
     *   { type: 'dropdown', options: [{value: 'val', text: '显示文本'}, ...], selectedValue: 'val' }
     * @param {Object} dropdownOptionsMap - 下拉框选项映射(可选,如果content中已包含options则不需要)
     */
    setContent(content = [], dropdownOptionsMap = {}) {
      const editor = this.$refs.editor;

      // 清空编辑器
      editor.innerHTML = "";

      // 重置ID计数器
      this.dropdownId = 0;
      this.inputBoxId = 0;

      if (!Array.isArray(content) || content.length === 0) {
        this.isEmpty = true;
        this.updateDebugContent();
        return;
      }

      // 遍历内容数组,依次插入
      content.forEach((item, index) => {
        if (item.type === "text") {
          // 插入文本
          const textNode = document.createTextNode(item.value || "");
          editor.appendChild(textNode);
        } else if (item.type === "input") {
          // 插入输入框
          const wrapper = document.createElement("span");
          wrapper.className = "input-box-wrapper";
          wrapper.contentEditable = "false";
          wrapper.setAttribute("data-id", `inputbox-${this.inputBoxId++}`);
          wrapper.setAttribute("data-type", "inputbox");

          const input = document.createElement("input");
          input.type = "text";
          input.className = "inline-input";
          input.placeholder = item.placeholder || "某某购人";
          input.value = item.value || "";

          // 自动调整输入框宽度
          input.addEventListener("input", e => {
            const target = e.target;
            const minWidth = 60;
            const maxWidth = 200;
            const temp = document.createElement("span");
            temp.style.visibility = "hidden";
            temp.style.position = "absolute";
            temp.style.whiteSpace = "pre";
            temp.style.font = window.getComputedStyle(target).font;
            temp.textContent = target.value || target.placeholder;
            document.body.appendChild(temp);
            const width = Math.min(
              Math.max(temp.offsetWidth + 20, minWidth),
              maxWidth
            );
            document.body.removeChild(temp);
            target.style.width = width + "px";
          });

          wrapper.appendChild(input);
          editor.appendChild(wrapper);

          // 初始化宽度
          setTimeout(() => {
            input.dispatchEvent(new Event("input"));
          }, 0);
        } else if (item.type === "dropdown") {
          // 插入下拉框
          const wrapper = document.createElement("span");
          wrapper.className = "dropdown-wrapper";
          wrapper.contentEditable = "false";
          wrapper.setAttribute("data-id", `dropdown-${this.dropdownId++}`);
          wrapper.setAttribute("data-type", "dropdown");

          // 优先使用item中的options,其次使用dropdownOptionsMap,最后使用默认值
          let options = item.options;
          if (
            !options &&
            item.dropdownKey &&
            dropdownOptionsMap[item.dropdownKey]
          ) {
            options = dropdownOptionsMap[item.dropdownKey];
          }
          if (!options) {
            options = [
              { value: "1:1", text: "1:1 正方形,头像" },
              { value: "3:2", text: "3:2 比例" },
              { value: "16:9", text: "16:9 横屏" },
              { value: "9:16", text: "9:16 竖屏" }
            ];
          }

          // 获取要选中的值(优先使用selectedValue,其次使用value)
          const selectedValue = item.selectedValue || item.value;

          // 检查传入的选中值是否存在于选项中
          const hasSelectedValue =
            selectedValue && options.some(opt => opt.value === selectedValue);

          // 找到要选中的选项
          const selectedOption = hasSelectedValue
            ? options.find(opt => opt.value === selectedValue)
            : options[0];

          // 创建下拉按钮
          const button = document.createElement("button");
          button.className = "dropdown-button";
          button.type = "button";
          button.setAttribute("data-value", selectedOption.value);
          button.appendChild(document.createTextNode(selectedOption.text));

          // 添加下拉箭头图标
          const arrow = document.createElement("span");
          arrow.className = "dropdown-arrow";
          arrow.innerHTML = "▼";
          button.appendChild(arrow);

          // 创建下拉菜单
          const menu = document.createElement("div");
          menu.className = "dropdown-menu";

          options.forEach((opt, optIndex) => {
            const menuItem = document.createElement("div");
            menuItem.className = "dropdown-menu-item";
            menuItem.setAttribute("data-value", opt.value);
            menuItem.textContent = opt.text;

            // 设置选中状态
            if (hasSelectedValue && opt.value === selectedValue) {
              menuItem.classList.add("selected");
            } else if (!hasSelectedValue && optIndex === 0) {
              menuItem.classList.add("selected");
            }

            // 点击选项时更新按钮文本和值
            menuItem.addEventListener("click", e => {
              e.stopPropagation();
              const value = menuItem.getAttribute("data-value");
              const text = menuItem.textContent;

              // 更新按钮
              button.setAttribute("data-value", value);
              // 更新文本(保留箭头节点)
              const textNode = Array.from(button.childNodes).find(
                node => node.nodeType === Node.TEXT_NODE
              );
              if (textNode) {
                textNode.textContent = text;
              } else {
                const arrow = button.querySelector(".dropdown-arrow");
                if (arrow) {
                  button.insertBefore(document.createTextNode(text), arrow);
                } else {
                  button.textContent = text;
                  const newArrow = document.createElement("span");
                  newArrow.className = "dropdown-arrow";
                  newArrow.innerHTML = "▼";
                  button.appendChild(newArrow);
                }
              }

              // 更新选中状态
              menu.querySelectorAll(".dropdown-menu-item").forEach(item => {
                item.classList.remove("selected");
              });
              menuItem.classList.add("selected");

              // 关闭菜单
              menu.classList.remove("open");

              // 重置箭头状态
              const wrapper = menu.closest(".dropdown-wrapper");
              if (wrapper) {
                const arrow = wrapper.querySelector(".dropdown-arrow");
                if (arrow) {
                  arrow.style.transform = "rotate(0deg)";
                }
              }

              this.updateDebugContent();
            });

            // hover效果
            menuItem.addEventListener("mouseenter", () => {
              menuItem.classList.add("hover");
            });
            menuItem.addEventListener("mouseleave", () => {
              menuItem.classList.remove("hover");
            });

            menu.appendChild(menuItem);
          });

          wrapper.appendChild(button);
          wrapper.appendChild(menu);
          editor.appendChild(wrapper);
        }

        // 在组件后添加零宽空格(除了最后一个元素)
        if (index < content.length - 1) {
          const space = document.createTextNode("\u200B");
          editor.appendChild(space);
        }
      });

      // 更新状态
      this.isEmpty = false;
      this.updateDebugContent();

      // 将光标放在末尾
      this.$nextTick(() => {
        const range = document.createRange();
        const selection = window.getSelection();
        range.selectNodeContents(editor);
        range.collapse(false);
        selection.removeAllRanges();
        selection.addRange(range);
      });
    }
  },

  mounted() {
    // 组件挂载后保持空状态,等待外部调用setContent方法

    // 关闭所有下拉菜单的方法
    this.closeAllDropdowns = () => {
      const editor = this.$refs.editor;
      if (editor) {
        editor.querySelectorAll(".dropdown-menu.open").forEach(menu => {
          menu.classList.remove("open");
          // 重置箭头状态
          const wrapper = menu.closest(".dropdown-wrapper");
          if (wrapper) {
            const arrow = wrapper.querySelector(".dropdown-arrow");
            if (arrow) {
              arrow.style.transform = "rotate(0deg)";
            }
          }
        });
      }
    };

    // 监听窗口滚动和调整大小,关闭所有下拉菜单
    this.handleWindowEvent = this.closeAllDropdowns;

    // 监听全局点击事件,点击组件外部时关闭下拉菜单
    this.handleGlobalClick = e => {
      const editor = this.$refs.editor;
      if (!editor) return;

      // 检查点击的目标是否在下拉菜单内部
      const clickedDropdown = e.target.closest(".dropdown-menu");
      const clickedButton = e.target.closest(".dropdown-button");
      const clickedWrapper = e.target.closest(".dropdown-wrapper");

      // 如果点击的是下拉菜单、按钮或包装器内部,不关闭
      if (clickedDropdown || clickedButton || clickedWrapper) {
        return;
      }

      // 检查是否有打开的下拉菜单
      const openMenus = editor.querySelectorAll(".dropdown-menu.open");
      if (openMenus.length === 0) {
        return;
      }

      // 如果点击的是编辑器外部,或者点击的不是下拉菜单相关元素,关闭所有下拉菜单
      if (!editor.contains(e.target)) {
        this.closeAllDropdowns();
      }
    };

    window.addEventListener("scroll", this.handleWindowEvent, true);
    window.addEventListener("resize", this.handleWindowEvent);
    document.addEventListener("click", this.handleGlobalClick, true);
  },

  beforeDestroy() {
    // 清理事件监听器
    if (this.handleWindowEvent) {
      window.removeEventListener("scroll", this.handleWindowEvent, true);
      window.removeEventListener("resize", this.handleWindowEvent);
    }
    if (this.handleGlobalClick) {
      document.removeEventListener("click", this.handleGlobalClick, true);
    }
  }
};
</script>

<style scoped>
.toolbar {
  display: flex;
  gap: 8px;
  padding: 12px;
  background: #fafafa;
  border-bottom: 1px solid #e8e8e8;
  border-radius: 8px 8px 0 0;
}

.toolbar-btn {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  background: white;
  cursor: pointer;
  font-size: 13px;
  color: #333;
  transition: all 0.2s;
}

.toolbar-btn:hover {
  border-color: #1890ff;
  color: #1890ff;
  background: #f0f5ff;
}

.toolbar-btn:active {
  transform: translateY(1px);
}

.rich-input-editor {
  height: 110px;
  padding: 0 20px;
  font-size: 14px;
  line-height: 26px;
  outline: none;
  position: relative;
  word-wrap: break-word;
  white-space: pre-wrap;
  overflow-y: auto;
  overflow-x: hidden;
  box-sizing: border-box;
}

.placeholder {
  color: #bbb;
  position: absolute;
  pointer-events: none;
}

/* 使用 ::v-deep 来修改动态创建的元素样式 */
/* 下拉框样式 */
::v-deep .dropdown-wrapper {
  display: inline-flex;
  align-items: center;
  margin: 0 2px;
  vertical-align: middle;
  user-select: none;
  position: relative;
  height: 26px;
  box-sizing: border-box;
}

::v-deep .dropdown-button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0 12px;
  margin: 0;
  border: none;
  background: #d8e7ff;
  border-radius: 6px;
  font-size: 14px;
  line-height: 26px;
  height: 26px;
  color: #0062ff;
  font-weight: 500;
  cursor: pointer;
  transition: all 0.2s;
  outline: none;
  white-space: nowrap;
  vertical-align: middle;
  box-sizing: border-box;
  font-family: inherit;
}

::v-deep .dropdown-button:hover {
  background: #c5d9ff;
}

::v-deep .dropdown-arrow {
  font-size: 10px;
  color: #0062ff;
  transition: transform 0.2s;
}

/* 气泡式下拉菜单 */
::v-deep .dropdown-menu {
  position: fixed;
  min-width: 118px;
  box-sizing: border-box;
  padding: 8px;
  border-radius: 12px;
  background-color: #fff;
  border: 1px solid rgba(201, 201, 201, 1);
  box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.25);
  z-index: 9999;
  display: none;
  overflow: hidden;
}

::v-deep .dropdown-menu.open {
  display: block;
}

::v-deep .dropdown-menu-item {
  height: 38px;
  font-size: 14px;
  color: #000;
  display: flex;
  border-radius: 8px;
  cursor: pointer;
  align-items: center;
  box-sizing: border-box;
  padding: 0 12px;
  transition: background-color 0.15s, color 0.15s;
  white-space: nowrap;
}

::v-deep .dropdown-menu-item:hover,
::v-deep .dropdown-menu-item.hover {
  background-color: rgb(235, 243, 255);
  color: rgb(2, 109, 252);
}

/* 输入框样式 */
::v-deep .input-box-wrapper {
  display: inline-flex;
  align-items: center;
  margin: 0 2px;
  vertical-align: middle;
  user-select: none;
  background: #e0ebfb;
  border-radius: 4px;
  padding: 0;
  transition: all 0.2s;
  cursor: text;
  height: 26px;
  box-sizing: border-box;
  line-height: 26px;
}

::v-deep .input-box-wrapper:hover {
  background: #e0ebfb;
  border-color: #40a9ff;
}

::v-deep .input-box-wrapper:focus-within {
  background: #e0ebfb;
  border-color: #1890ff;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.1);
}

::v-deep .inline-input {
  border: none;
  background: transparent;
  font-size: 14px;
  line-height: 26px;
  padding: 0 8px;
  margin: 0;
  outline: none;
  color: #0062ff;
  font-weight: 400;
  width: 80px;
  min-width: 60px;
  max-width: 200px;
  height: 26px;
  vertical-align: middle;
  box-sizing: border-box;
  font-family: inherit;
}

::v-deep .inline-input::placeholder {
  color: #7492c2;
}

.debug-info {
  padding: 12px 16px;
  font-size: 12px;
  color: #666;
  background: #f5f5f5;
  border-top: 1px solid #e8e8e8;
  border-radius: 0 0 8px 8px;
  font-family: "Courier New", monospace;
}

.debug-info div {
  padding: 4px 0;
}

.rich-input-editor::-webkit-scrollbar {
  width: 6px;
}

.rich-input-editor::-webkit-scrollbar-thumb {
  background: #d9d9d9;
  border-radius: 3px;
}

.rich-input-editor::-webkit-scrollbar-thumb:hover {
  background: #bfbfbf;
}
</style>

调用方法

<template>
 <step-input ref="stepInputRef" />
 </template>
 <script>
 //调用方法
        this.$refs.stepInputRef.setContent([
          {
            type: "dropdown",
            options: [
              { value: "添加订阅", text: "添加订阅" },
              { value: "取消订阅", text: "取消订阅" }
            ],
            selectedValue:"添加订阅"
          },
          { type: "input", placeholder: "地区", value: "" },
          { type: "input", placeholder: "关键词", value: "" },
          { type: "text", value: "有关的标讯" }
        ]);
</script>

type:dropdown下拉 input输入框 text普通文本
options下拉菜单的选项
selectedValue默认选中
placeholder 输入框提示

效果如图
在这里插入图片描述

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐