Right.vue 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. <template>
  2. <el-card class="my-card h-full">
  3. <template #header
  4. ><h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
  5. <span>预览</span>
  6. <!-- 展示在右上角 -->
  7. <el-button color="#846af7" v-show="showCopy" @click="copyContent" size="small">
  8. <template #icon>
  9. <Icon icon="ph:copy-bold" />
  10. </template>
  11. 复制
  12. </el-button>
  13. </h3></template
  14. >
  15. <div ref="contentRef" class="hide-scroll-bar h-full box-border overflow-y-auto">
  16. <div class="w-full min-h-full relative flex-grow bg-white box-border p-3 sm:p-7">
  17. <!-- 终止生成内容的按钮 -->
  18. <el-button
  19. v-show="isWriting"
  20. class="absolute bottom-2 sm:bottom-5 left-1/2 -translate-x-1/2 z-36"
  21. @click="emits('stopStream')"
  22. size="small"
  23. >
  24. <template #icon>
  25. <Icon icon="material-symbols:stop" />
  26. </template>
  27. 终止生成
  28. </el-button>
  29. <el-input
  30. id="inputId"
  31. type="textarea"
  32. v-model="compContent"
  33. autosize
  34. :input-style="{ boxShadow: 'none' }"
  35. resize="none"
  36. placeholder="生成的内容……"
  37. />
  38. </div>
  39. </div>
  40. </el-card>
  41. </template>
  42. <script setup lang="ts">
  43. import { useClipboard } from '@vueuse/core'
  44. const message = useMessage() // 消息弹窗
  45. const { copied, copy } = useClipboard() // 粘贴板
  46. const props = defineProps({
  47. content: {
  48. // 生成的结果
  49. type: String,
  50. default: ''
  51. },
  52. isWriting: {
  53. // 是否正在生成文章
  54. type: Boolean,
  55. default: false
  56. }
  57. })
  58. const emits = defineEmits(['update:content', 'stopStream'])
  59. /** 通过计算属性,双向绑定,更改生成的内容,考虑到用户想要更改生成文章的情况 */
  60. const compContent = computed({
  61. get() {
  62. return props.content
  63. },
  64. set(val) {
  65. emits('update:content', val)
  66. }
  67. })
  68. /** 滚动 */
  69. const contentRef = ref<HTMLDivElement>()
  70. defineExpose({
  71. scrollToBottom() {
  72. contentRef.value?.scrollTo(0, contentRef.value?.scrollHeight)
  73. }
  74. })
  75. /** 点击复制的时候复制内容 */
  76. const showCopy = computed(() => props.content && !props.isWriting) // 是否展示复制按钮,在生成内容完成的时候展示
  77. const copyContent = () => {
  78. copy(props.content)
  79. }
  80. /** 复制成功的时候 copied.value 为 true */
  81. watch(copied, (val) => {
  82. if (val) {
  83. message.success('复制成功')
  84. }
  85. })
  86. </script>
  87. <style lang="scss" scoped>
  88. .hide-scroll-bar {
  89. -ms-overflow-style: none;
  90. scrollbar-width: none;
  91. &::-webkit-scrollbar {
  92. width: 0;
  93. height: 0;
  94. }
  95. }
  96. .my-card{
  97. display: flex;
  98. flex-direction: column;
  99. :deep(.el-card__body) {
  100. box-sizing: border-box;
  101. flex-grow: 1;
  102. overflow-y: auto;
  103. padding: 0;
  104. @extend .hide-scroll-bar;
  105. }
  106. }
  107. </style>