diff --git a/CHANGELOG.md b/CHANGELOG.md
index 01868895..0326f5ed 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+# 2.0.7
+## ⭐ New Features
+
+- Export query result as file is supported
+
+## 🐞 Bug Fixes
+
+- Fixed ai config issues [Issue #346](https://github.com/chat2db/Chat2DB/issues/346)
+
+
# 2.0.6
## 🐞 Bug Fixes
diff --git a/chat2db-client/src/blocks/Setting/AiSetting/index.tsx b/chat2db-client/src/blocks/Setting/AiSetting/index.tsx
index 7512115d..56c27a4c 100644
--- a/chat2db-client/src/blocks/Setting/AiSetting/index.tsx
+++ b/chat2db-client/src/blocks/Setting/AiSetting/index.tsx
@@ -195,15 +195,12 @@ export default function SettingAI(props: IProps) {
)}
- {/* */}
- {aiConfig?.aiSqlSource === AiSqlSourceType.CHAT2DBAI && !aiConfig.apiKey && }
+ {/* {aiConfig?.aiSqlSource === AiSqlSourceType.CHAT2DBAI && !aiConfig.apiKey && } */}
>
);
}
diff --git a/chat2db-client/src/blocks/Setting/index.tsx b/chat2db-client/src/blocks/Setting/index.tsx
index df860043..a2471bbd 100644
--- a/chat2db-client/src/blocks/Setting/index.tsx
+++ b/chat2db-client/src/blocks/Setting/index.tsx
@@ -9,12 +9,11 @@ import ProxySetting from './ProxySetting';
import About from './About';
import { connect } from 'umi';
import { IAIState } from '@/models/ai';
-import styles from './index.less';
-import configService from '@/service/config';
-import { AiSqlSourceType } from '@/typings/ai';
import TestVersion from '@/components/TestVersion';
import { IAiConfig } from '@/typings';
+import styles from './index.less';
+
interface IProps {
aiConfig: IAiConfig;
className?: string;
@@ -28,6 +27,12 @@ function Setting(props: IProps) {
const [currentMenu, setCurrentMenu] = useState(0);
+ useEffect(() => {
+ if (isModalVisible) {
+ getAiSystemConfig();
+ }
+ }, [isModalVisible]);
+
useEffect(() => {
getAiSystemConfig();
}, []);
diff --git a/chat2db-client/src/components/SearchResult/TableBox/index.less b/chat2db-client/src/components/SearchResult/TableBox/index.less
index fda0d7bf..b26a3cd5 100644
--- a/chat2db-client/src/components/SearchResult/TableBox/index.less
+++ b/chat2db-client/src/components/SearchResult/TableBox/index.less
@@ -111,6 +111,7 @@
// top: 0;
// right: 0;
// bottom: 0;
+ cursor: pointer;
i {
font-size: 15px;
@@ -139,3 +140,7 @@
}
}
}
+
+.monacoEditor {
+ height: 60vh;
+}
diff --git a/chat2db-client/src/components/SearchResult/TableBox/index.tsx b/chat2db-client/src/components/SearchResult/TableBox/index.tsx
index b40a3c33..4ed7c415 100644
--- a/chat2db-client/src/components/SearchResult/TableBox/index.tsx
+++ b/chat2db-client/src/components/SearchResult/TableBox/index.tsx
@@ -24,7 +24,7 @@ interface ITableProps {
config: IResultConfig;
onConfigChange: (config: IResultConfig) => void;
onSearchTotal: () => Promise;
- onExport: (exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => void;
+ onExport: (sql: string, originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => void;
}
interface IViewTableCellData {
@@ -54,42 +54,46 @@ export default function TableBox(props: ITableProps) {
const [appTheme] = useTheme();
const isDarkTheme = useMemo(() => appTheme.backgroundColor === ThemeType.Dark, [appTheme]);
+ const handleExport = (exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
+ props.onExport && props.onExport(data.sql, data.originalSql, exportType, exportSize);
+ };
+
const items: MenuProps['items'] = useMemo(
() => [
{
label: '导出全部数据为csv',
key: '1',
- icon: ,
+ // icon: ,
onClick: () => {
- props.onExport && props.onExport(ExportTypeEnum.CSV, ExportSizeEnum.ALL);
+ handleExport(ExportTypeEnum.CSV, ExportSizeEnum.ALL);
},
},
{
label: '导出全部数据为插入语句',
key: '2',
- icon: ,
+ // icon: ,
onClick: () => {
- props.onExport && props.onExport(ExportTypeEnum.INSERT, ExportSizeEnum.ALL);
+ handleExport(ExportTypeEnum.INSERT, ExportSizeEnum.ALL);
},
},
{
label: '导出当前页数据为csv',
key: '3',
- icon: ,
+ // icon: ,
onClick: () => {
- props.onExport && props.onExport(ExportTypeEnum.CSV, ExportSizeEnum.CURRENT_PAGE);
+ handleExport(ExportTypeEnum.CSV, ExportSizeEnum.CURRENT_PAGE);
},
},
{
label: '导出当前页数据为插入语句',
key: '4',
- icon: ,
+ // icon: ,
onClick: () => {
- props.onExport && props.onExport(ExportTypeEnum.INSERT, ExportSizeEnum.CURRENT_PAGE);
+ handleExport(ExportTypeEnum.INSERT, ExportSizeEnum.CURRENT_PAGE);
},
},
],
- [],
+ [data],
);
const defaultSorts: SortItem[] = useMemo(
@@ -260,6 +264,7 @@ export default function TableBox(props: ITableProps) {
open={!!viewTableCellData?.name}
onCancel={handleCancel}
width="60vw"
+ height="70vh"
maskClosable={false}
footer={
<>
diff --git a/chat2db-client/src/components/SearchResult/index.tsx b/chat2db-client/src/components/SearchResult/index.tsx
index 99e205e7..07bf9ebc 100644
--- a/chat2db-client/src/components/SearchResult/index.tsx
+++ b/chat2db-client/src/components/SearchResult/index.tsx
@@ -1,4 +1,4 @@
-import React, { memo, useEffect, useState, useMemo, Fragment } from 'react';
+import React, { memo, useEffect, useState, useMemo, Fragment, useCallback } from 'react';
import classnames from 'classnames';
import Tabs, { IOption } from '@/components/Tabs';
import Iconfont from '@/components/Iconfont';
@@ -16,7 +16,7 @@ interface IProps {
manageResultDataList?: IManageResultData[];
resultConfig: IResultConfig[];
onExecute: (sql: string, config: IResultConfig, index: number) => void;
- onExport: (originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => Promise;
+ onExport: (sql: string, originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => Promise;
onTabEdit: (type: 'add' | 'remove', value?: number | string) => void;
onSearchTotal: (index: number) => Promise;
isLoading?: boolean;
@@ -45,7 +45,7 @@ const handleTabs = (result: IManageResultData[]) => {
};
export default memo(function SearchResult(props) {
- const { className, manageResultDataList = [], isLoading, onExecute, onSearchTotal } = props;
+ const { className, manageResultDataList = [], isLoading, onExecute, onExport } = props;
const [currentTab, setCurrentTab] = useState();
const [resultDataList, setResultDataList] = useState([]);
const [resultConfig, setResultConfig] = useState([]);
@@ -64,7 +64,7 @@ export default memo(function SearchResult(props) {
setCurrentTab(manageResultDataList[0].uuid);
}
- setResultDataList(manageResultDataList);
+ setResultDataList([...manageResultDataList]);
setTabs(handleTabs(manageResultDataList));
}, [manageResultDataList]);
@@ -85,48 +85,50 @@ export default memo(function SearchResult(props) {
);
};
- const renderTable = useMemo(() => {
+ const renderTable = () => {
if (!tabs || !tabs.length) {
return renderEmpty();
}
if (!resultDataList || !resultDataList.length) {
return renderEmpty();
}
- return (resultDataList || []).map((item, index: number) => {
- if (item.success) {
- return (
-
- {
- if (props.onSearchTotal) {
- return await props.onSearchTotal(index);
- }
- }}
- onExport={(exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
- props.onExport && props.onExport(item.originalSql, exportType, exportSize);
- }}
- />
-
- );
- } else {
- return (
-
-
-
- );
- }
- });
- }, [currentTab, resultDataList, resultConfig]);
+
+ return renderTableContent;
+ };
+
+ const renderTableContent = (resultDataList || []).map((item, index: number) => {
+ console.log(item.sql, item.originalSql);
+ if (item.success) {
+ return (
+ {
+ onExecute && onExecute(item.originalSql, config, index);
+ }}
+ onSearchTotal={async () => {
+ if (props.onSearchTotal) {
+ return await props.onSearchTotal(index);
+ }
+ }}
+ onExport={(sql: string, originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
+ onExport && onExport(sql, originalSql, exportType, exportSize);
+ }}
+ />
+ );
+ } else {
+ return (
+
+ );
+ }
+ });
return (
@@ -144,7 +146,7 @@ export default memo(function SearchResult(props) {
) : null}
- {renderTable}
+ {renderTable()}
);
diff --git a/chat2db-client/src/pages/main/workspace/components/WorkspaceRightItem/index.tsx b/chat2db-client/src/pages/main/workspace/components/WorkspaceRightItem/index.tsx
index 2b346282..0b91cd55 100644
--- a/chat2db-client/src/pages/main/workspace/components/WorkspaceRightItem/index.tsx
+++ b/chat2db-client/src/pages/main/workspace/components/WorkspaceRightItem/index.tsx
@@ -15,6 +15,7 @@ import { v4 as uuidV4 } from 'uuid';
import sql from '@/service/sql';
import { isNumber } from 'lodash';
import { ExportSizeEnum, ExportTypeEnum } from '@/typings/resultTable';
+import { downloadFile } from '@/utils/common';
interface IProps {
className?: string;
isActive: boolean;
@@ -139,10 +140,14 @@ const WorkspaceRightItem = memo(function (props) {
return total;
};
- const handleExportSQLResult = async (originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
- const params: IExportParams = { ...data, originalSql, exportType, exportSize };
-
- await sqlServer.exportResultTable(params);
+ const handleExportSQLResult = async (
+ sql: string,
+ originalSql: string,
+ exportType: ExportTypeEnum,
+ exportSize: ExportSizeEnum,
+ ) => {
+ const params: IExportParams = { ...data, sql, originalSql, exportType, exportSize };
+ downloadFile(window._BaseURL + '/api/rdb/dml/export', params);
};
const handleResultTabEdit = (type: 'add' | 'remove', uuid?: string | number) => {
diff --git a/chat2db-client/src/service/sql.ts b/chat2db-client/src/service/sql.ts
index 91d23eec..89144cba 100644
--- a/chat2db-client/src/service/sql.ts
+++ b/chat2db-client/src/service/sql.ts
@@ -123,7 +123,7 @@ export interface IExportParams extends IExecuteSqlParams {
/**
* 导出-表格
*/
-const exportResultTable = createRequest('/api/rdb/dml/export', { method: 'post' });
+// const exportResultTable = createRequest('/api/rdb/dml/export', { method: 'post' });
export default {
getList,
@@ -142,5 +142,5 @@ export default {
addTablePin,
deleteTablePin,
getDMLCount,
- exportResultTable
+ // exportResultTable
};
diff --git a/chat2db-client/src/utils/common.ts b/chat2db-client/src/utils/common.ts
index 007da3d1..ee293312 100644
--- a/chat2db-client/src/utils/common.ts
+++ b/chat2db-client/src/utils/common.ts
@@ -14,3 +14,45 @@ export function formatParams(obj: { [key: string]: any }) {
});
return params.toString();
}
+
+export function downloadFile(url: string, params: any) {
+ // 创建POST请求
+ fetch(url, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json', // 或者根据服务端的要求设置其他的内容类型
+ },
+ body: JSON.stringify(params), // 将参数转换为JSON字符串
+ })
+ .then((response) => {
+ // 从content-disposition头中获取文件名
+ const contentDisposition = response.headers.get('content-disposition');
+ const filename = contentDisposition ? decodeURIComponent(contentDisposition.split("''")[1]) : 'file.txt';
+
+ // 获取返回的Blob数据
+ return response.blob().then((blob) => ({ blob, filename }));
+ })
+ .then(({ blob, filename }) => {
+ // 创建一个代表Blob对象的URL
+ const blobUrl = URL.createObjectURL(blob);
+
+ // 创建一个隐藏的 标签,并设置其 href 属性
+ const a = document.createElement('a');
+ a.style.display = 'none';
+ a.href = blobUrl;
+
+ // 使用从响应头解析的文件名
+ a.download = filename;
+
+ // 将 标签附加到 DOM,并触发点击事件
+ document.body.appendChild(a);
+ a.click();
+
+ // 清理:从 DOM 中移除 标签,并释放Blob URL
+ document.body.removeChild(a);
+ URL.revokeObjectURL(blobUrl);
+ })
+ .catch((error) => {
+ console.error('下载文件失败:', error);
+ });
+}
diff --git a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/Application.java b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/Application.java
index 25567ad0..2203cefc 100644
--- a/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/Application.java
+++ b/chat2db-server/chat2db-server-start/src/main/java/ai/chat2db/server/start/Application.java
@@ -4,7 +4,6 @@ import ai.chat2db.server.tools.common.model.ConfigJson;
import ai.chat2db.server.tools.common.util.ConfigUtils;
import com.dtflys.forest.springboot.annotation.ForestScan;
import lombok.extern.slf4j.Slf4j;
-import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
@@ -37,7 +36,7 @@ public class Application {
// Represents that the current version has been successfully launched
if (StringUtils.isNotBlank(currentVersion) && StringUtils.equals(currentVersion, configJson.getLatestStartupSuccessVersion())) {
// Flyway doesn't need to start every time to increase startup speed
- args = ArrayUtils.add(args, "--spring.flyway.enabled=false");
+ //args = ArrayUtils.add(args, "--spring.flyway.enabled=false");
log.info("The current version {} has been successfully launched once and will no longer load Flyway.",
currentVersion);
}
diff --git a/chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/mybatis/MybatisGeneratorTest.java b/chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/mybatis/MybatisGeneratorTest.java
index 7ddebb44..7ec7b4ee 100644
--- a/chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/mybatis/MybatisGeneratorTest.java
+++ b/chat2db-server/chat2db-server-start/src/test/java/ai/chat2db/server/start/test/mybatis/MybatisGeneratorTest.java
@@ -37,13 +37,13 @@ public class MybatisGeneratorTest extends BaseTest {
private void doGenerator(List tableList) {
- // 当前项目地址 拿到的是ali-dbhub-server-start地址
+ // 当前项目地址 拿到的是chat2db-server-start地址
String outputDir = System.getProperty("user.dir")
- + "/../ali-dbhub-server-domain/ali-dbhub-server-domain-repository/src/main"
+ + "/../chat2db-server-domain/chat2db-server-domain-repository/src/main"
+ "/java";
String xmlDir = System.getProperty("user.dir")
- + "/../ali-dbhub-server-domain/ali-dbhub-server-domain-repository/src/main"
- + "/resources/com/alibaba/dbhub/server/domain/repository";
+ + "/../chat2db-server-domain/chat2db-server-domain-repository/src/main"
+ + "/resources/ai/chat2db/server/domain/repository";
// 不要生成service controller
Map pathInfo = new HashMap<>();
@@ -58,7 +58,7 @@ public class MybatisGeneratorTest extends BaseTest {
//全局配置
.globalConfig(builder -> {
// 设置作者
- builder.author("ali-dbhub")
+ builder.author("chat2db")
//执行完毕不打开文件夹
.disableOpenDir()
// 指定输出目录