mirror of
				https://gitee.com/hhyykk/ipms-sjy-ui.git
				synced 2025-11-04 12:18:43 +08:00 
			
		
		
		
	[新增]AI: 思维导图
This commit is contained in:
		@@ -52,7 +52,12 @@
 | 
			
		||||
    "highlight.js": "^11.9.0",
 | 
			
		||||
    "jsencrypt": "^3.3.2",
 | 
			
		||||
    "lodash-es": "^4.17.21",
 | 
			
		||||
    "markdown-it": "^14.1.0",
 | 
			
		||||
    "marked": "^12.0.2",
 | 
			
		||||
    "markmap-common": "^0.16.0",
 | 
			
		||||
    "markmap-lib": "^0.16.1",
 | 
			
		||||
    "markmap-toolbar": "^0.17.0",
 | 
			
		||||
    "markmap-view": "^0.16.0",
 | 
			
		||||
    "min-dash": "^4.1.1",
 | 
			
		||||
    "mitt": "^3.0.1",
 | 
			
		||||
    "nprogress": "^0.2.0",
 | 
			
		||||
@@ -85,8 +90,8 @@
 | 
			
		||||
    "@types/qs": "^6.9.12",
 | 
			
		||||
    "@typescript-eslint/eslint-plugin": "^7.1.0",
 | 
			
		||||
    "@typescript-eslint/parser": "^7.1.0",
 | 
			
		||||
    "@unocss/transformer-variant-group": "^0.58.5",
 | 
			
		||||
    "@unocss/eslint-config": "^0.57.4",
 | 
			
		||||
    "@unocss/transformer-variant-group": "^0.58.5",
 | 
			
		||||
    "@vitejs/plugin-legacy": "^5.3.1",
 | 
			
		||||
    "@vitejs/plugin-vue": "^5.0.4",
 | 
			
		||||
    "@vitejs/plugin-vue-jsx": "^3.1.0",
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										12586
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										12586
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										39
									
								
								src/api/ai/mindmap/index.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/api/ai/mindmap/index.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
import { getAccessToken } from '@/utils/auth'
 | 
			
		||||
import { fetchEventSource } from '@microsoft/fetch-event-source'
 | 
			
		||||
import { config } from '@/config/axios/config'
 | 
			
		||||
 | 
			
		||||
export interface AiMindMapGenerateReqVO {
 | 
			
		||||
  prompt: string
 | 
			
