AI 应用生成平台:前端与可视化
六、前端与可视化
项目预览:https://www.joinoai.cloud
项目仓库:https://github.com/vasc-language/ai-code-mother
项目仓库:https://gitee.com/vasc-language/ai-code-mother
31. AI 零代码应用生成项目中,你在后端如何实现可视化修改功能?
基于项目代码分析,后端的可视化修改功能主要通过以下方式实现:
1. 工具系统架构
项目使用LangChain4j的工具调用机制,AI可以通过工具来实现文件的读取、修改和写入:
FileWriteTool: 文件写入工具FileReadTool: 文件读取工具FileModifyTool: 文件修改工具
2. 可视化编辑流程
// 用户在前端选中元素 → 生成选择器 → 发送修改指令 → AI调用工具修改代码
@Tool("修改HTML文件中指定元素的内容或样式")
public String modifyElement(
@P("文件路径") String filePath,
@P("元素选择器") String selector,
@P("修改内容") String modification
) {
// 1. 读取HTML文件
String htmlContent = FileReadTool.readFile(filePath);
// 2. 解析DOM,定位元素
Document document = Jsoup.parse(htmlContent);
Elements elements = document.select(selector);
// 3. 应用修改
elements.forEach(element -> {
// 根据modification类型执行不同操作
applyModification(element, modification);
});
// 4. 保存修改后的文件
FileWriteTool.writeFile(filePath, document.html());
return "元素修改成功";
}
3. 安全控制机制
- 路径白名单:限制只能修改应用目录下的文件
- 文件类型检查:只允许修改HTML、CSS、JS等前端文件
- 元素验证:验证选择器的合法性,防止恶意修改
33. AI 零代码应用生成项目中,实现对话历史功能时,前端如何配合后端游标分页机制进行数据请求?
基于AppChatPage.vue的分析,对话历史的游标分页机制实现如下:
1. 前端分页状态管理
// 历史消息分页相关
const hasMoreHistory = ref(true) // 是否有更多历史消息
const loadingHistory = ref(false) // 正在加载历史状态
const oldestMessageId = ref<number>() // 最旧消息的ID(游标)
2. 加载更多历史消息逻辑
const loadMoreHistory = async () => {
if (loadingHistory.value || !hasMoreHistory.value) return
loadingHistory.value = true
try {
// 使用游标分页请求
const res = await listChatHistoryByPage({
appId: appId.value,
pageSize: 20,
cursor: oldestMessageId.value, // 游标:最旧消息ID
sortOrder: 'desc' // 按时间倒序
})
if (res.data.code === 0) {
const historyList = res.data.data?.records || []
if (historyList.length > 0) {
// 处理历史消息格式
const formattedMessages = historyList.map(item => ({
type: item.messageType === 'USER' ? 'user' : 'ai',
content: item.message,
timestamp: item.createTime
}))
// 插入到消息列表开头(因为是历史消息)
messages.value.unshift(...formattedMessages.reverse())
// 更新游标为最旧的消息ID
oldestMessageId.value = historyList[historyList.length - 1].id
// 检查是否还有更多数据
hasMoreHistory.value = historyList.length === 20
} else {
hasMoreHistory.value = false
}
}
} finally {
loadingHistory.value = false
}
}
3. 游标分页UI展示
<template>
<div class="messages-container" ref="messagesContainer">
<!-- 加载更多按钮 -->
<div v-if="hasMoreHistory" class="load-more-container">
<a-button
type="link"
@click="loadMoreHistory"
:loading="loadingHistory"
size="small"
>
加载更多历史消息
</a-button>
</div>
<!-- 消息列表 -->
<div v-for="(message, index) in messages" :key="index" class="message-item">
<!-- 消息内容 -->
</div>
</div>
</template>
4. 后端游标分页接口设计
@PostMapping("/listByPage")
public BaseResponse<Page<ChatHistoryVO>> listChatHistoryByPage(
@RequestBody ChatHistoryQueryRequest request
) {
// 游标分页查询
QueryWrapper<ChatHistory> queryWrapper = QueryWrapper.create()
.eq(ChatHistory::getAppId, request.getAppId())
.lt(request.getCursor() != null, ChatHistory::getId, request.getCursor()) // 游标条件
.orderBy(ChatHistory::getCreateTime, false) // 按时间倒序
.limit(request.getPageSize()); // 限制条数
List<ChatHistory> records = chatHistoryService.list(queryWrapper);
return ResultUtils.success(new Page<>(records, records.size()));
}
5. 游标分页优势
- 性能优化:避免OFFSET深分页问题,查询性能稳定
- 数据一致性:即使有新消息插入,历史消息加载不会重复
- 无限滚动:支持平滑的历史消息加载体验
- 内存友好:按需加载,不会一次性加载所有历史记录
34. AI 零代码应用生成项目中,实现可视化编辑功能时,前端如何捕获并传递 iframe 内的用户点击事件给父页面?
基于项目代码分析,虽然当前代码中没有直接使用iframe,但可视化编辑功能的实现原理如下:
1. 跨域通信机制(postMessage)
// iframe内页面:发送点击事件到父页面
const handleElementClick = (event) => {
const element = event.target
// 构建元素信息
const elementInfo = {
type: 'ELEMENT_CLICKED',
data: {
tagName: element.tagName,
id: element.id,
className: element.className,
textContent: element.textContent?.substring(0, 100),
innerHTML: element.innerHTML,
selector: generateSelector(element),
boundingRect: element.getBoundingClientRect(),
styles: getComputedStyle(element)
}
}
// 发送到父页面
window.parent.postMessage(elementInfo, '*')
// 阻止默认行为和冒泡
event.preventDefault()
event.stopPropagation()
}
// 在iframe内页面添加点击监听
document.addEventListener('click', handleElementClick, true)
2. 父页面接收iframe消息
// AppChatPage.vue 中的消息接收逻辑
const setupIframeMessageListener = () => {
window.addEventListener('message', (event) => {
// 验证来源(安全检查)
if (!isValidOrigin(event.origin)) return
const { type, data } = event.data
if (type === 'ELEMENT_CLICKED' && isEditMode.value) {
// 更新选中元素信息
selectedElementInfo.value = {
tagName: data.tagName,
id: data.id,
className: data.className,
textContent: data.textContent,
selector: data.selector,
boundingRect: data.boundingRect,
styles: data.styles
}
// 显示选中元素面板
showSelectedElementPanel()
// 可选:高亮显示选中元素
highlightSelectedElement(data.selector)
}
})
}
3. 安全考虑
// 验证消息来源
const isValidOrigin = (origin: string) => {
const allowedOrigins = [
window.location.origin,
'http://localhost:8123',
// 其他允许的域名
]
return allowedOrigins.includes(origin)
}
// Content Security Policy 设置
const cspMeta = document.createElement('meta')
cspMeta.httpEquiv = 'Content-Security-Policy'
cspMeta.content = "frame-src 'self' http://localhost:8123"
document.head.appendChild(cspMeta)
4. 实际项目实现方式
根据AppChatPage.vue的代码,项目采用了更直接的方式:在同域名下展示生成的页面,通过直接的DOM操作来实现可视化编辑,避免了iframe跨域通信的复杂性。
这种方式的优势:
- 避免跨域问题
- 更好的性能表现
- 更简单的事件处理
- 更直接的DOM操作能力
35. AI 零代码应用生成项目中,当用户开启可视化编辑并选中元素后,前端如何生成稳定且唯一的选择器?
基于AppChatPage.vue的代码分析,元素选择器生成采用了多层级策略:
1. 选择器生成优先级策略
const generateElementSelector = (element: HTMLElement): string => {
// 第一优先级:ID选择器(最稳定)
if (element.id && element.id.trim() !== '') {
return `#${element.id}`
}
// 第二优先级:唯一class组合
if (element.className && element.className.trim() !== '') {
const classes = element.className.trim().split(/\s+/).filter(Boolean)
const classSelector = element.tagName.toLowerCase() + '.' + classes.join('.')
// 验证选择器唯一性
if (document.querySelectorAll(classSelector).length === 1) {
return classSelector
}
}
// 第三优先级:结构化路径选择器
return generatePathSelector(element)
}
2. 结构化路径选择器生成
const generatePathSelector = (element: HTMLElement): string => {
const path: string[] = []
let current = element
while (current && current.nodeType === Node.ELEMENT_NODE && current !== document.body) {
let selector = current.tagName.toLowerCase()
// 如果有ID,直接使用并终止路径构建
if (current.id) {
selector += `#${current.id}`
path.unshift(selector)
break
}
// 添加有意义的class
if (current.className) {
const classes = current.className.trim().split(/\s+/)
const meaningfulClasses = classes.filter(cls =>
// 过滤掉动态生成的class
!cls.match(/^(active|selected|hover|focus|\d+)$/) &&
cls.length > 2
)
if (meaningfulClasses.length > 0) {
selector += '.' + meaningfulClasses.slice(0, 2).join('.')
}
}
// 添加结构位置信息
const parent = current.parentElement
if (parent) {
const siblings = Array.from(parent.children).filter(
sibling => sibling.tagName === current.tagName
)
if (siblings.length > 1) {
const index = siblings.indexOf(current) + 1
selector += `:nth-of-type(${index})`
}
}
path.unshift(selector)
current = current.parentElement
}
return path.join(' > ')
}
3. 智能备用策略
const generateRobustSelector = (element: HTMLElement): string => {
const strategies = [
// 策略1:ID优先
(el) => el.id ? `#${el.id}` : null,
// 策略2:唯一属性组合
(el) => {
const attrs = ['data-id', 'data-key', 'data-testid', 'name']
for (const attr of attrs) {
const value = el.getAttribute(attr)
if (value) {
const selector = `${el.tagName.toLowerCase()}[${attr}="${value}"]`
if (document.querySelectorAll(selector).length === 1) {
return selector
}
}
}
return null
},
// 策略3:内容特征选择器
(el) => {
if (el.textContent && el.textContent.trim().length > 0 && el.textContent.length < 50) {
const text = el.textContent.trim().replace(/['"]/g, '')
const selector = `${el.tagName.toLowerCase()}:contains("${text}")`
return selector
}
return null
},
// 策略4:结构路径
generatePathSelector
]
for (const strategy of strategies) {
const selector = strategy(element)
if (selector && validateSelectorStability(selector, element)) {
return selector
}
}
// 最终备用策略:带序号的完整路径
return generateFullPathWithIndex(element)
}
4. 选择器优化特点
- 稳定性优先:优先使用ID等不易变化的属性
- 唯一性检查:确保选择器只能定位到一个元素
- 语义化友好:使用有意义的class名而非自动生成的
- 向后兼容:即使页面结构微调,选择器仍能正常工作
- 层级适度:避免过深的嵌套路径,提高可读性
36. AI 零代码应用生成项目中,哪些内容抽象成了可复用的 Vue 组件?举例说明你开发一个组件时的主要思路。
基于项目代码分析,项目中抽象了以下可复用的Vue组件:
1. 核心业务组件
AppCard.vue: 应用卡片展示组件AppDetailModal.vue: 应用详情弹窗组件DeploySuccessModal.vue: 部署成功提示组件MarkdownRenderer.vue: Markdown内容渲染组件CodeHighlight.vue: 代码高亮显示组件
2. 布局框架组件
GlobalHeader.vue: 全局头部导航组件GlobalFooter.vue: 全局底部组件UserInfo.vue: 用户信息展示组件
3. 以 CodeHighlight.vue 为例说明组件开发思路
a) 职责单一原则:
<!-- 专注于代码高亮显示功能 -->
<template>
<div class="code-highlight-container">
<div class="code-header">
<!-- 文件信息展示 -->
</div>
<div class="code-content">
<!-- 高亮代码内容 -->
</div>
</div>
</template>
b) Props接口设计:
interface Props {
code: string // 必需:代码内容
language?: string // 可选:编程语言
fileName?: string // 可选:文件名
theme?: 'github' | 'vs2015' // 可选:主题选择
}
c) 功能特性:
// 1. 智能语言检测
const highlightedCode = computed(() => {
if (props.language && hljs.getLanguage(props.language)) {
// 使用指定语言高亮
return hljs.highlight(props.code, { language: props.language }).value
}
// 自动检测语言
return hljs.highlightAuto(props.code).value
})
// 2. 一键复制功能
const copyCode = async () => {
await navigator.clipboard.writeText(props.code)
message.success('代码已复制到剪贴板')
}
// 3. 动态主题切换
const loadTheme = async (theme: string) => {
const link = document.createElement('link')
link.href = `https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/${theme}.min.css`
document.head.appendChild(link)
}
d) 样式设计考虑:
.code-highlight-container {
// 容器布局
border: 1px solid #e8e8e8;
border-radius: 8px;
overflow: hidden;
.code-header {
// 信息栏设计
display: flex;
justify-content: space-between;
background: #f8f9fa;
.language-badge {
// 语言标识
background: #1890ff;
color: white;
padding: 2px 6px;
border-radius: 4px;
}
}
// 响应式设计
@media (max-width: 768px) {
.code-content code {
font-size: 12px;
}
}
}
4. 组件开发最佳实践总结
a) 接口设计原则:
- Props类型化,提供默认值
- Events命名清晰,携带必要数据
- Slots预留扩展点
b) 功能实现策略:
- 使用computed保证响应性
- 错误边界处理
- 性能优化(如防抖、节流)
c) 样式方案:
- CSS Module或Scoped CSS
- 响应式设计
- 主题化支持
d) 可维护性考虑:
- 单一职责,功能内聚
- 文档完善,示例清晰
- 测试覆盖,边界用例
37. 你是如何设计前端路由的?对于需要管理员权限的页面,在前端层面做了哪些访问控制?
基于router/index.ts的分析,前端路由设计如下:
1. 路由结构设计
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
// 公开页面
{ path: '/', name: '主页', component: HomePage },
{ path: '/user/login', name: '用户登录', component: UserLoginPage },
{ path: '/user/register', name: '用户注册', component: UserRegisterPage },
// 用户功能页面
{ path: '/app/chat/:id', name: '应用对话', component: AppChatPage },
{ path: '/app/edit/:id', name: '编辑应用', component: AppEditPage },
// 管理员页面
{ path: '/admin/userManage', name: '用户管理', component: UserManagePage },
{ path: '/admin/appManage', name: '应用管理', component: AppManagePage },
{ path: '/admin/chatManage', name: '对话管理', component: ChatManagePage },
]
})
2. 路由级别权限控制
// 路由守卫实现权限控制
router.beforeEach(async (to, from, next) => {
const loginUserStore = useLoginUserStore()
// 获取当前用户信息
if (!loginUserStore.loginUser || !loginUserStore.loginUser.id) {
await loginUserStore.fetchLoginUser()
}
const currentUser = loginUserStore.loginUser
// 检查是否需要登录
if (needsAuth(to.path) && !currentUser.id) {
// 重定向到登录页,并保存目标页面
next({
path: '/user/login',
query: { redirect: to.fullPath }
})
return
}
// 检查管理员权限
if (isAdminRoute(to.path)) {
if (!hasAdminPermission(currentUser)) {
message.error('权限不足,无法访问该页面')
next('/') // 重定向到首页
return
}
}
next()
})
3. 导航菜单权限控制
<!-- GlobalHeader.vue 中的权限控制 -->
<template>
<a-menu mode="horizontal" :selectedKeys="selectedKeys">
<!-- 公共菜单项 -->
<a-menu-item key="home">
<router-link to="/">首页</router-link>
</a-menu-item>
<!-- 登录用户菜单 -->
<a-menu-item v-if="loginUser.id" key="apps">
<router-link to="/apps">我的应用</router-link>
</a-menu-item>
<!-- 管理员菜单 -->
<a-sub-menu v-if="isAdmin" key="admin" title="管理">
<a-menu-item key="userManage">
<router-link to="/admin/userManage">用户管理</router-link>
</a-menu-item>
<a-menu-item key="appManage">
<router-link to="/admin/appManage">应用管理</router-link>
</a-menu-item>
<a-menu-item key="chatManage">
<router-link to="/admin/chatManage">对话管理</router-link>
</a-menu-item>
</a-sub-menu>
</a-menu>
</template>
4. 路由设计优势
- 层次清晰:公开页面、用户页面、管理页面分层明确
- 参数化路由:支持动态参数(如
:id) - 权限分离:前端权限控制与后端接口权限双重保障
- 用户体验:登录重定向保持用户意图
- 安全考虑:敏感操作需要后端验证,前端仅做UI层面控制
38. AI 返回的 Markdown 内容包含代码块时,前端是如何解析并实现代码高亮的?
基于MarkdownRenderer.vue和CodeHighlight.vue的分析,代码高亮实现如下:
1. Markdown解析配置
import MarkdownIt from 'markdown-it'
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'
// 配置markdown-it实例
const md: MarkdownIt = new MarkdownIt({
html: true, // 允许HTML标签
linkify: true, // 自动识别链接
typographer: true, // 智能引号和其他符号
highlight: function (str: string, lang: string): string {
// 自定义代码高亮函数
if (lang && hljs.getLanguage(lang)) {
try {
return (
'<pre class="hljs"><code>' +
hljs.highlight(str, { language: lang, ignoreIllegals: true }).value +
'</code></pre>'
)
} catch {
// 高亮失败时的降级处理
}
}
// 默认处理:转义HTML
return '<pre class="hljs"><code>' + md.utils.escapeHtml(str) + '</code></pre>'
}
})
2. 渲染流程
// 计算渲染后的Markdown
const renderedMarkdown = computed(() => {
return md.render(props.content)
})
<template>
<!-- 使用v-html渲染,包含高亮的代码块 -->
<div class="markdown-content" v-html="renderedMarkdown"></div>
</template>
3. 代码高亮样式定制
.markdown-content {
// 代码块基础样式
:deep(pre) {
background-color: #f8f8f8;
border: 1px solid #e1e1e1;
border-radius: 6px;
padding: 1em;
overflow-x: auto;
margin: 1em 0;
}
:deep(pre code) {
background-color: transparent;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9em;
line-height: 1.4;
}
// 特定语言的代码块样式
:deep(.hljs-keyword) { color: #d73a49; font-weight: 600; }
:deep(.hljs-string) { color: #032f62; }
:deep(.hljs-comment) { color: #6a737d; font-style: italic; }
:deep(.hljs-number) { color: #005cc5; }
:deep(.hljs-function) { color: #6f42c1; }
}
4. 独立代码高亮组件
// 智能语言检测和高亮
const highlightedCode = computed(() => {
if (!props.code) return ''
// 优先使用指定语言
if (props.language && hljs.getLanguage(props.language)) {
try {
return hljs.highlight(props.code, {
language: props.language,
ignoreIllegals: true
}).value
} catch (error) {
console.warn('代码高亮失败:', error)
}
}
// 自动检测语言
try {
return hljs.highlightAuto(props.code).value
} catch (error) {
console.warn('自动检测语言失败:', error)
}
// 最终降级:HTML转义
return hljs.escapeHtml(props.code)
})
5. 完整的代码块处理流程
AI输出Markdown文本
↓
MarkdownIt解析(识别```代码块)
↓
highlight函数处理(语言检测+语法高亮)
↓
生成带高亮的HTML
↓
v-html渲染到DOM
↓
CSS样式美化显示
6. 性能优化和错误处理
- 降级策略:高亮失败时转义HTML显示
- 语言检测:支持自动检测和手动指定
- 缓存机制:计算属性缓存渲染结果
- 主题热切换:动态CSS注入,无需刷新页面
这种实现方式确保了AI返回的代码内容能够以美观、易读的方式展示给用户,支持多种编程语言的语法高亮。
📞 联系我们
⭐ Star History ⭐
如果这个项目对你有帮助,请给我们一个 Star!

摘要:AI零代码应用生成项目中的前后端交互实现 该项目实现了可视化修改功能,后端通过LangChain4j工具系统(文件读写/修改工具)和DOM解析技术定位并修改指定元素,同时设有路径白名单等安全机制。前端采用游标分页管理对话历史,通过API请求按需加载历史消息,并优化了分页性能和数据一致性。可视化编辑功能利用postMessage实现跨域通信,iframe捕获用户点击事件并传递元素信息至父页面,父页面通过事件监听接收处理。整体架构注重安全性和用户体验,实现了前后端的高效协作。
浙公网安备 33010602011771号