基于react-markdown实现对大模型输出展示——2:自定义标签及事件触发

背景

上一篇文章 我们基于 react-markdown 并引用 remark-gfm rehype-raw github-markdown-css来处理复杂md格式,处理html标签 和美化样式,本篇章我们来实现自定义标签和事件触发

场景

举例:例如后端跟我们约定,他们流式返回数据,我们接到后拼装展示,但是有一些特殊标识,比如说引用,类似于参考文献,一般是右上角上标,点击会跳转详情或者其他操作

这里我们就需要,自定义标签,并需要触发事件,暂定 我们约定 后端返回的数据特殊格式如下, 第一个是引用的顺序,第二个是引用的id

js 复制代码
[[1,'引用1的id']]

我们需要判定此格式,转为 sup标签,并自定义样式,点击的时候,需要将id传过去,触发事件

实现方案

1:原始效果及展示

以上规则,demo代码及展示如下

js 复制代码
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import "github-markdown-css"

const richMarkdownContent = `
# 一级标题:Markdown 丰富示例 [[1,'我是1的id']]

## 二级标题:Markdown 丰富示例 [[1,'我是1的id']]

### 三级标题:Markdown 丰富示例 [[1,'我是1的id']]
`;

const App = () => {
  return (
    <div className="markdown-body">
      <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
        {richMarkdownContent}
      </ReactMarkdown>
    </div>
  );
};

export default App;

效果:

如果我们不处理,则按照字符串展示

现在我们按需截取特殊约定字符

2: 正则截取

正则截取,我们将顺序回显,id作为自定义属性添加到元素上,借助rehype-raw渲染出来自定义的标签,注意自定义属性data-supid

js 复制代码
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import "github-markdown-css"

const richMarkdownContent = `
# 一级标题:Markdown 丰富示例 [[1,'我是1的id']]

## 二级标题:Markdown 丰富示例 [[1,'我是1的id']]

### 三级标题:Markdown 丰富示例 [[1,'我是1的id']]
`;

const replaceReferences = (str)=> {
  // 定义正则表达式
  const regex = /\[\[(\d+),'(.*?)'\]\]/g;

  // 使用 replace 方法进行全局替换
  return str.replace(regex, (match, num, id) => {
      return `<sup className="text-active cursor-pointer" data-supid="${id}">[${num}]</sup>`;
  });
}

const regStr = replaceReferences(richMarkdownContent)

const App = () => {
  return (
    <div className="markdown-body">
      <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]}>
        {regStr}
      </ReactMarkdown>
    </div>
  );
};

export default App;

此时效果如下,

3:美化样式

对于创建的元素,可以使用tailwindcss 修改样式,或者其他自定义样式均可,和普通html样式样式无异

js 复制代码
const replaceReferences = (str)=> {
  // 定义正则表达式
  const regex = /\[\[(\d+),'(.*?)'\]\]/g;

  // 使用 replace 方法进行全局替换
  return str.replace(regex, (match, num, id) => {
      return `<sup className="text-blue-600 cursor-pointer" data-supid="${id}">[${num}]</sup>`;
  });
}

注意text-blue-600颜色设置,现在效果展示如下

4:自定义事件触发

借助react-markdowncomponents来自定义标签

js 复制代码
import React from 'react';
import ReactMarkdown from 'react-markdown';
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import "github-markdown-css"

const richMarkdownContent = `
# 一级标题:Markdown 丰富示例 [[1,'我是1的id']]

## 二级标题:Markdown 丰富示例 [[2,'我是2的id']]

### 三级标题:Markdown 丰富示例 [[3,'我是3的id']]
`;

const replaceReferences = (str) => {
  // 定义正则表达式
  const regex = /\[\[(\d+),'(.*?)'\]\]/g;

  // 使用 replace 方法进行全局替换
  return str.replace(regex, (match, num, id) => {
    return `<sup className="text-blue-600 cursor-pointer" data-supid="${id}">[${num}]</sup>`;
  });
}

const regStr = replaceReferences(richMarkdownContent)

// 自定义渲染器
const components = {
  sup: ({ children, ...rest }) => {
    return (
      <sup className="text-active" onClick={(event) => handleSupClick(event)} {...rest}>
        {children}
      </sup>
    );
  },
};

// 点击事件处理函数
const handleSupClick = (event) => {
  const supid = event.target.dataset.supid;
  console.log("Clicked sup data-supid:", supid);
  // 你可以在这里进行其他操作,比如将内容传递给父组件等
};

const App = () => {
  return (
    <div className="markdown-body">
      <ReactMarkdown remarkPlugins={[remarkGfm]} rehypePlugins={[rehypeRaw]} components={components}>
        {regStr}
      </ReactMarkdown>
    </div>
  );
};

export default App;
  1. components 对象中的 sup 组件

    • onClick 事件处理函数中,将事件对象 event 传递给 handleSupClick 函数。
    • 使用扩展运算符 ...rest 传递其他属性,确保 data-supid 属性被正确传递。
  2. handleSupClick 函数

    • 通过 event.target.dataset.supid 获取自定义属性 data-supid 的值。

    • 打印获取到的值,你可以在这里进行其他操作,比如将内容传递给父组件等。

这样,当你点击 sup 元素时,就可以获取到自定义属性 data-supid 的值了。

此时的展示及展示效果如下

总结

这篇文章 我们基于自定义的sup标签,借助自定义components,结合正则来实现了文章前期的需求,可以发现功能很强大,对于多数其他标签有更特殊的需求,可以自己来实现,一家之言,有需要的按需取舍,你也可以下载项目项目地址 dev2分支

未完待续。。。

相关推荐
木亦Sam12 分钟前
前端安全之 CSRF 攻击的防御策略
前端
光影少年21 分钟前
es6+新增特性有哪些
前端·javascript·es6
木亦Sam22 分钟前
前端代码优化之函数节流与防抖技巧
前端
GDAL35 分钟前
Better-SQLite3 参数绑定详解
javascript·sqlite3
diang36 分钟前
DeepSeek在前端的使用场景及使用
前端·deepseek
Georgewu39 分钟前
【HarmonyOS Next】鸿蒙应用弹框和提示气泡详解(一)
前端·华为·harmonyos
双口馋猫41 分钟前
cesium+vite demo
前端·vue.js
雾岛听风来42 分钟前
Cython与CUDA之Add
前端·cython
猿榜42 分钟前
js逆向-某博博返回数据解密
javascript·python
IT、木易1 小时前
大白话解释 JavaScript 中的this关键字,它在不同场景下是如何取值的?
开发语言·javascript·ecmascript