		||||
}
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
export const AiMindMapApi = {
 | 
			
		||||
  generateMindMap: ({
 | 
			
		||||
    data,
 | 
			
		||||
    onClose,
 | 
			
		||||
    onMessage,
 | 
			
		||||
    onError,
 | 
			
		||||
    ctrl
 | 
			
		||||
  }: {
 | 
			
		||||
    data: AiMindMapGenerateReqVO
 | 
			
		||||
    onMessage?: (res: any) => void
 | 
			
		||||
    onError?: (...args: any[]) => void
 | 
			
		||||
    onClose?: (...args: any[]) => void
 | 
			
		||||
    ctrl: AbortController
 | 
			
		||||
  }) => {
 | 
			
		||||
    const token = getAccessToken()
 | 
			
		||||
    return fetchEventSource(`${config.base_url}/ai/mind-map/generate-stream`, {
 | 
			
		||||
      method: 'post',
 | 
			
		||||
      headers: {
 | 
			
		||||
        'Content-Type': 'application/json',
 | 
			
		||||
        Authorization: `Bearer ${token}`
 | 
			
		||||
      },
 | 
			
		||||
      openWhenHidden: true,
 | 
			
		||||
      body: JSON.stringify(data),
 | 
			
		||||
      onmessage: onMessage,
 | 
			
		||||
      onerror: onError,
 | 
			
		||||
      onclose: onClose,
 | 
			
		||||
      signal: ctrl.signal
 | 
			
		||||
    })
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										75
									
								
								src/views/ai/mindmap/index/components/Left.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								src/views/ai/mindmap/index/components/Left.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="w-[350px] p-5 flex flex-col bg-[#f5f7f9]">
 | 
			
		||||
    <h3 class="w-full h-full h-7 text-5 text-center leading-[28px] title">思维导图创作中心</h3>
 | 
			
		||||
    <!--下面表单部分-->
 | 
			
		||||
    <div class="flex-grow overflow-y-auto">
 | 
			
		||||
      <div class="mt-[30ppx]">
 | 
			
		||||
        <el-text tag="b">您的需求?</el-text>
 | 
			
		||||
        <el-input
 | 
			
		||||
          v-model="formData.prompt"
 | 
			
		||||
          maxlength="1024"
 | 
			
		||||
          rows="5"
 | 
			
		||||
          class="w-100% mt-15px"
 | 
			
		||||
          input-style="border-radius: 7px;"
 | 
			
		||||
          placeholder="请输入提示词,让AI帮你完善"
 | 
			
		||||
          show-word-limit
 | 
			
		||||
          type="textarea"
 | 
			
		||||
        />
 | 
			
		||||
        <el-button
 | 
			
		||||
          class="!w-full mt-[15px]"
 | 
			
		||||
          type="primary"
 | 
			
		||||
          :loading="isGenerating"
 | 
			
		||||
          @click="emits('submit', formData)"
 | 
			
		||||
          >智能生成思维导图</el-button
 | 
			
		||||
        >
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="mt-[30px]">
 | 
			
		||||
        <el-text tag="b">使用已有内容生成?</el-text>
 | 
			
		||||
        <el-input
 | 
			
		||||
          v-model="existPrompt"
 | 
			
		||||
          maxlength="1024"
 | 
			
		||||
          rows="5"
 | 
			
		||||
          class="w-100% mt-15px"
 | 
			
		||||
          input-style="border-radius: 7px;"
 | 
			
		||||
          placeholder="例如:童话里的小屋应该是什么样子?"
 | 
			
		||||
          show-word-limit
 | 
			
		||||
          type="textarea"
 | 
			
		||||
        />
 | 
			
		||||
        <el-button
 | 
			
		||||
          class="!w-full mt-[15px]"
 | 
			
		||||
          type="primary"
 | 
			
		||||
          @click="emits('directGenerate', existPrompt)"
 | 
			
		||||
          :disabled="isGenerating"
 | 
			
		||||
          >直接生成</el-button
 | 
			
		||||
        >
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { MindmapExitExample } from '@/views/ai/utils/constants'
 | 
			
		||||
 | 
			
		||||
const emits = defineEmits(['submit', 'directGenerate'])
 | 
			
		||||
defineProps<{
 | 
			
		||||
  isGenerating: boolean
 | 
			
		||||
}>()
 | 
			
		||||
// 提交的提示词字段
 | 
			
		||||
const formData = reactive({
 | 
			
		||||
  prompt: ''
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const existPrompt = ref(MindmapExitExample) // 已有的内容
 | 
			
		||||
 | 
			
		||||
defineExpose({
 | 
			
		||||
  setExistPrompt(e: string){ // 设置已有的内容,在生成结束的时候将结果赋值给该值
 | 
			
		||||
    existPrompt.value = e
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.title {
 | 
			
		||||
  color: var(--el-color-primary);
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										169
									
								
								src/views/ai/mindmap/index/components/Right.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/views/ai/mindmap/index/components/Right.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,169 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <el-card class="my-card h-full flex-grow">
 | 
			
		||||
    <template #header
 | 
			
		||||
      ><h3 class="m-0 px-7 shrink-0 flex items-center justify-between">
 | 
			
		||||
        <span>思维导图预览</span>
 | 
			
		||||
        <!-- 展示在右上角 -->
 | 
			
		||||
        <el-button type="primary" v-show="isEnd" @click="downloadImage" size="small">
 | 
			
		||||
          <template #icon>
 | 
			
		||||
            <Icon icon="ph:copy-bold" />
 | 
			
		||||
          </template>
 | 
			
		||||
          下载图片
 | 
			
		||||
        </el-button>
 | 
			
		||||
      </h3></template
 | 
			
		||||
    >
 | 
			
		||||
 | 
			
		||||
    <div ref="contentRef" class="hide-scroll-bar h-full box-border">
 | 
			
		||||
      <!--展示markdown的容器,最终生成的是html字符串,直接用v-html嵌入-->
 | 
			
		||||
      <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>
 | 
			
		||||
 | 
			
		||||
      <div ref="mindmapRef" class="wh-full">
 | 
			
		||||
        <svg ref="svgRef" class="w-full" :style="{ height: `${contentAreaHeight}px` }" />
 | 
			
		||||
        <div ref="toolBarRef" class="absolute bottom-[10px] right-5"></div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
  </el-card>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import { Markmap } from 'markmap-view'
 | 
			
		||||
import { Transformer } from 'markmap-lib'
 | 
			
		||||
import { Toolbar } from 'markmap-toolbar'
 | 
			
		||||
import markdownit from 'markdown-it'
 | 
			
		||||
 | 
			
		||||
const md = markdownit()
 | 
			
		||||
const props = defineProps<{
 | 
			
		||||
  mindmapResult: string // 生成结果
 | 
			
		||||
  isEnd: boolean // 是否结束
 | 
			
		||||
  isGenerating: boolean // 是否正在生成
 | 
			
		||||
  isStart: boolean // 开始状态,开始时需要清除html
 | 
			
		||||
}>()
 | 
			
		||||
const contentRef = ref<HTMLDivElement>() // 右侧出来header以下的区域
 | 
			
		||||
const mdContainerRef = ref<HTMLDivElement>() // markdown的容器,用来滚动到底下的
 | 
			
		||||
const mindmapRef = ref<HTMLDivElement>() // 思维导图的容器
 | 
			
		||||
const svgRef = ref<SVGElement>() // 思维导图的渲染svg
 | 
			
		||||
const toolBarRef = ref<HTMLDivElement>() // 思维导图右下角的工具栏,缩放等
 | 
			
		||||
const html = ref('') // 生成过程中的文本
 | 
			
		||||
const contentAreaHeight = ref(0) // 生成区域的高度,出去header部分
 | 
			
		||||
let markMap: Markmap | null = null
 | 
			
		||||
const transformer = new Transformer()
 | 
			
		||||
 | 
			
		||||
const message = useMessage()
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  contentAreaHeight.value = contentRef.value?.clientHeight || 0 // 获取区域高度
 | 
			
		||||
  /** 初始化思维导图 **/
 | 
			
		||||
  try {
 | 
			
		||||
    markMap = Markmap.create(svgRef.value!)
 | 
			
		||||
    const { el } = Toolbar.create(markMap)
 | 
			
		||||
    toolBarRef.value?.append(el)
 | 
			
		||||
    nextTick(update)
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    message.error('思维导图初始化失败')
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
watch(props, ({ mindmapResult, isGenerating, isEnd, isStart }) => {
 | 
			
		||||
  // 开始生成的时候清空一下markdown的内容
 | 
			
		||||
  if (isStart) {
 | 
			
		||||
    html.value = ''
 | 
			
		||||
  }
 | 
			
		||||
  // 生成内容的时候使用markdown来渲染
 | 
			
		||||
  if (isGenerating) {
 | 
			
		||||
    html.value = md.render(mindmapResult)
 | 
			
		||||
  }
 | 
			
		||||
  if (isEnd) {
 | 
			
		||||
    update()
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const update = () => {
 | 
			
		||||
  try {
 | 
			
		||||
    const { root } = transformer.transform(processContent(props.mindmapResult))
 | 
			
		||||
    markMap?.setData(root)
 | 
			
		||||
    markMap?.fit()
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    console.error(e)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const processContent = (text) => {
 | 
			
		||||
  const arr: string[] = []
 | 
			
		||||
  const lines = text.split('\n')
 | 
			
		||||
  for (let line of lines) {
 | 
			
		||||
    if (line.indexOf('```') !== -1) {
 | 
			
		||||
      continue
 | 
			
		||||
    }
 | 
			
		||||
    line = line.replace(/([*_~`>])|(\d+\.)\s/g, '')
 | 
			
		||||
    arr.push(line)
 | 
			
		||||
  }
 | 
			
		||||
  return arr.join('\n')
 | 
			
		||||
}
 | 
			
		||||
// download SVG to png file
 | 
			
		||||
const downloadImage = () => {
 | 
			
		||||
  const svgElement = mindmapRef.value
 | 
			
		||||
  // 将 SVG 渲染到图片对象
 | 
			
		||||
  const serializer = new XMLSerializer()
 | 
			
		||||
  const source =
 | 
			
		||||
    '<?xml version="1.0" standalone="no"?>\r\n' + serializer.serializeToString(svgRef.value!)
 | 
			
		||||
  const image = new Image()
 | 
			
		||||
  image.src = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(source)
 | 
			
		||||
 | 
			
		||||
  // 将图片对象渲染
 | 
			
		||||
  const canvas = document.createElement('canvas')
 | 
			
		||||
  canvas.width = svgElement?.offsetWidth || 0
 | 
			
		||||
  canvas.height = svgElement?.offsetHeight || 0
 | 
			
		||||
  let context = canvas.getContext('2d')
 | 
			
		||||
  context?.clearRect(0, 0, canvas.width, canvas.height)
 | 
			
		||||
 | 
			
		||||
  image.onload = function () {
 | 
			
		||||
    context?.drawImage(image, 0, 0)
 | 
			
		||||
    const a = document.createElement('a')
 | 
			
		||||
    a.download = 'ruoyi-mindmap.png'
 | 
			
		||||
    a.href = canvas.toDataURL(`image/png`)
 | 
			
		||||
    a.click()
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
defineExpose({
 | 
			
		||||
  scrollBottom() {
 | 
			
		||||
    mdContainerRef.value?.scrollTo(0, mdContainerRef.value?.scrollHeight)
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
</script>
 | 
			
		||||
<style lang="scss" scoped>
 | 
			
		||||
.hide-scroll-bar {
 | 
			
		||||
  -ms-overflow-style: none;
 | 
			
		||||
  scrollbar-width: none;
 | 
			
		||||
 | 
			
		||||
  &::-webkit-scrollbar {
 | 
			
		||||
    width: 0;
 | 
			
		||||
    height: 0;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
.my-card {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: column;
 | 
			
		||||
 | 
			
		||||
  :deep(.el-card__body) {
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
    padding: 0;
 | 
			
		||||
    @extend .hide-scroll-bar;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
// markmap的tool样式覆盖
 | 
			
		||||
:deep(.markmap) {
 | 
			
		||||
  width: 100%;
 | 
			
		||||
}
 | 
			
		||||
:deep(.mm-toolbar-brand) {
 | 
			
		||||
  display: none;
 | 
			
		||||
}
 | 
			
		||||
:deep(.mm-toolbar) {
 | 
			
		||||
  display: flex;
 | 
			
		||||
  flex-direction: row;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										82
									
								
								src/views/ai/mindmap/index/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/views/ai/mindmap/index/index.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
<template>
 | 
			
		||||
  <div class="absolute top-0 left-0 right-0 bottom-0 flex">
 | 
			
		||||
    <!--表单区域-->
 | 
			
		||||
    <Left ref="leftRef" @submit="submit" @direct-generate="directGenerate" :is-generating="isGenerating" />
 | 
			
		||||
    <!--右边生成思维导图区域-->
 | 
			
		||||
    <Right
 | 
			
		||||
      ref="rightRef"
 | 
			
		||||
      :mindmapResult="mindmapResult"
 | 
			
		||||
      :isEnd="isEnd"
 | 
			
		||||
      :isGenerating="isGenerating"
 | 
			
		||||
      :isStart="isStart"
 | 
			
		||||
    />
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script setup lang="ts">
 | 
			
		||||
import Left from './components/Left.vue'
 | 
			
		||||
import Right from './components/Right.vue'
 | 
			
		||||
import { AiMindMapApi, AiMindMapGenerateReqVO } from '@/api/ai/mindmap'
 | 
			
		||||
import { MindmapExitExample } from '@/views/ai/utils/constants'
 | 
			
		||||
 | 
			
		||||
defineOptions({
 | 
			
		||||
  name: 'AIMindMap'
 | 
			
		||||
})
 | 
			
		||||
const ctrl = ref<AbortController>() // 请求控制
 | 
			
		||||
const isGenerating = ref(false) // 是否正在生成思维导图
 | 
			
		||||
const isStart = ref(false) // 开始生成,用来清空思维导图
 | 
			
		||||
const isEnd = ref(true) // 用来判断结束的时候渲染思维导图
 | 
			
		||||
const message = useMessage() // 消息提示
 | 
			
		||||
 | 
			
		||||
const mindmapResult = ref('') // 生成思维导图结果
 | 
			
		||||
 | 
			
		||||
const leftRef = ref<InstanceType<typeof Left>>() // 左边组件
 | 
			
		||||
const rightRef = ref<InstanceType<typeof Right>>() // 右边组件
 | 
			
		||||
 | 
			
		||||
onMounted(() => {
 | 
			
		||||
  mindmapResult.value = MindmapExitExample
 | 
			
		||||
})
 | 
			
		||||
/** 使用已有内容直接生成 **/
 | 
			
		||||
const directGenerate = (existPrompt: string) => {
 | 
			
		||||
  isEnd.value = false // 先设置为false再设置为true,让子组建的watch能够监听到
 | 
			
		||||
  mindmapResult.value = existPrompt
 | 
			
		||||
  isEnd.value = true
 | 
			
		||||
}
 | 
			
		||||
/** 停止 stream 生成 */
 | 
			
		||||
const stopStream = () => {
 | 
			
		||||
  isGenerating.value = false
 | 
			
		||||
  isStart.value = false
 | 
			
		||||
  ctrl.value?.abort()
 | 
			
		||||
}
 | 
			
		||||
const submit = (data: AiMindMapGenerateReqVO) => {
 | 
			
		||||
  isGenerating.value = true
 | 
			
		||||
  isStart.value = true
 | 
			
		||||
  isEnd.value = false
 | 
			
		||||
  ctrl.value = new AbortController() // 请求控制赋值
 | 
			
		||||
  mindmapResult.value = '' // 清空生成数据
 | 
			
		||||
  AiMindMapApi.generateMindMap({
 | 
			
		||||
    data,
 | 
			
		||||
    onMessage:async (res) => {
 | 
			
		||||
      const { code, data, msg } = JSON.parse(res.data)
 | 
			
		||||
      if (code !== 0) {
 | 
			
		||||
        message.alert(`生成思维导图异常! ${msg}`)
 | 
			
		||||
        stopStream()
 | 
			
		||||
        return
 | 
			
		||||
      }
 | 
			
		||||
      mindmapResult.value = mindmapResult.value + data
 | 
			
		||||
      await nextTick()
 | 
			
		||||
      rightRef.value?.scrollBottom()
 | 
			
		||||
    },
 | 
			
		||||
    onClose() {
 | 
			
		||||
      isEnd.value = true
 | 
			
		||||
      leftRef.value?.setExistPrompt(mindmapResult.value)
 | 
			
		||||
      stopStream()
 | 
			
		||||
    },
 | 
			
		||||
    onError(err) {
 | 
			
		||||
      console.error('生成思维导图失败', err)
 | 
			
		||||
      stopStream()
 | 
			
		||||
    },
 | 
			
		||||
    ctrl: ctrl.value
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
</script>
 | 
			
		||||
@@ -414,3 +414,64 @@ export const WriteExample = {
 | 
			
		||||
    data: '您的请假申请已收悉,经核实和考虑,暂时无法批准您的请假申请。\n\n如有特殊情况或紧急事务,请及时与我联系。\n\n祝工作顺利。\n\n谢谢。'
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
/** 思维导图已有内容生成示例 **/
 | 
			
		||||
export const MindmapExitExample = `# Java 技术栈
 | 
			
		||||
 | 
			
		||||
## 核心技术
 | 
			
		||||
### Java SE
 | 
			
		||||
### Java EE
 | 
			
		||||
 | 
			
		||||
## 框架
 | 
			
		||||
### Spring
 | 
			
		||||
#### Spring Boot
 | 
			
		||||
#### Spring MVC
 | 
			
		||||
#### Spring Data
 | 
			
		||||
### Hibernate
 | 
			
		||||
### MyBatis
 | 
			
		||||
 | 
			
		||||
## 构建工具
 | 
			
		||||
### Maven
 | 
			
		||||
### Gradle
 | 
			
		||||
 | 
			
		||||
## 版本控制
 | 
			
		||||
### Git
 | 
			
		||||
### SVN
 | 
			
		||||
 | 
			
		||||
## 测试工具
 | 
			
		||||
### JUnit
 | 
			
		||||
### Mockito
 | 
			
		||||
### Selenium
 | 
			
		||||
 | 
			
		||||
## 应用服务器
 | 
			
		||||
### Tomcat
 | 
			
		||||
### Jetty
 | 
			
		||||
### WildFly
 | 
			
		||||
 | 
			
		||||
## 数据库
 | 
			
		||||
### MySQL
 | 
			
		||||
### PostgreSQL
 | 
			
		||||
### Oracle
 | 
			
		||||
### MongoDB
 | 
			
		||||
 | 
			
		||||
## 消息队列
 | 
			
		||||
### Kafka
 | 
			
		||||
### RabbitMQ
 | 
			
		||||
### ActiveMQ
 | 
			
		||||
 | 
			
		||||
## 微服务
 | 
			
		||||
### Spring Cloud
 | 
			
		||||
### Dubbo
 | 
			
		||||
 | 
			
		||||
## 容器化
 | 
			
		||||
### Docker
 | 
			
		||||
### Kubernetes
 | 
			
		||||
 | 
			
		||||
## 云服务
 | 
			
		||||
### AWS
 | 
			
		||||
### Azure
 | 
			
		||||
### Google Cloud
 | 
			
		||||
 | 
			
		||||
## 开发工具
 | 
			
		||||
### IntelliJ IDEA
 | 
			
		||||
### Eclipse
 | 
			
		||||
### Visual Studio Code`
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user