mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-08-01 06:16:04 +08:00
Merge branch 'developing' into team
This commit is contained in:
10
CHANGELOG.md
10
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
|
# 2.0.6
|
||||||
|
|
||||||
## 🐞 Bug Fixes
|
## 🐞 Bug Fixes
|
||||||
|
@ -195,15 +195,12 @@ export default function SettingAI(props: IProps) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<div className={styles.bottomButton}>
|
<div className={styles.bottomButton}>
|
||||||
{/* <Button >
|
|
||||||
|
|
||||||
</Button> */}
|
|
||||||
<Button type="primary" onClick={handleApplyAiConfig}>
|
<Button type="primary" onClick={handleApplyAiConfig}>
|
||||||
{i18n('setting.button.apply')}
|
{i18n('setting.button.apply')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{aiConfig?.aiSqlSource === AiSqlSourceType.CHAT2DBAI && !aiConfig.apiKey && <Popularize source="setting" />}
|
{/* {aiConfig?.aiSqlSource === AiSqlSourceType.CHAT2DBAI && !aiConfig.apiKey && <Popularize source="setting" />} */}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,12 +9,11 @@ import ProxySetting from './ProxySetting';
|
|||||||
import About from './About';
|
import About from './About';
|
||||||
import { connect } from 'umi';
|
import { connect } from 'umi';
|
||||||
import { IAIState } from '@/models/ai';
|
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 TestVersion from '@/components/TestVersion';
|
||||||
import { IAiConfig } from '@/typings';
|
import { IAiConfig } from '@/typings';
|
||||||
|
|
||||||
|
import styles from './index.less';
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
aiConfig: IAiConfig;
|
aiConfig: IAiConfig;
|
||||||
className?: string;
|
className?: string;
|
||||||
@ -28,6 +27,12 @@ function Setting(props: IProps) {
|
|||||||
|
|
||||||
const [currentMenu, setCurrentMenu] = useState(0);
|
const [currentMenu, setCurrentMenu] = useState(0);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isModalVisible) {
|
||||||
|
getAiSystemConfig();
|
||||||
|
}
|
||||||
|
}, [isModalVisible]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
getAiSystemConfig();
|
getAiSystemConfig();
|
||||||
}, []);
|
}, []);
|
||||||
|
@ -111,6 +111,7 @@
|
|||||||
// top: 0;
|
// top: 0;
|
||||||
// right: 0;
|
// right: 0;
|
||||||
// bottom: 0;
|
// bottom: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
i {
|
i {
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
@ -139,3 +140,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.monacoEditor {
|
||||||
|
height: 60vh;
|
||||||
|
}
|
||||||
|
@ -24,7 +24,7 @@ interface ITableProps {
|
|||||||
config: IResultConfig;
|
config: IResultConfig;
|
||||||
onConfigChange: (config: IResultConfig) => void;
|
onConfigChange: (config: IResultConfig) => void;
|
||||||
onSearchTotal: () => Promise<number | undefined>;
|
onSearchTotal: () => Promise<number | undefined>;
|
||||||
onExport: (exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => void;
|
onExport: (sql: string, originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IViewTableCellData {
|
interface IViewTableCellData {
|
||||||
@ -54,42 +54,46 @@ export default function TableBox(props: ITableProps) {
|
|||||||
const [appTheme] = useTheme();
|
const [appTheme] = useTheme();
|
||||||
const isDarkTheme = useMemo(() => appTheme.backgroundColor === ThemeType.Dark, [appTheme]);
|
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(
|
const items: MenuProps['items'] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
label: '导出全部数据为csv',
|
label: '导出全部数据为csv',
|
||||||
key: '1',
|
key: '1',
|
||||||
icon: <UserOutlined />,
|
// icon: <UserOutlined />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
props.onExport && props.onExport(ExportTypeEnum.CSV, ExportSizeEnum.ALL);
|
handleExport(ExportTypeEnum.CSV, ExportSizeEnum.ALL);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '导出全部数据为插入语句',
|
label: '导出全部数据为插入语句',
|
||||||
key: '2',
|
key: '2',
|
||||||
icon: <UserOutlined />,
|
// icon: <UserOutlined />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
props.onExport && props.onExport(ExportTypeEnum.INSERT, ExportSizeEnum.ALL);
|
handleExport(ExportTypeEnum.INSERT, ExportSizeEnum.ALL);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '导出当前页数据为csv',
|
label: '导出当前页数据为csv',
|
||||||
key: '3',
|
key: '3',
|
||||||
icon: <UserOutlined />,
|
// icon: <UserOutlined />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
props.onExport && props.onExport(ExportTypeEnum.CSV, ExportSizeEnum.CURRENT_PAGE);
|
handleExport(ExportTypeEnum.CSV, ExportSizeEnum.CURRENT_PAGE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '导出当前页数据为插入语句',
|
label: '导出当前页数据为插入语句',
|
||||||
key: '4',
|
key: '4',
|
||||||
icon: <UserOutlined />,
|
// icon: <UserOutlined />,
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
props.onExport && props.onExport(ExportTypeEnum.INSERT, ExportSizeEnum.CURRENT_PAGE);
|
handleExport(ExportTypeEnum.INSERT, ExportSizeEnum.CURRENT_PAGE);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
[],
|
[data],
|
||||||
);
|
);
|
||||||
|
|
||||||
const defaultSorts: SortItem[] = useMemo(
|
const defaultSorts: SortItem[] = useMemo(
|
||||||
@ -260,6 +264,7 @@ export default function TableBox(props: ITableProps) {
|
|||||||
open={!!viewTableCellData?.name}
|
open={!!viewTableCellData?.name}
|
||||||
onCancel={handleCancel}
|
onCancel={handleCancel}
|
||||||
width="60vw"
|
width="60vw"
|
||||||
|
height="70vh"
|
||||||
maskClosable={false}
|
maskClosable={false}
|
||||||
footer={
|
footer={
|
||||||
<>
|
<>
|
||||||
|
@ -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 classnames from 'classnames';
|
||||||
import Tabs, { IOption } from '@/components/Tabs';
|
import Tabs, { IOption } from '@/components/Tabs';
|
||||||
import Iconfont from '@/components/Iconfont';
|
import Iconfont from '@/components/Iconfont';
|
||||||
@ -16,7 +16,7 @@ interface IProps {
|
|||||||
manageResultDataList?: IManageResultData[];
|
manageResultDataList?: IManageResultData[];
|
||||||
resultConfig: IResultConfig[];
|
resultConfig: IResultConfig[];
|
||||||
onExecute: (sql: string, config: IResultConfig, index: number) => void;
|
onExecute: (sql: string, config: IResultConfig, index: number) => void;
|
||||||
onExport: (originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => Promise<void>;
|
onExport: (sql: string, originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => Promise<void>;
|
||||||
onTabEdit: (type: 'add' | 'remove', value?: number | string) => void;
|
onTabEdit: (type: 'add' | 'remove', value?: number | string) => void;
|
||||||
onSearchTotal: (index: number) => Promise<number>;
|
onSearchTotal: (index: number) => Promise<number>;
|
||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
@ -45,7 +45,7 @@ const handleTabs = (result: IManageResultData[]) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default memo<IProps>(function SearchResult(props) {
|
export default memo<IProps>(function SearchResult(props) {
|
||||||
const { className, manageResultDataList = [], isLoading, onExecute, onSearchTotal } = props;
|
const { className, manageResultDataList = [], isLoading, onExecute, onExport } = props;
|
||||||
const [currentTab, setCurrentTab] = useState<string | number | undefined>();
|
const [currentTab, setCurrentTab] = useState<string | number | undefined>();
|
||||||
const [resultDataList, setResultDataList] = useState<IManageResultData[]>([]);
|
const [resultDataList, setResultDataList] = useState<IManageResultData[]>([]);
|
||||||
const [resultConfig, setResultConfig] = useState<IResultConfig[]>([]);
|
const [resultConfig, setResultConfig] = useState<IResultConfig[]>([]);
|
||||||
@ -64,7 +64,7 @@ export default memo<IProps>(function SearchResult(props) {
|
|||||||
setCurrentTab(manageResultDataList[0].uuid);
|
setCurrentTab(manageResultDataList[0].uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
setResultDataList(manageResultDataList);
|
setResultDataList([...manageResultDataList]);
|
||||||
setTabs(handleTabs(manageResultDataList));
|
setTabs(handleTabs(manageResultDataList));
|
||||||
}, [manageResultDataList]);
|
}, [manageResultDataList]);
|
||||||
|
|
||||||
@ -85,48 +85,50 @@ export default memo<IProps>(function SearchResult(props) {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderTable = useMemo(() => {
|
const renderTable = () => {
|
||||||
if (!tabs || !tabs.length) {
|
if (!tabs || !tabs.length) {
|
||||||
return renderEmpty();
|
return renderEmpty();
|
||||||
}
|
}
|
||||||
if (!resultDataList || !resultDataList.length) {
|
if (!resultDataList || !resultDataList.length) {
|
||||||
return renderEmpty();
|
return renderEmpty();
|
||||||
}
|
}
|
||||||
return (resultDataList || []).map((item, index: number) => {
|
|
||||||
if (item.success) {
|
return renderTableContent;
|
||||||
return (
|
};
|
||||||
<Fragment key={item.uuid!}>
|
|
||||||
<TableBox
|
const renderTableContent = (resultDataList || []).map((item, index: number) => {
|
||||||
className={classnames({ [styles.cursorTableBox]: item.uuid === currentTab })}
|
console.log(item.sql, item.originalSql);
|
||||||
data={item}
|
if (item.success) {
|
||||||
config={resultConfig?.[index]}
|
return (
|
||||||
onConfigChange={function (config: IResultConfig) {
|
<TableBox
|
||||||
onExecute && onExecute(item.originalSql, config, index);
|
key={item.uuid!}
|
||||||
}}
|
className={classnames({ [styles.cursorTableBox]: item.uuid === currentTab })}
|
||||||
onSearchTotal={async () => {
|
data={item}
|
||||||
if (props.onSearchTotal) {
|
config={resultConfig?.[index]}
|
||||||
return await props.onSearchTotal(index);
|
onConfigChange={(config: IResultConfig) => {
|
||||||
}
|
onExecute && onExecute(item.originalSql, config, index);
|
||||||
}}
|
}}
|
||||||
onExport={(exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
|
onSearchTotal={async () => {
|
||||||
props.onExport && props.onExport(item.originalSql, exportType, exportSize);
|
if (props.onSearchTotal) {
|
||||||
}}
|
return await props.onSearchTotal(index);
|
||||||
/>
|
}
|
||||||
</Fragment>
|
}}
|
||||||
);
|
onExport={(sql: string, originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
|
||||||
} else {
|
onExport && onExport(sql, originalSql, exportType, exportSize);
|
||||||
return (
|
}}
|
||||||
<Fragment key={item.uuid}>
|
/>
|
||||||
<StateIndicator
|
);
|
||||||
className={classnames(styles.stateIndicator, { [styles.cursorStateIndicator]: item.uuid === currentTab })}
|
} else {
|
||||||
state="error"
|
return (
|
||||||
text={item.message}
|
<StateIndicator
|
||||||
/>
|
key={item.uuid}
|
||||||
</Fragment>
|
className={classnames(styles.stateIndicator, { [styles.cursorStateIndicator]: item.uuid === currentTab })}
|
||||||
);
|
state="error"
|
||||||
}
|
text={item.message}
|
||||||
});
|
/>
|
||||||
}, [currentTab, resultDataList, resultConfig]);
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classnames(className, styles.box)}>
|
<div className={classnames(className, styles.box)}>
|
||||||
@ -144,7 +146,7 @@ export default memo<IProps>(function SearchResult(props) {
|
|||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
<Spin spinning={isLoading} wrapperClassName={styles.resultContentWrapper}>
|
<Spin spinning={isLoading} wrapperClassName={styles.resultContentWrapper}>
|
||||||
{renderTable}
|
{renderTable()}
|
||||||
</Spin>
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -15,6 +15,7 @@ import { v4 as uuidV4 } from 'uuid';
|
|||||||
import sql from '@/service/sql';
|
import sql from '@/service/sql';
|
||||||
import { isNumber } from 'lodash';
|
import { isNumber } from 'lodash';
|
||||||
import { ExportSizeEnum, ExportTypeEnum } from '@/typings/resultTable';
|
import { ExportSizeEnum, ExportTypeEnum } from '@/typings/resultTable';
|
||||||
|
import { downloadFile } from '@/utils/common';
|
||||||
interface IProps {
|
interface IProps {
|
||||||
className?: string;
|
className?: string;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
@ -139,10 +140,14 @@ const WorkspaceRightItem = memo<IProps>(function (props) {
|
|||||||
return total;
|
return total;
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExportSQLResult = async (originalSql: string, exportType: ExportTypeEnum, exportSize: ExportSizeEnum) => {
|
const handleExportSQLResult = async (
|
||||||
const params: IExportParams = { ...data, originalSql, exportType, exportSize };
|
sql: string,
|
||||||
|
originalSql: string,
|
||||||
await sqlServer.exportResultTable(params);
|
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) => {
|
const handleResultTabEdit = (type: 'add' | 'remove', uuid?: string | number) => {
|
||||||
|
@ -123,7 +123,7 @@ export interface IExportParams extends IExecuteSqlParams {
|
|||||||
/**
|
/**
|
||||||
* 导出-表格
|
* 导出-表格
|
||||||
*/
|
*/
|
||||||
const exportResultTable = createRequest<IExportParams, any>('/api/rdb/dml/export', { method: 'post' });
|
// const exportResultTable = createRequest<IExportParams, any>('/api/rdb/dml/export', { method: 'post' });
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getList,
|
getList,
|
||||||
@ -142,5 +142,5 @@ export default {
|
|||||||
addTablePin,
|
addTablePin,
|
||||||
deleteTablePin,
|
deleteTablePin,
|
||||||
getDMLCount,
|
getDMLCount,
|
||||||
exportResultTable
|
// exportResultTable
|
||||||
};
|
};
|
||||||
|
@ -14,3 +14,45 @@ export function formatParams(obj: { [key: string]: any }) {
|
|||||||
});
|
});
|
||||||
return params.toString();
|
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);
|
||||||
|
|
||||||
|
// 创建一个隐藏的 <a> 标签,并设置其 href 属性
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.style.display = 'none';
|
||||||
|
a.href = blobUrl;
|
||||||
|
|
||||||
|
// 使用从响应头解析的文件名
|
||||||
|
a.download = filename;
|
||||||
|
|
||||||
|
// 将 <a> 标签附加到 DOM,并触发点击事件
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
|
||||||
|
// 清理:从 DOM 中移除 <a> 标签,并释放Blob URL
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(blobUrl);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error('下载文件失败:', error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -4,7 +4,6 @@ import ai.chat2db.server.tools.common.model.ConfigJson;
|
|||||||
import ai.chat2db.server.tools.common.util.ConfigUtils;
|
import ai.chat2db.server.tools.common.util.ConfigUtils;
|
||||||
import com.dtflys.forest.springboot.annotation.ForestScan;
|
import com.dtflys.forest.springboot.annotation.ForestScan;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.ArrayUtils;
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.mybatis.spring.annotation.MapperScan;
|
import org.mybatis.spring.annotation.MapperScan;
|
||||||
import org.springframework.boot.SpringApplication;
|
import org.springframework.boot.SpringApplication;
|
||||||
@ -37,7 +36,7 @@ public class Application {
|
|||||||
// Represents that the current version has been successfully launched
|
// Represents that the current version has been successfully launched
|
||||||
if (StringUtils.isNotBlank(currentVersion) && StringUtils.equals(currentVersion, configJson.getLatestStartupSuccessVersion())) {
|
if (StringUtils.isNotBlank(currentVersion) && StringUtils.equals(currentVersion, configJson.getLatestStartupSuccessVersion())) {
|
||||||
// Flyway doesn't need to start every time to increase startup speed
|
// 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.",
|
log.info("The current version {} has been successfully launched once and will no longer load Flyway.",
|
||||||
currentVersion);
|
currentVersion);
|
||||||
}
|
}
|
||||||
|
@ -37,13 +37,13 @@ public class MybatisGeneratorTest extends BaseTest {
|
|||||||
|
|
||||||
private void doGenerator(List<String> tableList) {
|
private void doGenerator(List<String> tableList) {
|
||||||
|
|
||||||
// 当前项目地址 拿到的是ali-dbhub-server-start地址
|
// 当前项目地址 拿到的是chat2db-server-start地址
|
||||||
String outputDir = System.getProperty("user.dir")
|
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";
|
+ "/java";
|
||||||
String xmlDir = System.getProperty("user.dir")
|
String xmlDir = System.getProperty("user.dir")
|
||||||
+ "/../ali-dbhub-server-domain/ali-dbhub-server-domain-repository/src/main"
|
+ "/../chat2db-server-domain/chat2db-server-domain-repository/src/main"
|
||||||
+ "/resources/com/alibaba/dbhub/server/domain/repository";
|
+ "/resources/ai/chat2db/server/domain/repository";
|
||||||
|
|
||||||
// 不要生成service controller
|
// 不要生成service controller
|
||||||
Map<OutputFile, String> pathInfo = new HashMap<>();
|
Map<OutputFile, String> pathInfo = new HashMap<>();
|
||||||
@ -58,7 +58,7 @@ public class MybatisGeneratorTest extends BaseTest {
|
|||||||
//全局配置
|
//全局配置
|
||||||
.globalConfig(builder -> {
|
.globalConfig(builder -> {
|
||||||
// 设置作者
|
// 设置作者
|
||||||
builder.author("ali-dbhub")
|
builder.author("chat2db")
|
||||||
//执行完毕不打开文件夹
|
//执行完毕不打开文件夹
|
||||||
.disableOpenDir()
|
.disableOpenDir()
|
||||||
// 指定输出目录
|
// 指定输出目录
|
||||||
|
Reference in New Issue
Block a user