惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
S
Security Affairs
阮一峰的网络日志
阮一峰的网络日志
L
LangChain Blog
Microsoft Azure Blog
Microsoft Azure Blog
雷峰网
雷峰网
Recent Announcements
Recent Announcements
WordPress大学
WordPress大学
The GitHub Blog
The GitHub Blog
博客园_首页
The Cloudflare Blog
M
MIT News - Artificial intelligence
博客园 - 【当耐特】
MyScale Blog
MyScale Blog
S
SegmentFault 最新的问题
P
Proofpoint News Feed
Y
Y Combinator Blog
Jina AI
Jina AI
博客园 - 聂微东
A
About on SuperTechFans
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
G
Google Developers Blog
云风的 BLOG
云风的 BLOG
F
Full Disclosure
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Microsoft Security Blog
Microsoft Security Blog
爱范儿
爱范儿
T
Tailwind CSS Blog
J
Java Code Geeks
Vercel News
Vercel News
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Stack Overflow Blog
Stack Overflow Blog
罗磊的独立博客
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
T
The Blog of Author Tim Ferriss
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
博客园 - 三生石上(FineUI控件)
W
WeLiveSecurity
PCI Perspectives
PCI Perspectives
Attack and Defense Labs
Attack and Defense Labs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
宝玉的分享
宝玉的分享
IT之家
IT之家
Hacker News: Ask HN
Hacker News: Ask HN
The Register - Security
The Register - Security
T
The Exploit Database - CXSecurity.com
T
Threat Research - Cisco Blogs

Vue.js

AI 特效 shadcn-vue-admin 已升级至 vite8 - V2EX 我真的要放弃 Vue,改用 React 了 - V2EX 请问 vue3 的 onclick 和 @click 有什么区别 - V2EX 求推荐美观、功能全的 vue AI chat 组件 - V2EX [求助贴] 有没有推荐的 vue3 页面设计器 大屏设计器(源码可以拿来二开的 开源 or 收费) - V2EX 有没有大佬在正式项目里使用 rolldown 了 - V2EX 用 reactive 來實現 oo 對象的封裝 - V2EX Vue 的 reactive 会有人专门用吗? - V2EX 大佬们,想在 vue 中集成一个 excel,有什么方案? - V2EX vue3 无法手动控制 keepalive 的缓存| fork 了独立的 keep-alive 组件|支持手动删除缓存或者添加缓存 - V2EX WebStorm 写 个 v-for, cpu 就狂飙 - V2EX Nuxt UI 4 可以免费用 Pro 了 - V2EX Vue2 除了 ele-table-editor,还有没有其他“子表单(inline-formset)”的组件? - V2EX 请问 vue 的 defineEmits 类型如何使用动态键名 - V2EX VUE 开发求助 - V2EX 屎代码靠我自己骂已经不解气了,佬们来看个笑话 - V2EX 我的 mp4 播放不出来! - V2EX 有一个 8K 行的 VUE 代码文件用什么 AI 可以解耦 - V2EX 求助 Vue 大佬,为什么使用 reactive 数据没有双向绑定? - V2EX uniapp 求助 - V2EX Vue3 编写的最佳实践是怎样的? - V2EX 开发一款轻便简洁的论坛程序 - V2EX VueConf 2025 所有演讲嘉宾及日程已确定, 7 月 12 日,深圳见! - V2EX vue3+ELInput 无法输入的问题 - V2EX 想问一下 pinia 持久化到 localStorage 如何实现有效期 - V2EX 有什么适用于 Vue/Nuxt 的表单/问卷/试卷+设计器库么 - V2EX vue_cli 用到 env 环境变量值特殊字符处理 - V2EX 统计基于 vue 的汽水音乐电脑版用了哪些开源组件 - V2EX ant-design-vue 好像不怎么维护了, vue 有什么比较靠谱的 ui 组件库吗 - V2EX 关于二次封装 element-plus 组件库主题问题 - V2EX 🎉 VueConf 2025 来啦, 7 月 12 日,深圳见! - V2EX vue 怎么 5 个月不更新了 - V2EX 前端 props 单向数据流 vs 状态管理流 你更能接受哪种代码 - V2EX WSL2 + System Proxy 后, vite 启动的项目,随机一部分网络请求变得巨慢,求指导。 - V2EX vue vite 打包 白屏问题 - V2EX 外行搞前端构建环境真的很头疼,有谁能帮忙搞定这个 Nuxt3 开源项目的构建?有偿。 - V2EX vue 指令更新问题 - V2EX 为什么 vue 的 nuxt.js 不跟进 nextjs 的 app route 目录结构 - V2EX shadcn, shadcnvue 全是基于 tailwindcss 的,没有基于 unocss 的嘛? - V2EX vue3 的 template typescript 支持稀烂,退坑 - V2EX 组件持久化的问题请教 - V2EX Vue3 tsx 应该用 props 还是 emit 传递事件? - V2EX 话说我怎么就懵逼了呢? Vue3 的泛型组件 generic="T"? - V2EX 请教 Vue3 + typescript 最佳实践问题 - V2EX vue3 纯前端如何生成 icns 格式图片? - V2EX 你们写 vue 一定是按照 vue 的脚手架的目录结构来写吗? - V2EX 大佬们, vue3 新人求教,就没有办法对 ref 包装的 obj 进行解构吗 - V2EX vue3 中 v-show 分页问题 - V2EX el-table 使用 expand 展开倒数第二行时,最下面一行会错乱,我把第一列的数据进行了合并,求问大家怎么解决呀?
使用 Ant design X vue 组件库怎么解决无法正确展示 MarkDown 格式字符串的问题? - V2EX
tiRolin · 2025-03-17 · via Vue.js

