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

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

parent 8a629996
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
"dompurify": "^3.3.1", "dompurify": "^3.3.1",
"element-plus": "^2.4.0", "element-plus": "^2.4.0",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"lodash-es": "^4.17.21",
"marked": "^17.0.1", "marked": "^17.0.1",
"pako": "^2.1.0", "pako": "^2.1.0",
"pangea-ui": "^0.14.2-beta.9", "pangea-ui": "^0.14.2-beta.9",
...@@ -4739,7 +4740,7 @@ ...@@ -4739,7 +4740,7 @@
}, },
"node_modules/lodash-es": { "node_modules/lodash-es": {
"version": "4.17.21", "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==", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"license": "MIT" "license": "MIT"
}, },
......
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
"dompurify": "^3.3.1", "dompurify": "^3.3.1",
"element-plus": "^2.4.0", "element-plus": "^2.4.0",
"highlight.js": "^11.9.0", "highlight.js": "^11.9.0",
"lodash-es": "^4.17.21",
"marked": "^17.0.1", "marked": "^17.0.1",
"pako": "^2.1.0", "pako": "^2.1.0",
"pangea-ui": "^0.14.2-beta.9", "pangea-ui": "^0.14.2-beta.9",
......
...@@ -88,6 +88,7 @@ import { ref, nextTick, onMounted, defineExpose } from "vue"; ...@@ -88,6 +88,7 @@ import { ref, nextTick, onMounted, defineExpose } from "vue";
import { ElMessage, ElMessageBox } from "element-plus"; import { ElMessage, ElMessageBox } from "element-plus";
import MessageItem from "./MessageItem.vue"; import MessageItem from "./MessageItem.vue";
import request from "@/utils/request"; import request from "@/utils/request";
import { useFormStore } from "@/stores/form";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
interface Message { interface Message {
...@@ -112,9 +113,12 @@ const messages = ref<Message[]>([]); ...@@ -112,9 +113,12 @@ const messages = ref<Message[]>([]);
const inputMessage = ref(""); const inputMessage = ref("");
const isLoading = ref(false); const isLoading = ref(false);
const messagesContainer = ref<HTMLElement>(); const messagesContainer = ref<HTMLElement>();
const formJson = ref<any>(null);
// 获取当前路由 // 获取当前路由
const route = useRoute(); const route = useRoute();
// 表单store
const formStore = useFormStore();
// 全局维护SSE流超时计时器引用,确保能够正确清除 // 全局维护SSE流超时计时器引用,确保能够正确清除
let streamTimeoutTimer: ReturnType<typeof setTimeout> | null = null; let streamTimeoutTimer: ReturnType<typeof setTimeout> | null = null;
...@@ -649,6 +653,37 @@ const processSSELine = async ( ...@@ -649,6 +653,37 @@ const processSSELine = async (
break; 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 = ""; currentEventRef.value = "";
} catch (err) { } catch (err) {
...@@ -740,7 +775,7 @@ const sendMessage = async () => { ...@@ -740,7 +775,7 @@ const sendMessage = async () => {
const decoder = new TextDecoder(); const decoder = new TextDecoder();
let buffer = ""; let buffer = "";
let isStreamComplete = false; // 标记流是否已完成 let isStreamComplete = false; // 标记流是否已完成
const STREAM_TIMEOUT = 60000; // 60秒无流式消息则为超时 const STREAM_TIMEOUT = 120000; // 120秒无流式消息则为超时
// 设置超时检查 // 设置超时检查
const resetStreamTimeout = () => { const resetStreamTimeout = () => {
...@@ -832,6 +867,10 @@ const sendMessage = async () => { ...@@ -832,6 +867,10 @@ const sendMessage = async () => {
messages.value[aiMessageIndex].content = accumulatedContentRef.value; messages.value[aiMessageIndex].content = accumulatedContentRef.value;
} }
if (formJson.value) {
formStore.openForm(formJson.value);
}
// 确保最终状态正确 // 确保最终状态正确
messages.value[aiMessageIndex].isStreaming = false; messages.value[aiMessageIndex].isStreaming = false;
// 设置isLoading为false,结束加载状态 // 设置isLoading为false,结束加载状态
......
<template> <template>
<div v-if="formStore.showForm">
<hi-page-template <hi-page-template
ref="templateRef" ref="templateRef"
:json="json" :json="formStore.formJson"
:open-intl="false" :open-intl="false"
></hi-page-template> ></hi-page-template>
<div class="button-wrap"> <div class="button-wrap">
<a-button type="primary" @click="submit">提交</a-button> <a-button type="primary" @click="submit">提交</a-button>
</div> </div>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from "vue"; import { ref } from "vue";
import { useFormStore } from "@/stores/form";
import HiPageTemplate from "pangea-ui/hi-page-template"; import HiPageTemplate from "pangea-ui/hi-page-template";
// 获取表单 store
const formStore = useFormStore();
// 表单组件ref
const templateRef = ref(); const templateRef = ref();
// 表单提交回调
const submit = () => { const submit = () => {
templateRef.value?.ctx.validate(1, (res, data) => { templateRef.value?.ctx.validate(1, (res: boolean, data: any) => {
console.log(res, data); 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>
.button-wrap { .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' * @Description:
import path from 'path' * @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({ export default defineConfig({
plugins: [vue()], plugins: [vue()],
resolve: { resolve: {
alias: { alias: {
'@': path.resolve(__dirname, './src') "@": path.resolve(__dirname, "./src"),
} },
}, },
server: { server: {
// port: 8081, // 注释掉固定端口,让Vite自动选择可用端口 // port: 8081, // 注释掉固定端口,让Vite自动选择可用端口
...@@ -15,40 +22,42 @@ export default defineConfig({ ...@@ -15,40 +22,42 @@ export default defineConfig({
fs: { fs: {
// 允许访问文件系统,解决Windows平台上的动态导入问题 // 允许访问文件系统,解决Windows平台上的动态导入问题
allow: [ allow: [
'..', "..",
// 明确允许项目根目录 // 明确允许项目根目录
path.resolve(__dirname), path.resolve(__dirname),
// 明确允许工作区根目录 // 明确允许工作区根目录
path.resolve(__dirname, '..') path.resolve(__dirname, ".."),
], ],
// 禁用严格的文件系统限制 // 禁用严格的文件系统限制
strict: false strict: false,
}, },
// 添加headers配置以允许iframe加载 // 添加headers配置以允许iframe加载
headers: { headers: {
'X-Frame-Options': 'SAMEORIGIN' "X-Frame-Options": "SAMEORIGIN",
}, },
proxy: { proxy: {
'/api': { "/api": {
target: 'http://localhost:8080', target: "http://localhost:8080",
// target: "http://agent-backend.clouddev.hisense.com",
changeOrigin: true, changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '/api') rewrite: (path) => path.replace(/^\/api/, "/api"),
}, },
'/ws': { "/ws": {
target: 'http://localhost:8080', target: "http://localhost:8080",
// target: "http://agent-backend.clouddev.hisense.com",
ws: true, // 启用WebSocket代理 ws: true, // 启用WebSocket代理
changeOrigin: true changeOrigin: true,
} },
} },
}, },
build: { build: {
target: 'esnext', target: "esnext",
minify: 'terser', minify: "terser",
sourcemap: false sourcemap: false,
}, },
esbuild: { esbuild: {
jsxFactory: 'h', jsxFactory: "h",
jsxFragment: 'Fragment', jsxFragment: "Fragment",
tsconfigRaw: '{}' tsconfigRaw: "{}",
} },
}) });
\ No newline at end of file
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