跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
TypeScriptAI大前端

Vue 3 重构 Dify 聊天前端:项目搭建与基础架构

综述由AI生成介绍使用 Vue 3 和 TypeScript 重构 Dify 聊天前端的完整流程。涵盖项目初始化、技术栈选型(Vite、Pinia、Element Plus)、TypeScript 类型体系设计(消息、Agent、工作流)、路由与 SSO 认证、HTTP 客户端封装以及 Pinia 状态管理。重点讲解了 SSE 流式输出架构及前后端数据转换逻辑,为构建生产级 AI 聊天界面奠定基础。

追风少年发布于 2026/4/6更新于 2026/5/2028 浏览
Vue 3 重构 Dify 聊天前端:项目搭建与基础架构

本系列教程将带你从零开始,用 Vue 3 + TypeScript 复刻一个类似 Dify 的 AI 聊天前端。上篇聚焦项目搭建、类型设计、路由认证、HTTP 封装和状态管理。

聊天页面

聊天输出

项目简介

背景

Dify 是一个开源的 LLM 应用开发平台,提供了对话式 AI 的后端服务。在实际项目中,我们往往需要自建前端来对接 Dify 后端 API 或 LLM 后端服务,实现定制化的聊天界面。

本项目的目标:用 Vue 3 构建一个生产级的 AI 聊天前端,具备以下能力:

  • SSE 流式输出(打字机效果)
  • Markdown 渲染 + 代码高亮
  • 用户认证
  • 文件/图片上传
  • 聊天会话历史管理
  • 工作流执行可视化
  • Agent 思考过程展示
  • 移动端响应式适配
技术栈
类别技术选型说明
框架Vue 3.4 + Composition API使用 <script setup> 语法
语言TypeScript 5.3全量类型覆盖
构建Vite 5.1极速 HMR
状态管理Pinia 2.1Composition API 风格
UI 库Element Plus 2.5成熟的 Vue 3 组件库
HTTPAxios 1.6请求拦截 + 统一错误处理
Markdownmarked 12 + highlight.js 11GFM 渲染 + 代码高亮
安全DOMPurify 3.0XSS 防护
样式SCSSCSS 变量 + 响应式
项目结构
src/
├── api/              # API 层
│   ├── app.ts        # 应用信息接口
│   ├── chat.ts       # 聊天 API + SSE 流式
│   └── user.ts       # 用户认证接口
├── assets/           # 静态资源
├── components/       
│   ├── AgentThought.vue      
│   ├── ChatSidebar.vue       
│   ├── ConversationItem.vue  
│   ├── FeedbackButtons.vue   
│   ├── FileUpload.vue        
│   ├── MarkdownRenderer.vue  
│   ├── MessageActions.vue    
│   ├── SuggestedQuestions.vue
│   └── WorkflowTracing.vue   
├── router/             
│   └── index.ts
├── stores/             
│   ├── conversation.ts 
│   └── user.ts         
├── styles/             
├── utils/              
│   └── request.ts      
├── views/              
│   ├── ChatView.vue    
│   └── ErrorView.vue   
├── main.ts             
└── App.vue             
# 可复用组件(9 个)
# Agent 思考过程
# 侧边栏
# 会话条目
# 反馈按钮
# 文件上传
# Markdown 渲染
# 消息操作
# 建议问题
# 工作流可视化
# 路由配置
# Pinia 状态管理
# 会话状态
# 用户状态
# 全局样式
# 工具函数
# Axios 封装
# 页面组件
# 主聊天页面
# 错误页面
# 入口文件
# 根组件
整体数据流
用户输入 → ChatView → sendChatMessageSSE() → Dify API
↑ ↓
messages 数组 ← SSE 事件流(message/agent_thought/workflow)
↓
MarkdownRenderer → 实时渲染 AI 回复

1. 项目初始化

1.1 创建项目
npm create vite@latest dify-chat -- --template vue-ts
cd dify-chat
npm install
1.2 安装依赖
# 核心依赖
npm install vue-router pinia axios element-plus @element-plus/icons-vue

# Markdown 与代码高亮
npm install marked highlight.js dompurify

# 开发依赖
npm install -D sass @types/dompurify
1.3 Vite 配置