是这样的,我毕设搞了一个 AI 对话,但是呢,这个 AI 对话返回的字符串是 markdown 格式,但是我前端无法正确解析,最后字符串格式就会变成下面这样

这里显然没有正确解析出样式来,我试了很多方法都没能解决这个问题,各个 ai 问了个遍了,然后去网上找了好多方法,还是没搞定,下面是我这个页面的源码,有没有大佬帮我看看到底该怎么解决这个问题啊?小弟我感激不尽啊

<template>
  <div class="layout">
    <div class="menu">
      <RouterLink to="/" class="re-home">返回首页</RouterLink>
      <!-- Logo -->
      <div class="logo">
        <img :src="logo" draggable="false" alt="logo" class="logo-img" />
        <span class="logo-span">柑橘智能问答</span>
      </div>
    </div>
    <div class="chat">
      <!-- 消息列表 -->
      <Bubble.List
        :items="messages"
        class="messages"
        style="flex: 1"
        :messageRender="renderMarkdown"
        :roles="{
          ai: {
            placement: 'start',
            typing: { step: 5, interval: 20 },
            styles: {
              content: {
                borderRadius: '16px'
              }
            }
          },
          local: {
            placement: 'end',
            variant: 'shadow'
          }
        }"
      />

      <!-- 输入框 -->
      <Sender
        :value="content"
        class="sender"
        @submit="onSubmit"
        @update:value="(val) => (content = val)"
      />
    </div>
  </div>
</template>

<script setup>
import { ref, h } from 'vue'
import { Conversations, Prompts, Sender, Bubble } from 'ant-design-x-vue'
import { Edit, Delete } from '@element-plus/icons-vue'
import reqAIChat from '@/api/chat/index'
import { message } from 'ant-design-vue'
import { ElMessage } from 'element-plus'
import logo from '@/assets/images/logo.png'
import { marked } from 'marked'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
marked.setOptions({
  highlight: (code) => hljs.highlightAuto(code).value,
  breaks: true,
  gfm: true
})

marked.use({
  renderer: {
    link(href, title, text) {
      return `<a href="${href}" target="_blank" rel="noopener">${text}</a>`
    }
  }
})
const renderMarkdown = (content, item) => {
  if (item?.isMarkdown) {
    return h('Typography', { class: 'markdown-container' }, [
      h('div', {
        innerHTML: marked.parse(content), // Vue 3 直接注入 HTML
        class: 'markdown-body'
      })
    ])
  }
  return content // 普通文本直接返回
}
const content = ref('')

