Commit fb1f853d authored by 高如斌's avatar 高如斌

模拟后端返回formJson渲染逻辑实现

parent 8a629996
......@@ -15,6 +15,7 @@
"dompurify": "^3.3.1",
"element-plus": "^2.4.0",
"highlight.js": "^11.9.0",
"lodash-es": "^4.17.21",
"marked": "^17.0.1",
"pako": "^2.1.0",
"pangea-ui": "^0.14.2-beta.9",
......@@ -4739,7 +4740,7 @@
},
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"resolved": "http://nexus.hisense.com/repository/npm-public/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT"
},
......
......@@ -17,6 +17,7 @@
"dompurify": "^3.3.1",
"element-plus": "^2.4.0",
"highlight.js": "^11.9.0",
"lodash-es": "^4.17.21",
"marked": "^17.0.1",
"pako": "^2.1.0",
"pangea-ui": "^0.14.2-beta.9",
......
......@@ -88,6 +88,7 @@ import { ref, nextTick, onMounted, defineExpose } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import MessageItem from "./MessageItem.vue";
import request from "@/utils/request";
import { useFormStore } from "@/stores/form";
import { useRoute } from "vue-router";
interface Message {
......@@ -112,9 +113,12 @@ const messages = ref<Message[]>([]);
const inputMessage = ref("");
const isLoading = ref(false);
const messagesContainer = ref<HTMLElement>();
const formJson = ref<any>(null);
// 获取当前路由
const route = useRoute();
// 表单store
const formStore = useFormStore();
// 全局维护SSE流超时计时器引用,确保能够正确清除
let streamTimeoutTimer: ReturnType<typeof setTimeout> | null = null;
......@@ -649,6 +653,37 @@ const processSSELine = async (
break;
}
// 模拟后端返回form类型的时间,并将form表单的json存到store
formJson.value = {
coms: [
{
key: 1766473421208,
name: "输入框",
code: "HiInput",
props: {
title: "输入框",
status: "default",
placeholder: "请输入",
name: "INPUT_6CP8HIBK",
},
bindProps: {},
},
{
key: 1766476676439,
name: "日期",
code: "HiDatePicker",
props: {
title: "日期",
type: "date",
format: "YYYY-MM-DD",
status: "default",
name: "DATE_PA9TUPQQ",
},
bindProps: {},
},
],
};
// 重置当前事件类型
currentEventRef.value = "";
} catch (err) {
......@@ -740,7 +775,7 @@ const sendMessage = async () => {
const decoder = new TextDecoder();
let buffer = "";
let isStreamComplete = false; // 标记流是否已完成
const STREAM_TIMEOUT = 60000; // 60秒无流式消息则为超时
const STREAM_TIMEOUT = 120000; // 120秒无流式消息则为超时
// 设置超时检查
const resetStreamTimeout = () => {
......@@ -832,6 +867,10 @@ const sendMessage = async () => {
messages.value[aiMessageIndex].content = accumulatedContentRef.value;
}
if (formJson.value) {
formStore.openForm(formJson.value);
}
// 确保最终状态正确
messages.value[aiMessageIndex].isStreaming = false;
// 设置isLoading为false,结束加载状态
......
<template>
<div v-if="formStore.showForm">
<hi-page-template
ref="templateRef"
:json="json"
:json="formStore.formJson"
:open-intl="false"
></hi-page-template>
<div class="button-wrap">
<a-button type="primary" @click="submit">提交</a-button>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref } from "vue";
import { useFormStore } from "@/stores/form";
import HiPageTemplate from "pangea-ui/hi-page-template";
// 获取表单 store
const formStore = useFormStore();
// 表单组件ref
const templateRef = ref();
// 表单提交回调
const submit = () => {
templateRef.value?.ctx.validate(1, (res, data) => {
templateRef.value?.ctx.validate(1, (res: boolean, data: any) => {
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>
<style scoped>
.button-wrap {
......
/*
* @Description: 表单页store
* @Author: gaorubin
* @Date: 2025-12-24 16:12:58
* @LastEditors: gaorubin
* @LastEditTime: 2025-12-24 17:26:54
*/
import { defineStore } from "pinia";
import { cloneDeep } from "lodash-es";
// 初始页面json外层
const INIT_FORM_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: [],
},
],
},
],
params: [],
apis: [],
funcs: [],
pageTemplate: {},
};
export const useFormStore = defineStore("form", {
state: () => ({
// 表单 JSON 配置(使用深拷贝避免共享引用)
formJson: cloneDeep(INIT_FORM_JSON),
// 是否显示表单页面
showForm: false,
}),
actions: {
/**
* 设置表单 JSON 配置
* @param json 表单配置对象
*/
setFormJson(json: any) {
this.formJson = json;
},
/**
* 清空表单 JSON 配置
*/
clearFormJson() {
this.formJson = cloneDeep(INIT_FORM_JSON);
},
/**
* 显示表单页面
* @param json 可选的表单配置对象,格式为 { coms: [] }
*/
openForm(json?: any) {
if (json) {
// 验证 json 的有效性
if (!json.coms || !Array.isArray(json.coms)) {
console.error("无效的表单配置:缺少 coms 数组", json);
return;
}
// 深拷贝 INIT_FORM_JSON 避免修改原始对象
const formConfig = cloneDeep(INIT_FORM_JSON);
// 将接收到的 coms 放入表单容器的 coms 数组中
// 表单容器位于 pages[0].coms[0](HiFormContainer)
if (
formConfig.pages &&
formConfig.pages[0] &&
formConfig.pages[0].coms &&
formConfig.pages[0].coms[0]
) {
formConfig.pages[0].coms[0].coms = json.coms;
} else {
console.error("INIT_FORM_JSON 结构异常,无法找到表单容器");
return;
}
// 设置表单配置
this.setFormJson(formConfig);
this.showForm = true;
}
},
/**
* 隐藏表单页面
*/
closeForm() {
this.showForm = false;
this.clearFormJson();
},
},
});
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
/*
* @Description:
* @Author: gaorubin
* @Date: 2025-12-22 14:30:43
* @LastEditors: gaorubin
* @LastEditTime: 2025-12-24 18:11:39
*/
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import path from "path";
export default defineConfig({
plugins: [vue()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src')
}
"@": path.resolve(__dirname, "./src"),
},
},
server: {
// port: 8081, // 注释掉固定端口,让Vite自动选择可用端口
......@@ -15,40 +22,42 @@ export default defineConfig({
fs: {
// 允许访问文件系统,解决Windows平台上的动态导入问题
allow: [
'..',
"..",
// 明确允许项目根目录
path.resolve(__dirname),
// 明确允许工作区根目录
path.resolve(__dirname, '..')
path.resolve(__dirname, ".."),
],
// 禁用严格的文件系统限制
strict: false
strict: false,
},
// 添加headers配置以允许iframe加载
headers: {
'X-Frame-Options': 'SAMEORIGIN'
"X-Frame-Options": "SAMEORIGIN",
},
proxy: {
'/api': {
target: 'http://localhost:8080',
"/api": {
target: "http://localhost:8080",
// target: "http://agent-backend.clouddev.hisense.com",
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/api')
rewrite: (path) => path.replace(/^\/api/, "/api"),
},
'/ws': {
target: 'http://localhost:8080',
"/ws": {
target: "http://localhost:8080",
// target: "http://agent-backend.clouddev.hisense.com",
ws: true, // 启用WebSocket代理
changeOrigin: true
}
}
changeOrigin: true,
},
},
},
build: {
target: 'esnext',
minify: 'terser',
sourcemap: false
target: "esnext",
minify: "terser",
sourcemap: false,
},
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment',
tsconfigRaw: '{}'
}
})
\ No newline at end of file
jsxFactory: "h",
jsxFragment: "Fragment",
tsconfigRaw: "{}",
},
});
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