新增 CLAUDE.md 文件以提供项目指导,添加 .claudeignore 文件以排除不必要的文件,更新 pom.xml 版本至 3.9.2,修复多个路径遍历和 SQL 注入漏洞,优化字典翻译切面逻辑,增强文件上传和下载的安全性,新增音频文件类型支持,改进动态数据源的安全校验。

This commit is contained in:
geht
2026-05-18 20:05:03 +08:00
parent 67ca5287e2
commit 140f4a816e
589 changed files with 65043 additions and 4682 deletions

View File

@@ -131,7 +131,7 @@ export const generateMemoryByAppId = (params) => {
url: Api.generateMemoryByAppId+'?variables='+ params.variables + '&memoryId='+ params.memoryId,
adapter: 'fetch',
responseType: 'stream',
timeout: 5 * 60 * 1000,
timeout: 60 * 60 * 1000,
},
{
isTransformResponse: false,

View File

@@ -333,6 +333,7 @@
initChartData(params.appId);
} else {
initChartData();
appData.value.metadata = { izDraw: '1', defaultSelect: '0' }
quickCommandData.value = [
{ name: '请介绍一下JeecgBoot', descr: "请介绍一下JeecgBoot" },
{ name: 'JEECG有哪些优势', descr: "JEECG有哪些优势" },

View File

@@ -360,8 +360,8 @@
const isThinking = ref<boolean>(false);
//是否开启网络搜索
const enableSearch = ref<boolean>(false);
//是否显示网络搜索按钮(只有千问模型支持
const showWebSearch = ref<boolean>(false);
//是否显示网络搜索按钮(默认显示
const showWebSearch = ref<boolean>(true);
//模型provider信息
const modelProvider = ref<string>('');
//是否显示深度思考( 只有deepsee-reason支持 )
@@ -565,7 +565,7 @@
// 停止响应
const handleStop = () => {
console.log('ai 聊天:::---停止响应');
console.log('ai 聊天:::---停止响应, 当前loading:', loading.value, ', 调用栈:', new Error().stack?.split('\n').slice(1,4).join(' <- '));
if (loading.value) {
loading.value = false;
}
@@ -666,7 +666,7 @@
params: param,
adapter: 'fetch',
responseType: 'stream',
timeout: 5 * 60 * 1000,
timeout: 60 * 60 * 1000,
},
{
isTransformResponse: false,
@@ -1037,59 +1037,34 @@
}
//update-begin---author:wangshuai---date:2025-03-12---for:【QQYUN-11555】聊天时要流式显示消息---
let result = decoder.decode(value, { stream: true });
result = buffer + result;
const lines = result.split('\n\n');
for (let line of lines) {
if (line.startsWith('data:')) {
let content = line.replace('data:', '').trim();
if(!content){
continue;
}
if(!content.endsWith('}')){
buffer = buffer + content;
continue;
}
buffer = "";
try {
//update-begin---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态内容不能加载出来得刷新才能看到全部回答---
if(content.indexOf(":::card:::") !== -1){
content = content.replace(/\s+/g, '');
}
let parse = JSON.parse(content);
await renderText(parse,conversationId,text,options).then((res)=>{
text = res.returnText;
conversationId = res.conversationId;
});
//update-end---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态内容不能加载出来得刷新才能看到全部回答---
} catch (error) {
console.log('Error parsing update:', error);
}
//update-end---author:wangshuai---date:2025-03-12---for:【QQYUN-11555】聊天时要流式显示消息---
}else{
if(!line){
continue;
}
if(!line.endsWith('}')){
buffer = buffer + line;
continue;
}
buffer = "";
//update-begin---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态内容不能加载出来得刷新才能看到全部回答---
try {
if(line.indexOf(":::card:::") !== -1){
line = line.replace(/\s+/g, '');
}
let parse = JSON.parse(line);
await renderText(parse, conversationId, text, options).then((res) => {
text = res.returnText;
conversationId = res.conversationId;
});
} catch (error) {
console.log('Error parsing update:', error);
}
//update-end---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态内容不能加载出来得刷新才能看到全部回答---
buffer += result;
// 按SSE协议用 \n\n 分割完整事件最后一个元素可能不完整需保留在buffer中
const parts = buffer.split('\n\n');
buffer = parts.pop() || '';
for (let part of parts) {
if (!part || !part.trim()) {
continue;
}
let content = part.startsWith('data:') ? part.replace('data:', '').trim() : part.trim();
if (!content) {
continue;
}
//update-begin---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态内容不能加载出来得刷新才能看到全部回答---
try {
if(content.indexOf(":::card:::") !== -1){
content = content.replace(/\s+/g, '');
}
let parse = JSON.parse(content);
await renderText(parse, conversationId, text, options).then((res) => {
text = res.returnText;
conversationId = res.conversationId;
});
} catch (error) {
console.log('JSON解析失败, content长度:', content.length, ', error:', error);
}
//update-end---author:wangshuai---date:2025-03-13---for:【QQYUN-11572】发布到线上不能实时动态内容不能加载出来得刷新才能看到全部回答---
}
//update-end---author:wangshuai---date:2025-03-12---for:【QQYUN-11555】聊天时要流式显示消息---
}
//update-begin---author:wangshuai---date:2025-11-05---for: 如果是断线重连并且文本为空,需要移出前面两条会话---
if(!text && isReConnect && chatData.value.length >1){
@@ -1118,7 +1093,7 @@
const result = await defHttp.get({ url: '/airag/chat/receive/' + requestId ,
adapter: 'fetch',
responseType: 'stream',
timeout: 5 * 60 * 1000
timeout: 60 * 60 * 1000
}, { isTransformResponse: false }).catch(async (err)=>{
loading.value = false;
localStorage.removeItem('chat_requestId_' + uuid.value);
@@ -1240,29 +1215,28 @@
//是否显示绘图工具
showDraw.value = metadata.izDraw === '1';
//是否选中生成图片
enableDraw.value = metadata.izDraw === '1';
//是否选中生成图片defaultSelect 为 0 时默认不选中)
const defaultSelect = metadata.defaultSelect || metadata.izDraw;
enableDraw.value = defaultSelect === '1';
drawModelId.value = metadata.drawModelId;
if (metadata && metadata.modelInfo) {
modelProvider.value = metadata.modelInfo.provider || '';
modelName.value = metadata.modelInfo.modelName || '';
// 只有千问模型支持网络搜索
showWebSearch.value = modelProvider.value === 'QWEN';
showThink.value = modelName.value === 'deepseek-reasoner';
} else {
showWebSearch.value = false;
showThink.value = false;
}
} catch (e) {
console.error('解析模型信息失败', e);
showWebSearch.value = false;
showThink.value = false;
enableDraw.value = false;
}
} else {
showWebSearch.value = false;
showThink.value = false;
showDraw.value = false;
enableDraw.value = false;
}
}

View File

@@ -1,5 +1,5 @@
<template>
<div class="chat" :class="[inversion === 'user' ? 'self' : 'chatgpt']" v-if="getText || (props.presetQuestion && props.presetQuestion.length>0)">
<div class="chat" :class="[inversion === 'user' ? 'self' : 'chatgpt']" v-if="getText || props.error || (props.presetQuestion && props.presetQuestion.length>0)">
<div class="avatar" v-if="showAvatar !== 'no'">
<img v-if="inversion === 'user'" :src="avatar()" />
<img v-else :src="getAiImg()" />

View File

@@ -1,5 +1,5 @@
<template>
<div v-if="parsedText != ''" class="textWrap" :class="[inversion === 'user' ? 'self' : (isOnlyImage ? 'chatgpt-image' : 'chatgpt')]" ref="textRef">
<div v-if="parsedText != '' || error" class="textWrap" :class="[inversion === 'user' ? 'self' : (isOnlyImage ? 'chatgpt-image' : 'chatgpt')]" ref="textRef">
<div v-if="inversion != 'user'" :style="{ width: getIsMobile? screenWidth : 'auto' }">
<div ref="markdownBodyRef" class="markdown-body" :class="{ 'markdown-body-generate': loading }" v-html="parsedText" />
<template v-if="showRefKnow">

View File

@@ -0,0 +1,43 @@
import { defHttp } from '/@/utils/http/axios';
enum Api {
sqlPageExecute = '/airag/mcp/database/sqlPageExecute',
sqlExportXls = '/airag/mcp/database/sqlExportXls',
}
/**
* 分页执行 SQL 查询
*/
export function sqlPageExecute(params: { sql: string; dbSource?: string; pageNo: number; pageSize: number }) {
return defHttp.post<Recordable>(
{
url: Api.sqlPageExecute,
params: {
sql: params.sql,
dbSourceKey: params.dbSource || '',
pageNo: params.pageNo,
pageSize: params.pageSize,
},
},
{ isTransformResponse: false }
);
}
/**
* 导出图表原始数据为 Excel
*/
export function sqlExportXls(params: { sql: string; dbSource?: string; columns?: Recordable }) {
return defHttp.post(
{
url: Api.sqlExportXls,
params: {
sql: params.sql,
dbSourceKey: params.dbSource || '',
columns: params.columns || {},
},
responseType: 'blob',
timeout: 5 * 60 * 1000,
},
{ isTransformResponse: false, isReturnNativeResponse: true }
);
}

View File

@@ -9,32 +9,42 @@
<span v-else>模型返回的图表渲染格式不正确请优化提示词或重新尝试</span>
</div>
<div v-else class="ai-chat-chart__body">
<!-- 折线图 -->
<LineMulti v-if="resolvedType === 'line'" v-bind="lineProps"/>
<!-- 柱状图 -->
<BarMulti v-else-if="resolvedType === 'bar'" v-bind="barProps"/>
<!-- 饼图 -->
<Pie v-else-if="resolvedType === 'pie'" v-bind="pieProps"/>
<!-- 多列柱状图 -->
<BarMulti v-else-if="resolvedType === 'multibar'" v-bind="multiBarProps"/>
<!-- 多行折线图 -->
<LineMulti v-else-if="resolvedType === 'multiline'" v-bind="multiLineProps"/>
<!-- 折柱图 -->
<BarAndLine v-else-if="resolvedType === 'barline'" v-bind="barLineProps"/>
<!-- 面积图 -->
<SingleLine v-else-if="resolvedType === 'area'" v-bind="areaLineProps"/>
<!-- 雷达图 -->
<Radar v-else-if="resolvedType === 'radar'" v-bind="radarProps"/>
<!-- 仪表盘 -->
<Gauge v-else-if="resolvedType === 'gauge'" v-bind="gaugeProps"/>
<Tabs v-model:activeKey="activeTab" size="small" class="ai-chat-chart__tabs">
<!-- 主图表 Tab -->
<TabPane :key="resolvedType" :tab="getChartLabel(resolvedType)">
<ChartBody :chartType="resolvedType" />
</TabPane>
<!-- 可替代图表 Tabs -->
<TabPane v-for="alt in validAltTypes" :key="alt" :tab="getChartLabel(alt)">
<ChartBody :chartType="alt" />
</TabPane>
<!-- 数据 Tab -->
<TabPane v-if="hasSql" key="__data__" tab="数据">
<div class="ai-chat-chart__data-toolbar">
<a-button size="small" :loading="exportLoading" @click="handleExport">
<template #icon><DownloadOutlined /></template>
导出
</a-button>
</div>
<BasicTable
v-bind="tableBindings"
:loading="tableLoading"
size="small"
class="ai-chat-chart__table"
/>
</TabPane>
</Tabs>
</div>
</div>
</template>
<script setup lang="ts">
import type { ChartType } from './types';
import { computed, ref, watchEffect } from 'vue';
import type { BasicColumn } from '/@/components/Table';
import { computed, defineComponent, h, ref, watch, watchEffect } from 'vue';
import { Tabs, TabPane, message } from 'ant-design-vue';
import { DownloadOutlined } from '@ant-design/icons-vue';
import { BasicTable, useTable } from '/@/components/Table';
import LineMulti from '/@/components/chart/LineMulti.vue';
import BarMulti from '/@/components/chart/BarMulti.vue';
import Pie from '/@/components/chart/Pie.vue';
@@ -42,11 +52,12 @@ import Radar from '/@/components/chart/Radar.vue';
import Gauge from '/@/components/chart/Gauge.vue';
import BarAndLine from '/@/components/chart/BarAndLine.vue';
import SingleLine from '/@/components/chart/SingleLine.vue';
import { sqlPageExecute, sqlExportXls } from './ChartRender.api';
const props = defineProps({
/**
* 图表配置字符串,示例:
* {"type":"bar","data":[{"x":"数据项1","y":100},{"x":"数据项2","y":80}]}
* {"type":"bar","altTypes":["line","pie"],"data":[...],"sql":"SELECT ...","dbSource":"","columns":{"field1":"标题1"}}
*/
data: {
type: String,
@@ -58,6 +69,39 @@ const props = defineProps({
},
});
/** 图表类型中文名映射 */
const chartTypeLabels: Record<string, string> = {
bar: '柱状图',
line: '折线图',
pie: '饼图',
radar: '雷达图',
gauge: '仪表盘',
barline: '折柱图',
multibar: '多列柱状图',
multiline: '多行折线图',
area: '面积图',
};
/** 获取图表类型的中文标签 */
function getChartLabel(type: string): string {
return chartTypeLabels[type] || type;
}
/** 当前激活的 Tab */
const activeTab = ref<string>('');
/** 表格加载状态 */
const tableLoading = ref(false);
/** 导出加载状态 */
const exportLoading = ref(false);
/** 表格数据 */
const tableData = ref<Recordable[]>([]);
/** 表格列定义 */
const tableColumns = ref<BasicColumn[]>([]);
/** 数据总条数 */
const tableTotal = ref(0);
/** 是否已首次加载过数据 */
const dataLoaded = ref(false);
/**
* 解析失败或类型错误的提示文本。
*/
@@ -85,7 +129,6 @@ const parsedConfig = computed<Recordable>(() => {
/**
* 支持的图表类型集合。
*/
// 支持的类型覆盖常见图表,名称与提示词保持宽松映射
const supportedTypes: ChartType[] = ['bar', 'line', 'pie', 'radar', 'gauge', 'barline', 'multibar', 'multiline', 'area'];
/**
@@ -115,6 +158,25 @@ const resolvedType = computed<ChartType>(() => {
return '';
});
/** 解析可替代图表类型列表(过滤掉无效类型和主类型重复) */
const validAltTypes = computed<ChartType[]>(() => {
const altTypes = (parsedConfig.value as any).altTypes;
if (!Array.isArray(altTypes)) {
return [];
}
const primary = resolvedType.value;
return altTypes
.map((t: string) => String(t).toLowerCase() as ChartType)
.filter((t: ChartType) => t !== primary && supportedTypes.includes(t));
});
/** 初始化 activeTab 为主图表类型 */
watchEffect(() => {
if (resolvedType.value && !activeTab.value) {
activeTab.value = resolvedType.value;
}
});
/**
* 当类型不被支持时,给出错误提示。
*/
@@ -149,6 +211,171 @@ const hasData = computed<boolean>(() => {
return Array.isArray(rawData.value) && rawData.value.length > 0;
});
/** 是否包含可查询的 SQL */
const hasSql = computed<boolean>(() => {
const sql = (parsedConfig.value as any).sql;
return typeof sql === 'string' && sql.trim().length > 0;
});
/** 从配置中提取 SQL */
const chartSql = computed<string>(() => {
return ((parsedConfig.value as any).sql || '').trim();
});
/** 从配置中提取数据源 */
const chartDbSource = computed<string>(() => {
return ((parsedConfig.value as any).dbSource || '').trim();
});
/** 从配置中提取列标题映射 */
const chartColumns = computed<Recordable>(() => {
return (parsedConfig.value as any).columns || {};
});
/**
* 使用 BasicTable 组件
*/
const [registerTable, { setPagination }] = useTable({
columns: tableColumns,
dataSource: tableData,
pagination: {
pageSize: 10,
current: 1,
total: 0,
},
showIndexColumn: false,
canResize: false,
bordered: true,
onChange: handleTableChange,
});
/** 表格绑定属性 */
const tableBindings = computed(() => ({
onRegister: registerTable,
}));
/**
* 加载表格数据
*/
async function loadTableData(pageNo = 1, pageSize = 10) {
if (!chartSql.value) {
return;
}
tableLoading.value = true;
try {
const res = await sqlPageExecute({
sql: chartSql.value,
dbSource: chartDbSource.value,
pageNo,
pageSize,
});
if (res.success && res.result) {
const { records = [], total = 0 } = res.result;
tableData.value = records;
tableTotal.value = total;
// 根据返回数据动态生成列(使用 columns 映射中文标题)
if (records.length > 0) {
tableColumns.value = buildColumns(records[0]);
}
setPagination({
current: pageNo,
pageSize,
total,
});
} else {
tableData.value = [];
tableTotal.value = 0;
}
} catch (e) {
console.error('加载图表原始数据失败', e);
tableData.value = [];
tableTotal.value = 0;
} finally {
tableLoading.value = false;
}
}
/**
* 根据数据行的 key 动态生成 BasicTable 列定义,优先使用 columns 映射的中文标题
*/
function buildColumns(row: Recordable): BasicColumn[] {
const columnMap = chartColumns.value;
return Object.keys(row).map((key) => ({
title: columnMap[key] || key,
dataIndex: key,
width: 150,
ellipsis: true,
}));
}
/**
* 表格分页变更处理
*/
function handleTableChange(pagination: any) {
loadTableData(pagination.current, pagination.pageSize);
}
/**
* 导出全部数据
*/
async function handleExport() {
if (!chartSql.value) {
return;
}
exportLoading.value = true;
try {
const response = await sqlExportXls({
sql: chartSql.value,
dbSource: chartDbSource.value,
columns: chartColumns.value,
});
if (!response || !response.data) {
message.warning('导出失败');
return;
}
const data = response.data;
// 检查是否为错误响应JSON 格式)
if (data.type && data.type.indexOf('json') !== -1) {
const text = await data.text();
try {
const json = JSON.parse(text);
if (!json.success) {
message.warning('导出失败:' + (json.message || '未知错误'));
return;
}
} catch (_e) {
// 非 JSON继续下载
}
}
const url = window.URL.createObjectURL(new Blob([data], { type: 'application/vnd.ms-excel' }));
const link = document.createElement('a');
link.style.display = 'none';
link.href = url;
link.setAttribute('download', '数据导出.xls');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
} catch (e) {
console.error('导出失败', e);
message.error('导出失败');
} finally {
exportLoading.value = false;
}
}
/**
* 切换到数据 Tab 时首次加载
*/
watch(activeTab, (val) => {
if (val === '__data__' && !dataLoaded.value && hasSql.value) {
dataLoaded.value = true;
loadTableData();
}
});
// ======================== 图表数据转换 ========================
/**
* 将原始数据标准化为多序列图表所需的结构。
*/
@@ -201,7 +428,7 @@ function buildMultiSeriesItem(item: Recordable): Recordable {
}
/**
* 解析系列名称,优先使用 series其次使用 type最后回退为数据
* 解析系列名称,优先使用 series其次使用 type最后回退为"数据"
*/
function resolveSeriesName(item: Recordable): string {
if (item && item.series !== undefined) {
@@ -295,114 +522,84 @@ const barLineSeriesData = computed<Recordable[]>(() => {
});
});
/**
* 折线图的渲染属性。
*/
const lineProps = computed(() => {
return {
type: 'line',
height: '360px',
width: '100%',
chartData: multiSeriesData.value,
};
});
// ======================== 根据类型获取图表 props ========================
/**
* 柱状图的渲染属性
* 根据图表类型返回对应的渲染属性
*/
const barProps = computed(() => {
return {
height: '360px',
width: '100%',
chartData: multiSeriesData.value,
};
});
function getChartPropsForType(type: ChartType): Recordable {
switch (type) {
case 'line':
return { type: 'line', height: '360px', width: '100%', chartData: multiSeriesData.value };
case 'bar':
return { height: '360px', width: '100%', chartData: multiSeriesData.value };
case 'pie':
return { height: '360px', width: '100%', chartData: pieSeriesData.value };
case 'multibar':
return { height: '360px', width: '100%', chartData: multiSeriesData.value, option: parsedConfig.value.option || {} };
case 'multiline':
return { type: 'line', height: '360px', width: '100%', chartData: multiSeriesData.value, option: parsedConfig.value.option || {} };
case 'barline':
return { height: '360px', width: '100%', chartData: barLineSeriesData.value, customColor: (parsedConfig.value as any).colors || [], option: parsedConfig.value.option || {} };
case 'area':
return { type: 'line', height: '360px', width: '100%', chartData: areaSeriesData.value, option: { ...(parsedConfig.value.option || {}), areaStyle: {} } };
case 'radar':
return { height: '420px', width: '100%', chartData: radarSeriesData.value, option: parsedConfig.value.option || {} };
case 'gauge':
return { height: '360px', width: '100%', chartData: gaugeData.value, option: parsedConfig.value.option || {}, seriesColor: (parsedConfig.value as any).seriesColor || undefined };
default:
return {};
}
}
/**
* 饼图的渲染属性。
* 根据图表类型返回对应的组件
*/
const pieProps = computed(() => {
return {
height: '360px',
width: '100%',
chartData: pieSeriesData.value,
};
});
function getChartComponentForType(type: ChartType) {
switch (type) {
case 'line':
return LineMulti;
case 'bar':
return BarMulti;
case 'pie':
return Pie;
case 'multibar':
return BarMulti;
case 'multiline':
return LineMulti;
case 'barline':
return BarAndLine;
case 'area':
return SingleLine;
case 'radar':
return Radar;
case 'gauge':
return Gauge;
default:
return null;
}
}
/**
* 多列柱状图配置,与 bar 相同但允许区分类型前缀。
* 内部图表渲染组件,根据 chartType 动态渲染对应图表
*/
const multiBarProps = computed(() => {
return {
height: '360px',
width: '100%',
chartData: multiSeriesData.value,
option: parsedConfig.value.option || {},
};
});
/**
* 多行折线图配置。
*/
const multiLineProps = computed(() => {
return {
type: 'line',
height: '360px',
width: '100%',
chartData: multiSeriesData.value,
option: parsedConfig.value.option || {},
};
});
/**
* 面积折线图配置,开启面积样式。
*/
const areaLineProps = computed(() => {
return {
type: 'line',
height: '360px',
width: '100%',
chartData: areaSeriesData.value,
option: { ...(parsedConfig.value.option || {}), areaStyle: {} },
};
});
/**
* 雷达图配置。
*/
const radarProps = computed(() => {
return {
height: '420px',
width: '100%',
chartData: radarSeriesData.value,
option: parsedConfig.value.option || {},
};
});
/**
* 仪表盘配置。
*/
const gaugeProps = computed(() => {
return {
height: '360px',
width: '100%',
chartData: gaugeData.value,
option: parsedConfig.value.option || {},
seriesColor: (parsedConfig.value as any).seriesColor || undefined,
};
});
/**
* 折柱图配置,支持自定义颜色。
*/
const barLineProps = computed(() => {
return {
height: '360px',
width: '100%',
chartData: barLineSeriesData.value,
customColor: (parsedConfig.value as any).colors || [],
option: parsedConfig.value.option || {},
};
const ChartBody = defineComponent({
props: {
chartType: {
type: String as () => ChartType,
required: true,
},
},
setup(bodyProps) {
return () => {
const comp = getChartComponentForType(bodyProps.chartType as ChartType);
if (!comp) {
return null;
}
const chartProps = getChartPropsForType(bodyProps.chartType as ChartType);
return h(comp, chartProps);
};
},
});
</script>
@@ -410,7 +607,7 @@ const barLineProps = computed(() => {
.ai-chat-chart {
width: 100%;
min-width: 360px;
min-width: 420px;
padding: 12px 0;
}
@@ -424,4 +621,22 @@ const barLineProps = computed(() => {
line-height: 20px;
}
.ai-chat-chart__tabs {
:deep(.ant-tabs-nav) {
margin-bottom: 8px;
}
}
.ai-chat-chart__data-toolbar {
display: flex;
justify-content: flex-end;
margin-bottom: 8px;
}
.ai-chat-chart__table {
:deep(.ant-table-wrapper) {
padding: 0;
}
}
</style>

View File

@@ -232,6 +232,7 @@ function formatData(value: unknown): string {
font-size: 14px;
line-height: 1.5;
color: #333;
max-width: 520px;
}
.tool-exec-loading,
@@ -371,6 +372,7 @@ function formatData(value: unknown): string {
font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
white-space: pre-wrap;
word-break: break-word;
max-height: 340px;
overflow: auto;
}

View File

@@ -125,6 +125,13 @@
prologue: '你好,我是 AI绘图智能体。',
presetQuestion: '[{"key":1,"descr":"请生成一张具有日本风格的动漫成年女孩。","update":true}, {"key":2,"descr":"请生成一幅中国神话故事中,手持武器的哪吒形象。","update":true}]',
metadata:"{\"izDraw\":\"1\"}"
},
{
id: '1993651187913981953',
name: '商品导购',
icon: 'https://minio.jeecg.com/otatest/shoppingGuide_1769754188966.png',
prologue: '向要了解哪一款产品,我可以给你介绍。',
presetQuestion: '[{"key":1,"sort":1,"descr":"HIP 0603T Series","update":false},{"key":2,"sort":2,"descr":"CHIP 1206HC Series","update":false},{"key":3,"sort":3,"descr":"BRICK 1032ST Series","update":true}]',
},
]);

View File

@@ -110,6 +110,7 @@
resetField();
await getPromptList();
setModalProps({
title: '选择提示词',
height: 600,
bodyStyle: { padding: '24px' },
});
@@ -200,6 +201,8 @@
*/
async function handleOk() {
if (selectedPrompt.value) {
// select 传递完整对象,供调用方按需取用 id/name/content
emit('select', selectedPrompt.value);
emit('ok', selectedPrompt.value.content);
} else {
emit('ok');

View File

@@ -428,6 +428,20 @@
</div>
</a-form-item>
</a-row>
<a-row>
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol">
<div style="display: flex;margin-top: 10px">
<div style="margin-left: 2px">显示工具调用过程</div>
<a-switch
v-model:checked="showToolProcessChecked"
:disabled="isRelease"
checked-children=""
un-checked-children=""
@change="handleShowToolProcessChange"
/>
</div>
</a-form-item>
</a-row>
<a-row v-if="izDrawChecked" class="mt-10">
<a-col :span="24">
<a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" v-bind="validateInfos.drawModelId">
@@ -622,6 +636,8 @@
const multiSessionChecked = ref<boolean>(true);
//开启会话能力
const izDrawChecked = ref<boolean>(false);
//显示工具调用过程
const showToolProcessChecked = ref<boolean>(true);
// 是否已发布
const isRelease = ref<boolean>(false);
//对话设置弹窗ref
@@ -1208,6 +1224,11 @@
}else{
izDrawChecked.value = false;
}
if(metadata.value?.showToolProcess != null){
showToolProcessChecked.value = metadata.value.showToolProcess === '1';
}else{
showToolProcessChecked.value = true;
}
if(metadata.value?.drawModelId){
formState.drawModelId = metadata.value.drawModelId;
}
@@ -1611,6 +1632,20 @@
}
//================================================ end 开启绘画 ========================================================
//================================================ begin 显示工具调用过程 =========================================================
/**
* 显示工具调用过程开关回调
*/
function handleShowToolProcessChange(checked){
if(checked){
metadata.value.showToolProcess = "1";
}else{
metadata.value.showToolProcess = "0";
}
formState.metadata = JSON.stringify(metadata.value);
}
//================================================ end 显示工具调用过程 ========================================================
return {
registerModal,
title,
@@ -1705,6 +1740,8 @@
izDrawChecked,
handleDrawChange,
handleDrawModelChange,
showToolProcessChecked,
handleShowToolProcessChange,
};
},
};

View File

@@ -39,7 +39,6 @@
import { Icon } from '/src/components/Icon';
import { Button, Checkbox, Switch, Popconfirm } from 'ant-design-vue';
import { JVxeTypes, JVxeColumn, JVxeTableInstance } from '/src/components/jeecg/JVxeTable/types';
import { JVxeTable } from '/src/components/jeecg/JVxeTable';
import { useMessage } from '/src/hooks/web/useMessage';
export default defineComponent({
@@ -47,7 +46,6 @@
components: {
BasicModal,
Icon,
JVxeTable,
AButton: Button,
ACheckbox: Checkbox,
ASwitch: Switch,