const messages = ref([
  {
    key: '1',
    content:
      '## Markdown 测试\n[链接]( https://x.ant.design)1. **橙( Orange )**:包括甜橙和酸橙,甜橙中又分为脐橙、瓦伦西亚橙等。2. **柑橘( Mandarin )**:也称为蜜橘,包括各种小型、易剥皮的柑橘。3. **柚子( Pomelo )**:也称为文旦,是柑橘类中最大的一种。4. **柠檬( Lemon )**:以其酸味和香气闻名。5. **青柠( Lime )**:比柠檬小,酸味较轻。6. **葡萄柚( Grapefruit )**:大小和形状类似葡萄,味道可以是甜的也可以是酸的。',
    role: 'ai',
    variant: 'primary',
    isMarkdown: true
  }
])
const activeKey = ref('0')
const conversationsItems = ref(
  Array.from({ length: 2 }).map((_, index) => ({
    key: `item${index + 1}`,
    label: `未命名对话 ${index + 1}`
  }))
)
const onSubmit = async (nextContent) => {
  if (!nextContent) return
  messages.value.push({
    key: `${messages.value.length + 1}`,
    content: nextContent,
    role: 'local',
    variant: 'shadow'
  })
  content.value = ''
  try {
    const res = await reqAIChat({ message: nextContent })
    if (!res) {
      ElMessage({
        message: '请求失败',
        type: 'error'
      })
      return
    }

    messages.value.push({
      key: `${messages.value.length + 1}`,
      content: res,
      role: 'ai',
      variant: 'primary',
      isMarkdown: true
    })
  } catch (error) {
    ElMessage({
      message: '请求出错',
      type: 'error'
    })
  }
}
const onAddConversation = () => {
  conversationsItems.value.push({
    key: `${conversationsItems.value.length + 1}`,
    label: `未命名对话${conversationsItems.value.length + 1}`
  })
  activeKey.value = `${conversationsItems.value.length + 1}`
}

const menuConfig = (conversation) => {
  return {
    items: [
      {
        label: '编辑',
        key: 'edit',
        icon: () => h(Edit)
      },
      {
        label: '删除',
        key: 'delete',
        icon: () => h(Delete),
        danger: true
      }
    ],
    onClick: (menuInfo) => {
      message.info(`点击 ${conversation.key} - ${menuInfo.key}`)
    }
  }
}
</script>

<style scoped>
.markdown-body {
  padding: 12px 16px;
  line-height: 1.7;

  /* 必须继承字体 */
  font-family: inherit;

  /* 代码块样式 */
  pre {
    padding: 12px;
    border-radius: 8px;
    background: #f6f8fa !important;
  }

  code {
    font-family: 'SFMono-Regular', Consolas, monospace;
  }
}
.markdown-body {
  line-height: 1.6;
  color: #333;
}

.re-home {
  display: inline-block;
  padding: 10px 20px;
  margin: 12px;
  background: #ffa500;
  color: white;
  text-decoration: none;
  border-radius: 4px;
  font-size: 14px;
  font-weight: bold;
  text-align: center;
  transition: background-color 0.3s ease;
}
.layout {
  width: 100%;
  min-width: 1000px;
  height: 722px;
  display: flex;
  background: #fff;
  font-family: AlibabaPuHuiTi, sans-serif;
}

.menu {
  padding: 24px 0;
  /* background: #fff; */
  width: 280px;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: rgba(245, 245, 245, 0.5);
}

.logo {
  display: flex;
  height: 72px;
  align-items: center;
  justify-content: start;
  padding: 0 24px;
}

.logo-img {
  width: 24px;
  height: 24px;
}

.logo-span {
  margin: 0 8px;
  font-weight: bold;
  color: #333;
  font-size: 16px;
}

.add-btn {
  background: #1677ff0f;
  border: 1px solid #1677ff34;
  width: calc(100% - 24px);
  cursor: pointer;
  margin: 0 12px 24px 12px;
  font-size: 14px;
  height: 32px;
  padding: 4px 15px;
  border-radius: 6px;
}
.add-btn:hover {
  color: #69b1ff;
}

.chat {
  height: 100%;
  width: 100%;
  max-width: 700px;
  margin: 0 auto;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  padding: 24px;
  gap: 16px;
}
</style>

这里 content 里已经有一些写死的内容了,那是我用来测试的,就是想着如果可以解析了那么这些写死的内容是会被正确解析上去的,但是我看了好久都没搞懂该怎么解决这个问题,所以这个写死的内容也一直留着了