ChatAnswer.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  1. <script setup>
  2. import { computed, ref } from 'vue';
  3. import MarkdownIt from 'markdown-it';
  4. import hljs from 'highlight.js';
  5. import mila from 'markdown-it-link-attributes';
  6. import mdKatex from '@traptitech/markdown-it-katex';
  7. import { SvgIcon } from '@/components';
  8. const props = defineProps({
  9. content: {
  10. type: String,
  11. default: ''
  12. },
  13. loading: {
  14. type: Boolean,
  15. default: false
  16. }
  17. })
  18. const highlightBlock = (str, lang) => {
  19. return `<pre class="code-block-wrapper"><div class="code-block-header"><span class="code-block-header__lang">${lang}</span><span class="code-block-header__copy">${t('chat.copyCode')}</span></div><code class="hljs code-block-body ${lang}">${str}</code></pre>`
  20. }
  21. const mdi = new MarkdownIt({
  22. html: true,
  23. linkify: true, // 将类似 URL 的文本自动转换为链接。
  24. // breaks: true,
  25. typographer: true,
  26. highlight(code, language) {
  27. const validLang = !!(language && hljs.getLanguage(language))
  28. if (validLang) {
  29. const lang = language ?? ''
  30. return highlightBlock(hljs.highlight(code, { language: lang }).value, lang)
  31. }
  32. return highlightBlock(hljs.highlightAuto(code).value, '')
  33. },
  34. })
  35. mdi.use(mila, { attrs: { target: '_blank', rel: 'noopener' } })
  36. mdi.use(mdKatex, { blockClass: 'katexmath-block rounded-md p-[10px]', errorColor: ' #cc0000' })
  37. const text = computed(() => {
  38. const value = props.content ?? ""
  39. if (!props.asRawText)
  40. return mdi.render(value)
  41. return value
  42. })
  43. </script>
  44. <template>
  45. <div class="answer-inner">
  46. <div class="answer-card">
  47. <div class="chat-answer_icon relative">
  48. <SvgIcon name="common-logo" class="chat-logo" size="30" :style="{ scale: loading ? 0 : 1 }"/>
  49. <div style="color: #2454FF" class="la-ball-circus la-dark la-sm" v-show="loading">
  50. <div v-for="item in 5" :key="item"></div>
  51. </div>
  52. </div>
  53. <div class="flex-1 pt-[4px] ml-[16px] text-[15px] leading-[24px]">
  54. <p class="font-bold text-[#1A2029]" v-if="loading">内容生成中...</p>
  55. <p class="flex-1 pt-[6px] ml-[16px] text-[15px] leading-[24px]" v-html="text" v-else></p>
  56. </div>
  57. </div>
  58. <ul class="answer-btn-group">
  59. <li class="btn">
  60. <SvgIcon name="chat-icon-copy" size="16" />
  61. </li>
  62. <li class="line"></li>
  63. <li class="btn">
  64. <SvgIcon name="chat-icon-yes" size="16" />
  65. </li>
  66. <li class="line"></li>
  67. <li class="btn">
  68. <SvgIcon name="chat-icon-no" size="16" />
  69. </li>
  70. </ul>
  71. </div>
  72. </template>
  73. <style lang="scss">
  74. .chat-logo {
  75. position: absolute;
  76. transition: all 1s;
  77. }
  78. .answer-inner {
  79. margin-bottom: 20px;
  80. .answer-card {
  81. @include flex(x, start, start);
  82. padding: 20px;
  83. border-radius: 8px;
  84. background: #fff;
  85. }
  86. .answer-btn-group {
  87. @include flex(x, center, end);
  88. padding-top: 6px;
  89. .btn {
  90. @include flex(x, center, center);
  91. @include layout(28px, 28px, 4px);
  92. color: #89909B;
  93. cursor: pointer;
  94. &:hover {
  95. background: #DBEFFF;
  96. color: #2454FF;
  97. }
  98. }
  99. .line {
  100. @include layout(1px, 12px, 0);
  101. margin: 0 5px;
  102. background: #D3D0E1;
  103. }
  104. }
  105. }
  106. </style>