Commit 8c6ba975 authored by ligaowei's avatar ligaowei

refactor(backend): 重构用户上下文处理和工具管理逻辑

重构UserUtils使用ThreadLocal存储用户ID以支持异步线程
简化MetaObjectHandlerConfig的用户ID获取逻辑
移除SseTokenEmitter的@Component注解并重构构造函数
优化AgentToolManager的代码结构和日志记录
重构AsyncUserContextDecorator以增强线程上下文传播

refactor(frontend): 简化表单渲染组件并移除pangea-ui依赖
parent 0306580c
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -29,7 +29,6 @@ public class AgentChatService { ...@@ -29,7 +29,6 @@ public class AgentChatService {
private final AgentToolManager agentToolManager; private final AgentToolManager agentToolManager;
private final UserSseService userSseService; private final UserSseService userSseService;
private final pangea.hiagent.web.service.AgentService agentService; private final pangea.hiagent.web.service.AgentService agentService;
private final SseTokenEmitter sseTokenEmitter;
public AgentChatService( public AgentChatService(
EventService eventService, EventService eventService,
...@@ -37,14 +36,12 @@ public class AgentChatService { ...@@ -37,14 +36,12 @@ public class AgentChatService {
AgentProcessorFactory agentProcessorFactory, AgentProcessorFactory agentProcessorFactory,
AgentToolManager agentToolManager, AgentToolManager agentToolManager,
UserSseService userSseService, UserSseService userSseService,
pangea.hiagent.web.service.AgentService agentService, pangea.hiagent.web.service.AgentService agentService) {
SseTokenEmitter sseTokenEmitter) {
this.errorHandlerService = errorHandlerService; this.errorHandlerService = errorHandlerService;
this.agentProcessorFactory = agentProcessorFactory; this.agentProcessorFactory = agentProcessorFactory;
this.agentToolManager = agentToolManager; this.agentToolManager = agentToolManager;
this.userSseService = userSseService; this.userSseService = userSseService;
this.agentService = agentService; this.agentService = agentService;
this.sseTokenEmitter = sseTokenEmitter;
} }
// /** // /**
...@@ -182,7 +179,7 @@ public class AgentChatService { ...@@ -182,7 +179,7 @@ public class AgentChatService {
AgentRequest request = chatRequest.toAgentRequest(agent.getId(), agent, agentToolManager); AgentRequest request = chatRequest.toAgentRequest(agent.getId(), agent, agentToolManager);
// 创建新的SseTokenEmitter实例 // 创建新的SseTokenEmitter实例
SseTokenEmitter tokenEmitter = sseTokenEmitter.createNewInstance(emitter, agent, request, userId, this::handleCompletion); SseTokenEmitter tokenEmitter = new SseTokenEmitter(userSseService, emitter, agent, request, userId, this::handleCompletion);
// 处理流式请求 // 处理流式请求
processor.processStreamRequest(request, agent, userId, tokenEmitter); processor.processStreamRequest(request, agent, userId, tokenEmitter);
......
package pangea.hiagent.agent.service; package pangea.hiagent.agent.service;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import pangea.hiagent.model.Agent; import pangea.hiagent.model.Agent;
...@@ -13,7 +13,6 @@ import pangea.hiagent.web.dto.AgentRequest; ...@@ -13,7 +13,6 @@ import pangea.hiagent.web.dto.AgentRequest;
* 无状态设计,每次使用时创建新实例 * 无状态设计,每次使用时创建新实例
*/ */
@Slf4j @Slf4j
@Component
public class SseTokenEmitter implements TokenConsumerWithCompletion { public class SseTokenEmitter implements TokenConsumerWithCompletion {
private final UserSseService userSseService; private final UserSseService userSseService;
...@@ -47,6 +46,13 @@ public class SseTokenEmitter implements TokenConsumerWithCompletion { ...@@ -47,6 +46,13 @@ public class SseTokenEmitter implements TokenConsumerWithCompletion {
/** /**
* 无参构造函数,用于Spring容器初始化 * 无参构造函数,用于Spring容器初始化
*/ */
public SseTokenEmitter() {
this(null, null, null, null, null, null);
}
/**
* 构造函数,用于Spring容器初始化(带UserSseService参数)
*/
public SseTokenEmitter(UserSseService userSseService) { public SseTokenEmitter(UserSseService userSseService) {
this(userSseService, null, null, null, null, null); this(userSseService, null, null, null, null, null);
} }
......
...@@ -104,30 +104,22 @@ public class MetaObjectHandlerConfig implements MetaObjectHandler { ...@@ -104,30 +104,22 @@ public class MetaObjectHandlerConfig implements MetaObjectHandler {
/** /**
* 获取当前用户ID,支持异步线程上下文 * 获取当前用户ID,支持异步线程上下文
* 该方法支持以下场景: * 该方法支持以下场景:
* 1. 同步请求:从SecurityContext获取用户ID * 1. 优先从ThreadLocal获取(支持异步线程)
* 2. 异步任务:从AsyncUserContextDecorator传播的上下文获取用户ID * 2. 从SecurityContext获取(支持同步请求和AsyncUserContextDecorator传播)
* 3. 故障转移:尝试直接解析Token获取用户ID * 3. 从请求中解析Token获取用户ID
* *
* @return 用户ID,如果无法获取则返回null * @return 用户ID,如果无法获取则返回null
*/ */
private String getCurrentUserIdWithContext() { private String getCurrentUserIdWithContext() {
try { try {
// 方式1:首先尝试从SecurityContext获取(支持同步请求和AsyncUserContextDecorator传播) // 直接调用UserUtils.getCurrentUserId(),该方法已经包含了所有获取用户ID的方式
// 并且优先从ThreadLocal获取,支持异步线程
String userId = UserUtils.getCurrentUserId(); String userId = UserUtils.getCurrentUserId();
if (userId != null) { if (userId != null) {
log.debug("通过SecurityContext成功获取用户ID: {}", userId); log.debug("成功获取用户ID: {}", userId);
return userId; return userId;
} }
log.debug("无法从SecurityContext获取用户ID,可能是异步线程且未使用AsyncUserContextDecorator包装");
// 方式2:尝试直接从请求中解析Token(故障转移)
String asyncUserId = UserUtils.getCurrentUserIdInAsync();
if (asyncUserId != null) {
log.debug("通过直接解析Token成功获取用户ID: {}", asyncUserId);
return asyncUserId;
}
log.warn("无法通过任何方式获取当前用户ID,createdBy/updatedBy字段将不被填充"); log.warn("无法通过任何方式获取当前用户ID,createdBy/updatedBy字段将不被填充");
return null; return null;
} catch (Exception e) { } catch (Exception e) {
......
...@@ -98,17 +98,25 @@ public class AsyncUserContextDecorator { ...@@ -98,17 +98,25 @@ public class AsyncUserContextDecorator {
public static Runnable wrapWithContext(Runnable runnable) { public static Runnable wrapWithContext(Runnable runnable) {
// 捕获当前线程的用户上下文 // 捕获当前线程的用户上下文
UserContextHolder userContext = captureUserContext(); UserContextHolder userContext = captureUserContext();
// 同时捕获当前线程的用户ID(用于ThreadLocal传播)
String currentUserId = UserUtils.getCurrentUserId();
return () -> { return () -> {
try { try {
// 在异步线程中传播用户上下文 // 在异步线程中传播用户上下文
propagateUserContext(userContext); propagateUserContext(userContext);
// 将用户ID设置到ThreadLocal中,增强可靠性
if (currentUserId != null) {
UserUtils.setCurrentUserId(currentUserId);
}
// 执行原始任务 // 执行原始任务
runnable.run(); runnable.run();
} finally { } finally {
// 清理当前线程的用户上下文 // 清理当前线程的用户上下文
clearUserContext(); clearUserContext();
// 清理ThreadLocal中的用户ID
UserUtils.clearCurrentUserId();
} }
}; };
} }
...@@ -122,17 +130,25 @@ public class AsyncUserContextDecorator { ...@@ -122,17 +130,25 @@ public class AsyncUserContextDecorator {
public static <V> Callable<V> wrapWithContext(Callable<V> callable) { public static <V> Callable<V> wrapWithContext(Callable<V> callable) {
// 捕获当前线程的用户上下文 // 捕获当前线程的用户上下文
UserContextHolder userContext = captureUserContext(); UserContextHolder userContext = captureUserContext();
// 同时捕获当前线程的用户ID(用于ThreadLocal传播)
String currentUserId = UserUtils.getCurrentUserId();
return () -> { return () -> {
try { try {
// 在异步线程中传播用户上下文 // 在异步线程中传播用户上下文
propagateUserContext(userContext); propagateUserContext(userContext);
// 将用户ID设置到ThreadLocal中,增强可靠性
if (currentUserId != null) {
UserUtils.setCurrentUserId(currentUserId);
}
// 执行原始任务 // 执行原始任务
return callable.call(); return callable.call();
} finally { } finally {
// 清理当前线程的用户上下文 // 清理当前线程的用户上下文
clearUserContext(); clearUserContext();
// 清理ThreadLocal中的用户ID
UserUtils.clearCurrentUserId();
} }
}; };
} }
......
...@@ -22,18 +22,70 @@ public class UserUtils { ...@@ -22,18 +22,70 @@ public class UserUtils {
// 注入JwtUtil bean // 注入JwtUtil bean
private static JwtUtil jwtUtil; private static JwtUtil jwtUtil;
// 使用InheritableThreadLocal存储用户ID,支持异步线程继承
private static final InheritableThreadLocal<String> USER_ID_THREAD_LOCAL = new InheritableThreadLocal<>();
public UserUtils(JwtUtil jwtUtil) { public UserUtils(JwtUtil jwtUtil) {
UserUtils.jwtUtil = jwtUtil; UserUtils.jwtUtil = jwtUtil;
} }
/**
* 设置当前线程的用户ID
* @param userId 用户ID
*/
public static void setCurrentUserId(String userId) {
if (StringUtils.hasText(userId)) {
USER_ID_THREAD_LOCAL.set(userId);
log.debug("设置当前线程的用户ID: {}", userId);
} else {
USER_ID_THREAD_LOCAL.remove();
log.debug("清除当前线程的用户ID");
}
}
/**
* 清除当前线程的用户ID
*/
public static void clearCurrentUserId() {
USER_ID_THREAD_LOCAL.remove();
log.debug("清除当前线程的用户ID");
}
/**
* 从ThreadLocal获取用户ID
* @return 用户ID,如果不存在则返回null
*/
public static String getCurrentUserIdFromThreadLocal() {
String userId = USER_ID_THREAD_LOCAL.get();
if (userId != null) {
log.debug("从ThreadLocal获取到用户ID: {}", userId);
}
return userId;
}
public static String getCurrentUserId() { public static String getCurrentUserId() {
// 优先从ThreadLocal获取(支持异步线程)
String userId = getCurrentUserIdFromThreadLocal();
if (userId != null) {
return userId;
}
String username = getCurrentUserIdInSync(); // 从同步上下文获取
if (username==null || username.isEmpty()) { userId = getCurrentUserIdInSync();
username = getCurrentUserIdInAsync(); if (userId != null) {
// 将获取到的用户ID存入ThreadLocal,供后续异步操作使用
setCurrentUserId(userId);
return userId;
} }
return username; // 从异步上下文获取
userId = getCurrentUserIdInAsync();
if (userId != null) {
// 将获取到的用户ID存入ThreadLocal,供后续异步操作使用
setCurrentUserId(userId);
}
return userId;
} }
/** /**
......
...@@ -12,6 +12,7 @@ import pangea.hiagent.web.service.ToolService; ...@@ -12,6 +12,7 @@ import pangea.hiagent.web.service.ToolService;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -38,37 +39,29 @@ public class AgentToolManager { ...@@ -38,37 +39,29 @@ public class AgentToolManager {
* @return 工具列表 * @return 工具列表
*/ */
public List<Tool> getAvailableTools(Agent agent) { public List<Tool> getAvailableTools(Agent agent) {
try {
log.info("获取Agent可用工具列表,Agent ID: {}, 名称: {}", agent.getId(), agent.getName()); log.info("获取Agent可用工具列表,Agent ID: {}, 名称: {}", agent.getId(), agent.getName());
// 获取与Agent关联的Tool ID列表 // 获取与Agent关联的Tool ID列表
List<String> toolIds = agentToolRelationRepository.getToolIdsByAgentId(agent.getId()); List<String> toolIds = agentToolRelationRepository.getToolIdsByAgentId(agent.getId());
log.info("Agent关联的工具ID数量: {}", toolIds != null ? toolIds.size() : 0); log.info("Agent关联的工具ID数量: {}", toolIds != null ? toolIds.size() : 0);
if (toolIds == null || toolIds.isEmpty()) {
// 如果没有关联特定工具,则返回该用户的所有活跃工具 // 如果没有关联特定工具,则返回该用户的所有活跃工具
if (toolIds == null || toolIds.isEmpty()) {
List<Tool> allTools = toolService.getUserToolsByStatus(agent.getOwner(), "active"); List<Tool> allTools = toolService.getUserToolsByStatus(agent.getOwner(), "active");
log.info("返回用户所有活跃工具,数量: {}", allTools != null ? allTools.size() : 0); log.info("返回用户所有活跃工具,数量: {}", allTools != null ? allTools.size() : 0);
return allTools != null ? allTools : List.of(); return allTools != null ? allTools : List.of();
} }
// 根据Tool ID获取具体的Tool对象 // 根据Tool ID获取具体的Tool对象
List<Tool> tools = new ArrayList<>(); List<Tool> tools = toolIds.stream()
for (String toolId : toolIds) { .map(toolService::getById)
Tool tool = toolService.getById(toolId); .filter(Objects::nonNull)
if (tool != null) { .collect(Collectors.toList());
tools.add(tool);
}
}
log.info("获取到的具体工具数量: {}", tools.size()); log.info("获取到的具体工具数量: {}", tools.size());
tools.forEach(tool -> log.info("工具名称: {}", tool.getName())); log.debug("工具列表: {}", tools.stream().map(Tool::getName).collect(Collectors.joining(", ")));
return tools; return tools;
} catch (Exception e) {
log.error("获取Agent可用工具时发生错误", e);
return List.of();
}
} }
/** /**
...@@ -100,34 +93,18 @@ public class AgentToolManager { ...@@ -100,34 +93,18 @@ public class AgentToolManager {
* @return 筛选后的工具实例列表 * @return 筛选后的工具实例列表
*/ */
public List<Object> filterToolsByInstances(List<Object> allTools, Set<String> toolNames) { public List<Object> filterToolsByInstances(List<Object> allTools, Set<String> toolNames) {
log.debug("开始筛选工具实例,工具名称集合: {}", toolNames);
if (toolNames == null || toolNames.isEmpty()) { if (toolNames == null || toolNames.isEmpty()) {
log.debug("工具名称集合为空,返回所有工具实例");
return allTools; return allTools;
} }
List<Object> filteredTools = allTools.stream() return allTools.stream()
.filter(tool -> { .filter(tool -> {
// 获取工具类名(不含包名)
String className = tool.getClass().getSimpleName(); String className = tool.getClass().getSimpleName();
log.debug("检查工具类: {}", className); return toolNames.contains(className) ||
// 检查类名是否匹配
boolean isMatch = toolNames.contains(className) ||
toolNames.stream().anyMatch(name -> toolNames.stream().anyMatch(name ->
className.toLowerCase().contains(name.toLowerCase())); className.toLowerCase().contains(name.toLowerCase()));
if (isMatch) {
log.debug("工具 {} 匹配成功", className);
}
return isMatch;
}) })
.collect(Collectors.toList()); .collect(Collectors.toList());
log.debug("筛选完成,返回 {} 个工具实例", filteredTools.size());
return filteredTools;
} }
/** /**
...@@ -143,8 +120,9 @@ public class AgentToolManager { ...@@ -143,8 +120,9 @@ public class AgentToolManager {
StringBuilder description = new StringBuilder(); StringBuilder description = new StringBuilder();
for (int i = 0; i < tools.size(); i++) { for (int i = 0; i < tools.size(); i++) {
Tool tool = tools.get(i); Tool tool = tools.get(i);
description.append(i + 1).append(". "); description.append(i + 1).append(". ")
description.append(tool.getName()); .append(tool.getName());
if (hasValue(tool.getDisplayName())) { if (hasValue(tool.getDisplayName())) {
description.append(" - ").append(tool.getDisplayName()); description.append(" - ").append(tool.getDisplayName());
} }
...@@ -168,20 +146,11 @@ public class AgentToolManager { ...@@ -168,20 +146,11 @@ public class AgentToolManager {
/** /**
* 获取Bean的原始目标类(穿透Spring AOP代理) * 获取Bean的原始目标类(穿透Spring AOP代理)
*
* 用于处理以下场景:
* 1. Bean被Spring AOP代理,需要获取原始类信息
* 2. 获取原始类的方法和字段信息
* 3. 进行类型检查和反射操作
*
* @param bean Bean实例(可能是代理对象) * @param bean Bean实例(可能是代理对象)
* @return 原始目标类的Class对象 * @return 原始目标类的Class对象
*/ */
private Class<?> getTargetClass(Object bean) { private Class<?> getTargetClass(Object bean) {
if (bean == null) { return bean == null ? null : AopUtils.getTargetClass(bean);
return null;
}
return AopUtils.getTargetClass(bean);
} }
/** /**
...@@ -209,18 +178,14 @@ public class AgentToolManager { ...@@ -209,18 +178,14 @@ public class AgentToolManager {
return result; return result;
} }
try {
log.debug("[{}] 根据原始类名'{}' 查找工具实例,精确匹配: {}", agent.getName(), originalClassName, isExactMatch);
List<Tool> availableTools = getAvailableTools(agent); List<Tool> availableTools = getAvailableTools(agent);
for (Tool tool : availableTools) { for (Tool tool : availableTools) {
try {
if (tool.getBeanName() == null || tool.getBeanName().trim().isEmpty()) { if (tool.getBeanName() == null || tool.getBeanName().trim().isEmpty()) {
continue; continue;
} }
Object bean = null; Object bean;
try { try {
bean = applicationContext.getBean(tool.getBeanName()); bean = applicationContext.getBean(tool.getBeanName());
} catch (Exception e) { } catch (Exception e) {
...@@ -242,31 +207,16 @@ public class AgentToolManager { ...@@ -242,31 +207,16 @@ public class AgentToolManager {
String targetFullClassName = targetClass.getName(); String targetFullClassName = targetClass.getName();
// 根据匹配模式进行判断 // 根据匹配模式进行判断
boolean matches = false; boolean matches = isExactMatch
if (isExactMatch) { ? originalClassName.equals(targetClassName) || originalClassName.equals(targetFullClassName)
// 精确匹配:检查简单类名和完整类名 : targetClassName.toLowerCase().contains(originalClassName.toLowerCase()) ||
matches = originalClassName.equals(targetClassName) ||
originalClassName.equals(targetFullClassName);
} else {
// 模糊匹配:检查是否包含(不区分大小写)
matches = targetClassName.toLowerCase().contains(originalClassName.toLowerCase()) ||
targetFullClassName.toLowerCase().contains(originalClassName.toLowerCase()); targetFullClassName.toLowerCase().contains(originalClassName.toLowerCase());
}
if (matches) { if (matches) {
result.add(bean); result.add(bean);
log.debug("[{}] 根据原始类名'{}' 匹配到工具实例: {}", agent.getName(), originalClassName, targetClassName);
}
} catch (Exception e) {
log.debug("[{}] 处理工具'{}' 时出错", agent.getName(), tool.getName(), e);
} }
} }
log.debug("[{}] 根据原始类名'{}' 共找到 {} 个工具实例", agent.getName(), originalClassName, result.size());
} catch (Exception e) {
log.error("[{}] 根据原始类名查找工具实例时发生错误", agent.getName(), e);
}
return result; return result;
} }
...@@ -316,7 +266,6 @@ public class AgentToolManager { ...@@ -316,7 +266,6 @@ public class AgentToolManager {
* @return 工具实例列表(包含AOP代理后的实例) * @return 工具实例列表(包含AOP代理后的实例)
*/ */
public List<Object> getAvailableToolInstances(Agent agent) { public List<Object> getAvailableToolInstances(Agent agent) {
try {
log.info("[{}] 开始获取可用的工具实例", agent.getName()); log.info("[{}] 开始获取可用的工具实例", agent.getName());
// 获取Agent可用的工具定义 // 获取Agent可用的工具定义
...@@ -328,7 +277,6 @@ public class AgentToolManager { ...@@ -328,7 +277,6 @@ public class AgentToolManager {
// 遍历每个工具定义,根据beanName查找Spring Bean实例 // 遍历每个工具定义,根据beanName查找Spring Bean实例
for (Tool tool : availableTools) { for (Tool tool : availableTools) {
try {
// 验证beanName是否为空 // 验证beanName是否为空
if (tool.getBeanName() == null || tool.getBeanName().trim().isEmpty()) { if (tool.getBeanName() == null || tool.getBeanName().trim().isEmpty()) {
log.warn("[{}] 工具'{}' 没有配置beanName,跳过此工具", agent.getName(), tool.getName()); log.warn("[{}] 工具'{}' 没有配置beanName,跳过此工具", agent.getName(), tool.getName());
...@@ -337,7 +285,7 @@ public class AgentToolManager { ...@@ -337,7 +285,7 @@ public class AgentToolManager {
} }
// 根据beanName查找Bean实例 // 根据beanName查找Bean实例
Object bean = null; Object bean;
try { try {
bean = applicationContext.getBean(tool.getBeanName()); bean = applicationContext.getBean(tool.getBeanName());
} catch (Exception e) { } catch (Exception e) {
...@@ -357,10 +305,6 @@ public class AgentToolManager { ...@@ -357,10 +305,6 @@ public class AgentToolManager {
log.warn("[{}] 工具'{}' 的Bean实例为null", agent.getName(), tool.getName()); log.warn("[{}] 工具'{}' 的Bean实例为null", agent.getName(), tool.getName());
failedBeans.add(tool.getName() + " (bean实例为null)"); failedBeans.add(tool.getName() + " (bean实例为null)");
} }
} catch (Exception e) {
log.error("[{}] 处理工具'{}' 时发生意外错误,详细信息", agent.getName(), tool.getName(), e);
failedBeans.add(tool.getName() + " (异常: " + e.getMessage() + ")");
}
} }
log.info("[{}] 成功获取了{}个工具实例", agent.getName(), toolInstances.size()); log.info("[{}] 成功获取了{}个工具实例", agent.getName(), toolInstances.size());
...@@ -371,9 +315,5 @@ public class AgentToolManager { ...@@ -371,9 +315,5 @@ public class AgentToolManager {
} }
return toolInstances; return toolInstances;
} catch (Exception e) {
log.error("[{}] 获取可用的工具实例时发生了意外错误", agent.getName(), e);
return List.of();
}
} }
} }
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -19,7 +19,7 @@ ...@@ -19,7 +19,7 @@
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"marked": "^17.0.1", "marked": "^17.0.1",
"pako": "^2.1.0", "pako": "^2.1.0",
"pangea-ui": "^0.14.2-beta.9",
"pinia": "^2.1.7", "pinia": "^2.1.7",
"snabbdom": "^3.6.3", "snabbdom": "^3.6.3",
"vue": "^3.4.0", "vue": "^3.4.0",
......
<template> <template>
<hi-page-template <div class="form-container">
ref="templateRef" <h2>表单渲染器(已简化)</h2>
:json="json" <div class="form-content">
:open-intl="false" <div class="form-field">
></hi-page-template> <label>输入框</label>
<el-input v-model="formData.input" placeholder="请输入"></el-input>
</div>
<div class="form-field">
<label>日期</label>
<el-date-picker v-model="formData.date" type="date" placeholder="选择日期"></el-date-picker>
</div>
</div>
<div class="button-wrap"> <div class="button-wrap">
<a-button type="primary" @click="submit">提交</a-button> <el-button type="primary" @click="submit">提交</el-button>
</div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref, reactive } from "vue";
import HiPageTemplate from "pangea-ui/hi-page-template"; import { ElInput, ElDatePicker, ElButton } from "element-plus";
const templateRef = ref(); const formData = reactive({
input: '',
date: null
});
const submit = () => { const submit = () => {
templateRef.value?.ctx.validate(1, (res, data) => { console.log('表单数据:', formData);
console.log(res, data); // 这里可以添加表单验证逻辑
});
};
const json = {
pages: [
{
key: 0,
type: "default",
name: "默认页",
code: "",
display: "",
props: {
margin: "16px",
padding: "12px",
backgroundColor: "white",
display: {},
},
bindProps: {},
coms: [
{
key: 1,
type: "node",
name: "表单容器",
code: "HiFormContainer",
display: "",
props: {
status: "default",
backgroundColor: "transparent",
layout: "horizontal",
size: "medium",
labelAlign: "right",
display: {},
borderRadius: {},
boxShadow: {},
loop: {
data: [],
},
},
bindProps: {},
coms: [
{
key: 1766473421208,
name: "输入框",
code: "HiInput",
props: {
title: "输入框",
status: "default",
placeholder: "请输入",
name: "INPUT_6CP8HIBK",
},
bindProps: {},
coms: [],
},
{
key: 1766476676439,
name: "日期",
code: "HiDatePicker",
props: {
title: "日期",
type: "date",
format: "YYYY-MM-DD",
status: "default",
name: "DATE_PA9TUPQQ",
},
bindProps: {},
},
],
},
],
},
],
params: [],
apis: [],
funcs: [],
pageTemplate: {},
}; };
</script> </script>
<style scoped> <style scoped>
.form-container {
background-color: white;
padding: 16px;
margin: 16px;
border-radius: 8px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.form-container h2 {
margin-top: 0;
margin-bottom: 16px;
color: #333;
}
.form-content {
margin-bottom: 16px;
}
.form-field {
margin-bottom: 16px;
}
.form-field label {
display: block;
margin-bottom: 8px;
font-weight: 500;
color: #606266;
}
.button-wrap { .button-wrap {
display: flex; display: flex;
justify-content: center; justify-content: center;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment