【代码评审】AI:code review 思维导图的逻辑

pull/491/MERGE
YunaiV 2024-07-29 22:07:31 +08:00
parent a805ceb534
commit 108dcccafe
7 changed files with 816 additions and 61 deletions

View File

@ -29,6 +29,9 @@ const include = [
'@wangeditor/editor-for-vue', '@wangeditor/editor-for-vue',
'@microsoft/fetch-event-source', '@microsoft/fetch-event-source',
'markdown-it', 'markdown-it',
'markmap-view',
'markmap-lib',
'markmap-toolbar',
'highlight.js', 'highlight.js',
'element-plus', 'element-plus',
'element-plus/es', 'element-plus/es',

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,6 @@ import { config } from '@/config/axios/config'
export interface AiMindMapGenerateReqVO { export interface AiMindMapGenerateReqVO {
prompt: string prompt: string
} }
//
export const AiMindMapApi = { export const AiMindMapApi = {
generateMindMap: ({ generateMindMap: ({

View File

@ -20,13 +20,14 @@
type="primary" type="primary"
:loading="isGenerating" :loading="isGenerating"
@click="emits('submit', formData)" @click="emits('submit', formData)"
>智能生成思维导图</el-button
> >
智能生成思维导图
</el-button>
</div> </div>
<div class="mt-[30px]"> <div class="mt-[30px]">
<el-text tag="b">使用已有内容生成</el-text> <el-text tag="b">使用已有内容生成</el-text>
<el-input <el-input
v-model="existPrompt" v-model="generatedContent"
maxlength="1024" maxlength="1024"
rows="5" rows="5"
class="w-100% mt-15px" class="w-100% mt-15px"
@ -38,17 +39,18 @@
<el-button <el-button
class="!w-full mt-[15px]" class="!w-full mt-[15px]"
type="primary" type="primary"
@click="emits('directGenerate', existPrompt)" @click="emits('directGenerate', generatedContent)"
:disabled="isGenerating" :disabled="isGenerating"
>直接生成</el-button
> >
直接生成
</el-button>
</div> </div>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { MindmapExitExample } from '@/views/ai/utils/constants' import { MindMapContentExample } from '@/views/ai/utils/constants'
const emits = defineEmits(['submit', 'directGenerate']) const emits = defineEmits(['submit', 'directGenerate'])
defineProps<{ defineProps<{
@ -59,11 +61,12 @@ const formData = reactive({
prompt: '' prompt: ''
}) })
const existPrompt = ref(MindmapExitExample) // const generatedContent = ref(MindMapContentExample) //
defineExpose({ defineExpose({
setExistPrompt(e: string){ // setGeneratedContent(newContent: string) {
existPrompt.value = e //
generatedContent.value = newContent
} }
}) })
</script> </script>

View File

@ -1,7 +1,7 @@
<template> <template>
<el-card class="my-card h-full flex-grow"> <el-card class="my-card h-full flex-grow">
<template #header <template #header>
><h3 class="m-0 px-7 shrink-0 flex items-center justify-between"> <h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
<span>思维导图预览</span> <span>思维导图预览</span>
<!-- 展示在右上角 --> <!-- 展示在右上角 -->
<el-button type="primary" v-show="isEnd" @click="downloadImage" size="small"> <el-button type="primary" v-show="isEnd" @click="downloadImage" size="small">
@ -10,13 +10,13 @@
</template> </template>
下载图片 下载图片
</el-button> </el-button>
</h3></template </h3>
> </template>
<div ref="contentRef" class="hide-scroll-bar h-full box-border"> <div ref="contentRef" class="hide-scroll-bar h-full box-border">
<!--展示markdown的容器最终生成的是html字符串直接用v-html嵌入--> <!--展示 markdown 的容器最终生成的是 html 字符串直接用 v-html 嵌入-->
<div v-if="isGenerating" ref="mdContainerRef" class="wh-full overflow-y-auto"> <div v-if="isGenerating" ref="mdContainerRef" class="wh-full overflow-y-auto">
<div class="flex flex-col items-center justify-center" v-html="html"></div> <div class="flex flex-col items-center justify-center" v-html="html"></div>
</div> </div>
<div ref="mindmapRef" class="wh-full"> <div ref="mindmapRef" class="wh-full">
@ -34,24 +34,25 @@ import { Toolbar } from 'markmap-toolbar'
import markdownit from 'markdown-it' import markdownit from 'markdown-it'
const md = markdownit() const md = markdownit()
const message = useMessage() //
// TODO @hheromindmap mindMap
const props = defineProps<{ const props = defineProps<{
mindmapResult: string // mindmapResult: string // TODO @hhero generatedContent
isEnd: boolean // isEnd: boolean //
isGenerating: boolean // isGenerating: boolean //
isStart: boolean // html isStart: boolean // html
}>() }>()
const contentRef = ref<HTMLDivElement>() // header const contentRef = ref<HTMLDivElement>() // header
const mdContainerRef = ref<HTMLDivElement>() // markdown const mdContainerRef = ref<HTMLDivElement>() // markdown
const mindmapRef = ref<HTMLDivElement>() // const mindmapRef = ref<HTMLDivElement>() //
const svgRef = ref<SVGElement>() // svg const svgRef = ref<SVGElement>() // svg
const toolBarRef = ref<HTMLDivElement>() // const toolBarRef = ref<HTMLDivElement>() //
const html = ref('') // const html = ref('') //
const contentAreaHeight = ref(0) // header const contentAreaHeight = ref(0) // header
let markMap: Markmap | null = null let markMap: Markmap | null = null
const transformer = new Transformer() const transformer = new Transformer()
const message = useMessage()
onMounted(() => { onMounted(() => {
contentAreaHeight.value = contentRef.value?.clientHeight || 0 // contentAreaHeight.value = contentRef.value?.clientHeight || 0 //
/** 初始化思维导图 **/ /** 初始化思维导图 **/
@ -66,11 +67,11 @@ onMounted(() => {
}) })
watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => { watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
// markdown // markdown
if (isStart) { if (isStart) {
html.value = '' html.value = ''
} }
// 使markdown // 使 markdown
if (isGenerating) { if (isGenerating) {
html.value = md.render(mindmapResult) html.value = md.render(mindmapResult)
} }
@ -79,6 +80,7 @@ watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
} }
}) })
/** 更新思维导图的展示 */
const update = () => { const update = () => {
try { try {
const { root } = transformer.transform(processContent(props.mindmapResult)) const { root } = transformer.transform(processContent(props.mindmapResult))
@ -89,7 +91,8 @@ const update = () => {
} }
} }
const processContent = (text) => { /** 处理内容 */
const processContent = (text: string) => {
const arr: string[] = [] const arr: string[] = []
const lines = text.split('\n') const lines = text.split('\n')
for (let line of lines) { for (let line of lines) {
@ -101,6 +104,9 @@ const processContent = (text) => {
} }
return arr.join('\n') return arr.join('\n')
} }
/** 下载图片 */
// TODO @hhhero download src/utils/download.ts image
// download SVG to png file // download SVG to png file
const downloadImage = () => { const downloadImage = () => {
const svgElement = mindmapRef.value const svgElement = mindmapRef.value
@ -121,7 +127,7 @@ const downloadImage = () => {
image.onload = function () { image.onload = function () {
context?.drawImage(image, 0, 0) context?.drawImage(image, 0, 0)
const a = document.createElement('a') const a = document.createElement('a')
a.download = 'ruoyi-mindmap.png' a.download = 'mindmap.png'
a.href = canvas.toDataURL(`image/png`) a.href = canvas.toDataURL(`image/png`)
a.click() a.click()
} }

View File

@ -1,7 +1,12 @@
<template> <template>
<div class="absolute top-0 left-0 right-0 bottom-0 flex"> <div class="absolute top-0 left-0 right-0 bottom-0 flex">
<!--表单区域--> <!--表单区域-->
<Left ref="leftRef" @submit="submit" @direct-generate="directGenerate" :is-generating="isGenerating" /> <Left
ref="leftRef"
@submit="submit"
@direct-generate="directGenerate"
:is-generating="isGenerating"
/>
<!--右边生成思维导图区域--> <!--右边生成思维导图区域-->
<Right <Right
ref="rightRef" ref="rightRef"
@ -17,10 +22,10 @@
import Left from './components/Left.vue' import Left from './components/Left.vue'
import Right from './components/Right.vue' import Right from './components/Right.vue'
import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap' import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap'
import { MindmapExitExample } from '@/views/ai/utils/constants' import { MindMapContentExample } from '@/views/ai/utils/constants'
defineOptions({ defineOptions({
name: 'AIMindMap' name: 'AiMindMap'
}) })
const ctrl = ref<AbortController>() // const ctrl = ref<AbortController>() //
const isGenerating = ref(false) // const isGenerating = ref(false) //
@ -33,21 +38,21 @@ const mindmapResult = ref('') // 生成思维导图结果
const leftRef = ref<InstanceType<typeof Left>>() // const leftRef = ref<InstanceType<typeof Left>>() //
const rightRef = ref<InstanceType<typeof Right>>() // const rightRef = ref<InstanceType<typeof Right>>() //
onMounted(() => {
mindmapResult.value = MindmapExitExample
})
/** 使用已有内容直接生成 **/ /** 使用已有内容直接生成 **/
const directGenerate = (existPrompt: string) => { const directGenerate = (existPrompt: string) => {
isEnd.value = false // falsetruewatch isEnd.value = false // falsetruewatch
mindmapResult.value = existPrompt mindmapResult.value = existPrompt
isEnd.value = true isEnd.value = true
} }
/** 停止 stream 生成 */ /** 停止 stream 生成 */
const stopStream = () => { const stopStream = () => {
isGenerating.value = false isGenerating.value = false
isStart.value = false isStart.value = false
ctrl.value?.abort() ctrl.value?.abort()
} }
/** 提交生成 */
const submit = (data: AiMindMapGenerateReqVO) => { const submit = (data: AiMindMapGenerateReqVO) => {
isGenerating.value = true isGenerating.value = true
isStart.value = true isStart.value = true
@ -56,7 +61,7 @@ const submit = (data: AiMindMapGenerateReqVO) => {
mindmapResult.value = '' // mindmapResult.value = '' //
AiMindMapApi.generateMindMap({ AiMindMapApi.generateMindMap({
data, data,
onMessage:async (res) => { onMessage: async (res) => {
const { code, data, msg } = JSON.parse(res.data) const { code, data, msg } = JSON.parse(res.data)
if (code !== 0) { if (code !== 0) {
message.alert(`生成思维导图异常! ${msg}`) message.alert(`生成思维导图异常! ${msg}`)
@ -69,7 +74,7 @@ const submit = (data: AiMindMapGenerateReqVO) => {
}, },
onClose() { onClose() {
isEnd.value = true isEnd.value = true
leftRef.value?.setExistPrompt(mindmapResult.value) leftRef.value?.setGeneratedContent(mindmapResult.value)
stopStream() stopStream()
}, },
onError(err) { onError(err) {
@ -79,4 +84,9 @@ const submit = (data: AiMindMapGenerateReqVO) => {
ctrl: ctrl.value ctrl: ctrl.value
}) })
} }
/** 初始化 */
onMounted(() => {
mindmapResult.value = MindMapContentExample
})
</script> </script>

View File

@ -67,10 +67,11 @@ export enum AiWriteTypeEnum {
// 表格展示对照map // 表格展示对照map
export const AiWriteTypeTableRender = { export const AiWriteTypeTableRender = {
[AiWriteTypeEnum.WRITING]: '撰写', [AiWriteTypeEnum.WRITING]: '撰写',
[AiWriteTypeEnum.REPLY]: '回复', [AiWriteTypeEnum.REPLY]: '回复'
} }
// ========== 【图片 UI】相关的枚举 ========== // ========== 【图片 UI】相关的枚举 ==========
export const ImageHotWords = [ export const ImageHotWords = [
'中国旗袍', '中国旗袍',
'古装美女', '古装美女',
@ -414,8 +415,11 @@ export const WriteExample = {
data: '您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务请及时与我联系。\n\n祝工作顺利。\n\n谢谢。' data: '您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务请及时与我联系。\n\n祝工作顺利。\n\n谢谢。'
} }
} }
// ========== 【思维导图 UI】相关的枚举 ==========
/** 思维导图已有内容生成示例 **/ /** 思维导图已有内容生成示例 **/
export const MindmapExitExample = `# Java 技术栈 export const MindMapContentExample = `# Java 技术栈
## ##
### Java SE ### Java SE