vite.config.ts 是整个项目的构建核心:

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())
  return {
    // 支持子路径部署(如 /chat-app/)
    base: env.VITE_APP_NGINX_SUB_PATH || '/',
    plugins: [vue()],
    css: {
      preprocessorOptions: {
        scss: {
          api: 'modern-compiler' // 使用现代 SCSS 编译器
        }
      }
    },
    resolve: {
      alias: {
        '@': '/src' // 路径别名,import xx from '@/utils/xx'
      }
    },
    server: {
      port: 3001,
      proxy: {
        // 开发环境将 /api 请求代理到后端
        '/api': {
          target: 'http://localhost:9000',
          changeOrigin: true,
          rewrite: (path) => path.replace(/^\/api/, '')
        }
      }
    }
  }
})

关键设计决策:

  • 代理配置:前端 3001 端口,后端 9000 端口。开发时所有 /api/* 请求自动转发到后端,避免跨域问题。
  • 路径别名:@/ 映射到 src/,让导入路径更清晰。
  • 子路径部署:通过环境变量 VITE_APP_NGINX_SUB_PATH 控制 base path,适配不同部署环境。
1.4 环境变量

创建 .env 文件:

VITE_APP_TITLE=AI 智能助手
VITE_API_BASE_URL=/api
VITE_APP_NGINX_SUB_PATH=/

TypeScript 类型声明 src/env.d.ts:

interface ImportMetaEnv {
  readonly VITE_APP_TITLE: string
  readonly VITE_API_BASE_URL: string
  readonly VITE_APP_NGINX_SUB_PATH: string
}

2. TypeScript 类型体系设计

类型设计是整个项目的基础。我们把所有聊天相关的类型集中在 src/api/chat.ts 中定义。

2.1 消息类型
// 前端使用的消息结构
export interface Message {
  id: string
  role: 'user' | 'assistant'
  content: string
  createAt?: number
  parentMessageId?: string
  messageFiles?: MessageFile[]
  agentThoughts?: AgentThought[]
  workflow_process?: WorkflowProcess
  feedback?: Feedback
  citation?: Citation[]
}

// 后端返回的消息结构(需要转换)
export interface BackendMessage {
  id: string
  conversationId: string
  parentMessageId: string
  inputs: Record<string, any>
  query: string // 用户问题
  answer: string // AI 回答
  messageFiles: MessageFile[]
  feedback: Feedback | null
  retrieverResources: any[]
  agentThoughts: AgentThought[]
  createdAt: number
  status: string
  error: string | null
}

为什么要区分 Message 和 BackendMessage?

后端返回的是'一轮对话'的结构(包含 query + answer),而前端渲染需要拆分为 user 和 assistant 两条独立消息。这个转换在 getMessages 函数中完成。

2.2 文件与附件
export interface MessageFile {
  id: string
  type: string // 'image' | 'document' 等
  url: string
  filename?: string
  size?: number
}
2.3 Agent 思考过程
export interface AgentThought {
  id: string
  thought: string // 思考内容
  tool?: string // 调用的工具名称
  tool_input?: string // 工具输入参数
  observation?: string // 工具执行结果
  message_files?: MessageFile[]
  created_at?: number
}
2.4 工作流追踪
export interface WorkflowProcess {
  status: string // 'running' | 'succeeded' | 'failed'
  tracing: WorkflowNode[]
}

export interface WorkflowNode {
  id: string
  node_id: string
  title: string // 节点名称,如'大模型'、'知识检索'
  status: string // 'running' | 'succeeded' | 'failed'
  inputs?: any
  outputs?: any
  elapsed_time?: number // 执行耗时(秒)
}
2.5 用户反馈
export interface Feedback {
  rating: 'like' | 'dislike' | null
  content?: string // 详细反馈内容
}
2.6 引用来源
export interface Citation {
  position: number
  document_id: string
  document_name: string
  data_source_type: string
  source_url?: string
}
2.7 请求与响应类型
export interface ChatRequest {
  appId: string
  query: string
  conversationId?: string // 有值表示继续已有会话
  inputs?: Record<string, any>
  files?: string[] // 已上传文件的 URL 列表
}
2.8 SSE 回调接口

这是整个流式聊天的核心类型定义——通过回调函数把不同类型的 SSE 事件分发到对应的处理逻辑:

export interface SSECallbacks {
  onMessage: (content: string, taskId?: string, messageId?: string) => void
  onThought: (thought: AgentThought) => void
  onFile: (file: MessageFile) => void
  onMessageEnd: (data: { messageId: string; conversationId: string; citation?: Citation[]; feedback?: Feedback }) => void
  onWorkflowStarted: (data: { workflowRunId: string; taskId: string; conversationId: string }) => void
  onNodeStarted: (node: WorkflowNode) => void
  onNodeFinished: (node: WorkflowNode) => void
  onWorkflowFinished: (data: { status: string; conversationId: string }) => void
  onTTSChunk: (audio: string) => void
  onSuggestedQuestions: (questions: string[]) => void
  onError: (error: string) => void
  onDone: () => void
}

这种设计的好处:SSE 解析逻辑与 UI 更新逻辑完全解耦。sendChatMessageSSE 函数只负责解析 SSE 流并触发回调,而 ChatView 组件通过回调函数决定如何更新 UI。


3. 路由与认证

3.1 路由设计

src/router/index.ts:

import { createRouter, createWebHistory } from 'vue-router'
import { getInfo, ssoLogin } from '@/api/user'
import { useUserStore } from '@/stores/user'

const router = createRouter({
  history: createWebHistory(import.meta.env.VITE_APP_NGINX_SUB_PATH),
  routes: [
    {
      path: '/',
      redirect: '/error?message=请从安溪专区访问'
    },
    {
      path: '/chat/:appId', // 动态路由,支持多应用
      name: 'Chat',
      component: () => import('@/views/ChatView.vue') // 懒加载
    },
    {
      path: '/error',
      name: 'Error',
      component: () => import('@/views/ErrorView.vue')
    }
  ]
})

设计要点:

  • /chat/:appId 使用动态路由,同一个前端可以对接不同的 AI 应用(每个应用有不同的 prompt、模型配置等)。
  • 路由懒加载(() => import()),首屏不会加载所有页面代码。
  • 根路径重定向到错误页,强制用户从指定入口访问。
3.2 SSO 单点登录

通过路由守卫 beforeEach 实现 SSO 认证流程:

router.beforeEach(async (to, from, next) => {
  const userStore = useUserStore()
  const ssoApp = to.query.appid as string
  const ssoCode = to.query.code as string
  let token = userStore.token // 开发环境自动 mock

  if (import.meta.env.DEV) {
    const devToken = 'eyJhbGciOiJIUzI1NiJ9...'
    userStore.setToken(devToken)
    userStore.setUserInfo({ ssoUserName: 'dev_user', nickName: '开发测试用户' })
    token = devToken
  }

  if (ssoApp && ssoCode && !token) {
    try {
      // 1. 用 SSO code 换取 token
      const newToken = await ssoLogin(ssoApp, ssoCode)
      userStore.setToken(newToken)
      // 2. 获取用户信息
      const userInfo = await getInfo()
      userStore.setUserInfo(userInfo)
      // 3. 去掉 URL 中的 code 参数,刷新路由
      const query = { ...to.query }
      delete query.code
      next({ ...to, query, replace: true })
    } catch (error) {
      next({ path: '/error', query: { message: 'SSO 登录失败,请重试' } })
    }
  } else if (!token && to.path !== '/error') {
    next({ path: '/error', query: { message: '未登录,请从门户网站进入' } })
  } else {
    next()
  }
})

SSO 认证流程图:

门户入口 → 带 appid + code 参数访问 /chat/xxx
↓
beforeEach 拦截 → 检测到 code
↓
ssoLogin(appid, code) → 获取 token
↓
getInfo() → 获取用户信息
↓
存储 token + userInfo → 重定向(去掉 code 参数)
↓
进入 ChatView 正常聊天

4. HTTP 客户端封装

4.1 Axios 实例

src/utils/request.ts:

import axios from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus'
import { TOKEN_KEY, useUserStore } from '@/stores/user'

const request = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL, // '/api'
  timeout: 120000 // 2 分钟超时,AI 响应可能较慢
})
4.2 请求拦截器

核心功能:自动注入 Bearer Token。

request.interceptors.request.use((config) => {
  const token = localStorage.getItem(TOKEN_KEY)
  if (token) {
    config.headers.Authorization = `Bearer ${token}`
  }
  return config
}, (error) => Promise.reject(error))

这里直接从 localStorage 读取 token,而不是从 Pinia store 读取,是为了避免循环依赖问题(store 还未初始化时就需要发请求)。

4.3 响应拦截器

统一处理后端返回的业务错误码:

request.interceptors.response.use(
  (res) => {
    const code = res.data.code || 200
    const msg = errorCode[code] || res.data.msg || errorCode['default']
    // 二进制数据直接返回
    if (res.request.responseType === 'blob' || res.request.responseType === 'arraybuffer') {
      return res.data
    }
    const userStore = useUserStore()
    if (code === 401) {
      // 认证过期 → 清除登录状态 + 弹窗提示
      userStore.logout()
      return ElMessageBox.alert('登录状态已过期,请重新从门户点击访问!', '认证失败').then(() => Promise.reject('无效的会话'))
    } else if (code === 500) {
      ElMessage.error(msg)
      return Promise.reject(new Error(msg))
    } else if (code === 601) {
      ElMessage.warning(msg)
      return Promise.reject('error')
    } else if (code !== 200) {
      ElMessage.error(msg)
      return Promise.reject('error')
    } else {
      return res.data.data // 只返回业务数据
    }
  },
  (error) => {
    ElMessage.error(error)
    return Promise.reject(error)
  }
)

关键设计:

  • 响应拦截器已经'解包'了后端响应,API 函数拿到的直接就是 data 字段的值。
  • 401 时自动清除用户状态,引导用户重新登录。
  • 二进制数据(文件下载)特殊处理,直接返回完整 response。
4.4 API 函数示例

有了封装好的 request,API 函数写起来非常简洁:

// src/api/app.ts
export const getAppById = async (id: string): Promise<AppInfo> => {
  return await request.get(`/app/${id}`)
}

// src/api/user.ts
export const ssoLogin = async (ssoApp: string, ssoCode: string): Promise<string> => {
  return await request.get('/public/login/ssoLogin', {
    params: { ssoApp, ssoCode }
  })
}

export const getInfo = async (): Promise<UserInfo> => {
  return await request.get('/user/getInfo')
}

5. Pinia Store 设计

5.1 用户状态管理

src/stores/user.ts — 使用 Composition API 风格(defineStore + setup 函数):

import { defineStore } from 'pinia'
import { ref } from 'vue'

export const TOKEN_KEY = 'ai-token'
export const USER_INFO_KEY = 'ai-userInfo'

export const useUserStore = defineStore('user', () => {
  const userInfo = ref<UserInfo | null>(null)
  const token = ref<string>('')

  // 设置 Token → 同时持久化到 localStorage
  const setToken = (value: string) => {
    token.value = value
    localStorage.setItem(TOKEN_KEY, value)
  }

  // 设置用户信息 → 同步持久化
  const setUserInfo = (info: UserInfo) => {
    userInfo.value = info
    localStorage.setItem(USER_INFO_KEY, JSON.stringify(info))
  }

  // 从 localStorage 恢复(页面刷新后)
  const initFromStorage = () => {
    const storedToken = localStorage.getItem(TOKEN_KEY)
    const storedUserInfo = localStorage.getItem(USER_INFO_KEY)
    if (storedToken) token.value = storedToken
    if (storedUserInfo) {
      try {
        userInfo.value = JSON.parse(storedUserInfo)
      } catch {
        userInfo.value = null
      }
    }
  }

  // 登出 → 清空所有
  const logout = () => {
    userInfo.value = null
    token.value = ''
    localStorage.removeItem(TOKEN_KEY)
    localStorage.removeItem(USER_INFO_KEY)
  }

  return {
    userInfo,
    token,
    setToken,
    setUserInfo,
    initFromStorage,
    logout
  }
})

设计要点:

  • Token 和 UserInfo 同时存在内存(ref)和 localStorage 中。内存保证响应式,localStorage 保证刷新后不丢失。
  • initFromStorage 在应用启动时调用(main.ts 中),确保路由守卫能拿到 token。
5.2 会话状态管理

src/stores/conversation.ts — 管理会话列表和消息:

import { defineStore } from 'pinia'
import { computed, ref } from 'vue'
import {
  getConversations,
  getMessages,
  deleteConversation as deleteConversationApi,
  pinConversation as pinConversationApi,
  unpinConversation as unpinConversationApi,
  updateConversationName
} from '@/api/chat'

export const useConversationStore = defineStore('conversation', () => {
  // === 状态 ===
  const appId = ref<string>('')
  const conversations = ref<ConversationItem[]>([])
  const pinnedConversations = ref<ConversationItem[]>([])
  const currentConversationId = ref<string>('')
  const currentMessages = ref<any[]>([])
  const isLoading = ref(false)
  const isLoadingMessages = ref(false)

  // === 计算属性 ===
  // 置顶 + 普通合并后的完整列表
  const allConversations = computed(() => [...pinnedConversations.value, ...conversations.value])

  // 当前选中的会话对象
  const currentConversation = computed(() => allConversations.value.find(c => c.id === currentConversationId.value))

  // === 核心方法 ===
  // 加载会话列表
  const loadConversations = async () => {
    if (!appId.value) return
    isLoading.value = true
    try {
      conversations.value = await getConversations(appId.value, 30)
    } finally {
      isLoading.value = false
    }
  }

  // 切换会话 → 加载该会话的消息
  const switchConversation = async (conversationId: string) => {
    if (conversationId === currentConversationId.value) return
    currentConversationId.value = conversationId
    currentMessages.value = []
    if (conversationId) await loadMessages(conversationId)
  }

  // 加载消息 → 关键转换:BackendMessage → Message
  const loadMessages = async (conversationId: string) => {
    if (!appId.value) return
    isLoadingMessages.value = true
    try {
      currentMessages.value = await getMessages(appId.value, conversationId)
    } finally {
      isLoadingMessages.value = false
    }
  }

  // 创建新会话
  const createNewConversation = () => {
    currentConversationId.value = ''
    currentMessages.value = []
  }

  // 删除会话 → 从列表移除 + 如果是当前会话则切换
  const deleteConversation = async (conversationId: string) => {
    await deleteConversationApi(conversationId)
    conversations.value = conversations.value.filter(c => c.id !== conversationId)
    pinnedConversations.value = pinnedConversations.value.filter(c => c.id !== conversationId)
    if (currentConversationId.value === conversationId) {
      createNewConversation()
    }
  }

  // 流式聊天时的实时更新方法
  const updateCurrentConversationId = (conversationId: string) => {
    currentConversationId.value = conversationId
  }

  const addMessage = (message: any) => {
    currentMessages.value.push(message)
  }

  const updateLastMessage = (content: string, extras?: any) => {
    const lastMsg = currentMessages.value[currentMessages.value.length - 1]
    if (lastMsg) {
      lastMsg.content = content
      Object.assign(lastMsg, extras)
    }
  }

  return {
    // 状态
    appId,
    conversations,
    pinnedConversations,
    currentConversationId,
    currentMessages,
    isLoading,
    isLoadingMessages,
    // 计算属性
    currentConversation,
    allConversations,
    // 方法
    setAppId,
    loadConversations,
    switchConversation,
    createNewConversation,
    deleteConversation,
    updateCurrentConversationId,
    addMessage,
    updateLastMessage,
    renameConversation,
    pinConversation,
    unpinConversation
  }
})

架构决策:

  1. 会话分两组管理(pinned / normal),计算属性合并展示。置顶操作就是数组间的移动。
  2. 消息更新使用 updateLastMessage,这是 SSE 流式更新的关键。每个 message 事件到达时,只需更新最后一条消息的 content。
  3. addMessage + updateLastMessage 组合使用:发送消息时先 addMessage 一个空的 assistant 消息占位,然后 SSE 回调不断 updateLastMessage 追加内容。
5.3 后端消息 → 前端消息的转换

这个转换发生在 src/api/chat.ts 的 getMessages 函数中:

export const getMessages = async (appId: string, conversationId: string): Promise<Message[]> => {
  const backendMessages: BackendMessage[] = await request.post('/chat/message/messages', {
    appId,
    conversationId,
    limit: 50
  })
  const messages: Message[] = []
  backendMessages.forEach(m => {
    // 后端一轮对话 = 一条 BackendMessage
    // 前端需要拆成 user + assistant 两条 Message
    messages.push({
      id: `${m.id}-user`,
      role: 'user',
      content: m.query, // 用户问题
      createAt: m.createdAt,
      messageFiles: m.messageFiles
    })
    messages.push({
      id: `${m.id}-assistant`,
      role: 'assistant',
      content: m.answer, // AI 回答
      createAt: m.createdAt,
      agentThoughts: m.agentThoughts,
      feedback: m.feedback || undefined,
      messageFiles: m.messageFiles
    })
  })
  return messages
}

小结

上篇我们完成了:

  1. 项目脚手架 — Vite + Vue 3 + TypeScript + Element Plus
  2. 类型体系 — 覆盖消息、文件、工作流、Agent 思考等所有业务实体
  3. 认证流程 — SSO 单点登录 + 路由守卫
  4. HTTP 封装 — 自动 Token 注入 + 统一错误处理
  5. 状态管理 — 用户 Store + 会话 Store,支持 localStorage 持久化

下一篇预告:中篇将深入核心功能——SSE 流式聊天的完整实现、ChatView 组件的核心逻辑、Markdown 渲染、文件上传和会话管理。

目录

  1. 项目简介
  2. 背景
  3. 技术栈
  4. 项目结构
  5. 整体数据流
  6. 1. 项目初始化
  7. 1.1 创建项目
  8. 1.2 安装依赖
  9. 核心依赖
  10. Markdown 与代码高亮
  11. 开发依赖
  12. 1.3 Vite 配置
  13. 1.4 环境变量
  14. 2. TypeScript 类型体系设计
  15. 2.1 消息类型
  16. 2.2 文件与附件
  17. 2.3 Agent 思考过程
  18. 2.4 工作流追踪
  19. 2.5 用户反馈
  20. 2.6 引用来源
  21. 2.7 请求与响应类型
  22. 2.8 SSE 回调接口
  23. 3. 路由与认证
  24. 3.1 路由设计
  25. 3.2 SSO 单点登录
  26. 4. HTTP 客户端封装
  27. 4.1 Axios 实例
  28. 4.2 请求拦截器
  29. 4.3 响应拦截器
  30. 4.4 API 函数示例
  31. 5. Pinia Store 设计
  32. 5.1 用户状态管理
  33. 5.2 会话状态管理
  34. 5.3 后端消息 → 前端消息的转换
  35. 小结
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • AstrBot 插件开发实战:Python3.10+ 天气查询机器人实现
  • Vite7+Vue3+DeepSeek-R1 流式 AI 聊天系统实战
  • AI 视频生成模型开源实战:从选型到生产环境部署的避坑指南
  • Spring Boot 3.x 启动报错 factoryBeanObjectType 解决方案
  • AI 小说生成器本地部署与配置指南
  • Java 时间类详解:JDK8 全新时间 API 教程
  • 基于纯 CSS 实现简洁名片卡片设计
  • Z-Image-Turbo 新手入门:从 0 开始使用 AI 绘画
  • Linux kill 命令用法及常用信号解析
  • 基于STM32的智能家居安防系统设计与实现
  • 深度学习实战:卷积神经网络(CNN)原理与应用
  • Cursor 集成 MCP 服务使用指南
  • 基于 Z-Image 与 ComfyUI 的国产 AIGC 图像生成生态构建
  • 全国计算机等级考试二级 Python 历年真题及参考答案(综合应用题)
  • 二级 Python 考试综合应用题真题及参考代码解析
  • 高级加密标准(AES)算法详解
  • GitHub 汉化插件安装与配置指南
  • Python OCR 文字识别:pytesseract 安装配置与实战
  • Linux Mint 22.3 安装指南与系统优化实战
  • Seedream 4.0 深度测评:AI 图像生成与企业级应用

相关免费在线工具

  • RSA密钥对生成器

    生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online

  • Mermaid 预览与可视化编辑

    基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online

  • 随机西班牙地址生成器

    随机生成西班牙地址(支持马德里、加泰罗尼亚、安达卢西亚、瓦伦西亚筛选),支持数量快捷选择、显示全部与下载。 在线工具,随机西班牙地址生成器在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online