后端重构

This commit is contained in:
Yvan
2026-01-14 14:34:49 +08:00
parent f5f77b32fe
commit 30842b42e5
274 changed files with 39754 additions and 1005 deletions
+569 -116
View File
@@ -21,6 +21,8 @@ import {
Alert,
Upload,
Modal,
Switch,
Tag,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import {
@@ -38,6 +40,7 @@ import {
HolderOutlined,
ReloadOutlined,
FileAddOutlined,
MenuOutlined,
} from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import { useParams, useSearchParams, useNavigate } from '@umijs/max';
@@ -51,10 +54,13 @@ import {
getMeta,
llmAuto,
eye,
getTemplates,
type AutoCodeParams,
} from '@/services/kratos/autoCode';
import { getDict } from '@/utils/dictionary';
import { getSysDictionaryList } from '@/services/kratos/dictionary';
import WarningBar from '@/components/WarningBar';
import CodePreview from './components/CodePreview';
import styles from './index.less';
const { Panel } = Collapse;
@@ -81,6 +87,7 @@ const fieldTemplate = {
primaryKey: false,
clearable: true,
fieldSearchType: '',
fieldSearchHide: false,
fieldIndexType: '',
dictType: '',
onlyNumber: '',
@@ -181,11 +188,19 @@ interface FieldItem {
primaryKey: boolean;
clearable?: boolean;
fieldSearchType: string;
fieldSearchHide?: boolean;
fieldIndexType: string;
dictType?: string;
onlyNumber?: string;
disabled?: boolean;
dataSource?: any;
dataSource?: {
dbName: string;
association: number;
table: string;
label: string;
value: string;
hasDeletedAt: boolean;
};
}
interface FormData {
@@ -208,6 +223,7 @@ interface FormData {
generateServer: boolean;
treeJson: string;
humpPackageName?: string;
templateType: string; // 模板类型:package 或 plugin
fields: FieldItem[];
}
@@ -233,6 +249,9 @@ const AutoCodePage: React.FC = () => {
// 模板列表
const [pkgs, setPkgs] = useState<any[]>([]);
// 模板类型选项
const [templateTypes, setTemplateTypes] = useState<string[]>(['package', 'plugin']);
// 字段类型映射
const [fdMap, setFdMap] = useState<Record<string, string>>({});
@@ -256,6 +275,7 @@ const AutoCodePage: React.FC = () => {
generateWeb: true,
generateServer: true,
treeJson: '',
templateType: 'package', // 默认使用 package 模板
fields: [],
});
@@ -273,6 +293,14 @@ const AutoCodePage: React.FC = () => {
// TextArea焦点状态
const [textareaFocused, setTextareaFocused] = useState(false);
// 字典选项列表
const [dictOptions, setDictOptions] = useState<any[]>([]);
// 数据源相关状态
const [dbTableList, setDbTableList] = useState<any[]>([]);
const [dbColumnList, setDbColumnList] = useState<any[]>([]);
const [dataSourceCollapsed, setDataSourceCollapsed] = useState<string[]>([]);
// 键盘快捷键处理 - Ctrl+Enter 或 Cmd+Enter 直接生成
const handleKeydown = useCallback((event: KeyboardEvent) => {
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
@@ -361,6 +389,8 @@ const AutoCodePage: React.FC = () => {
await getDbFunc();
await setFdMapFunc();
await getPkgs();
await getDictOptionsFunc();
await getTemplateTypesFunc();
// 从路由获取ID
const id = params.id;
@@ -372,6 +402,30 @@ const AutoCodePage: React.FC = () => {
getCatch();
};
// 获取模板类型列表
const getTemplateTypesFunc = async () => {
try {
const res = await getTemplates();
if (res.code === 0 && res.data) {
setTemplateTypes(res.data || ['package', 'plugin']);
}
} catch (error) {
console.error('获取模板类型失败:', error);
}
};
// 获取字典选项列表
const getDictOptionsFunc = async () => {
try {
const res = await getSysDictionaryList({ page: 1, pageSize: 999999 } as any);
if (res.code === 0 && res.data) {
setDictOptions(res.data.list || res.data || []);
}
} catch (error) {
console.error('获取字典列表失败:', error);
}
};
// 获取数据库列表
const getDbFunc = async () => {
try {
@@ -644,6 +698,176 @@ const AutoCodePage: React.FC = () => {
}
};
// 自动填充字段Json和数据库字段
const autoFillField = () => {
if (!dialogMiddle.fieldName) {
message.warning('请先输入字段名称');
return;
}
const fieldJson = toLowerCase(dialogMiddle.fieldName);
const columnName = toSQLLine(fieldJson);
setDialogMiddle(prev => ({
...prev,
fieldJson,
columnName,
}));
};
// 清除字段类型相关选项
const clearFieldTypeOptions = () => {
setDialogMiddle(prev => ({
...prev,
fieldSearchType: '',
dictType: '',
}));
};
// 数据源关联模式变更
const handleAssociationChange = (val: number) => {
if (val === 2) {
Modal.confirm({
title: '提示',
content: '一对多关联模式下,数据类型会改变为数组,后端表现为json,具体表现为数组模式,是否继续?',
onOk: () => {
setDialogMiddle(prev => ({
...prev,
fieldType: 'array',
dataSource: { ...prev.dataSource!, association: 2 },
}));
},
onCancel: () => {
setDialogMiddle(prev => ({
...prev,
dataSource: { ...prev.dataSource!, association: 1 },
}));
},
});
} else {
setDialogMiddle(prev => ({
...prev,
dataSource: { ...prev.dataSource!, association: val },
}));
}
};
// 获取数据源表列表
const getDBTableListFunc = async (dbName?: string) => {
try {
const res = await getTable({ businessDB: dbName || dialogMiddle.dataSource?.dbName || '' });
if (res.code === 0 && res.data) {
setDbTableList((res.data.tables || []).map((item: any) => ({
tableName: item.tableName,
value: item.tableName,
})));
}
} catch (error) {
console.error('获取表列表失败:', error);
}
};
// 选择数据源表
const selectDataSourceTable = async (tableName: string, isInit?: boolean) => {
if (!tableName) {
setDbColumnList([]);
setDialogMiddle(prev => ({
...prev,
dataSource: {
...prev.dataSource!,
table: '',
label: '',
value: '',
hasDeletedAt: false,
},
}));
return;
}
try {
const res = await getColumn({
businessDB: dialogMiddle.dataSource?.dbName || '',
tableName,
});
if (res.code === 0 && res.data) {
let hasDeletedAt = false;
const columns = (res.data.columns || []).map((item: any) => {
if (item.columnName === 'deleted_at') {
hasDeletedAt = true;
}
return {
columnName: item.columnName,
value: item.columnName,
type: item.dataType,
isPrimary: item.primaryKey,
comment: item.columnComment,
};
});
setDbColumnList(columns);
if (!isInit && columns.length > 0) {
setDialogMiddle(prev => ({
...prev,
dataSource: {
...prev.dataSource!,
table: tableName,
label: columns[0].columnName,
value: columns[0].columnName,
hasDeletedAt,
},
}));
} else {
setDialogMiddle(prev => ({
...prev,
dataSource: {
...prev.dataSource!,
table: tableName,
hasDeletedAt,
},
}));
}
}
} catch (error) {
console.error('获取字段列表失败:', error);
}
};
// 数据源数据库变更
const handleDataSourceDbChange = (dbName: string) => {
setDialogMiddle(prev => ({
...prev,
dataSource: {
...prev.dataSource!,
dbName,
table: '',
label: '',
value: '',
},
}));
setDbColumnList([]);
if (dbName) {
getDBTableListFunc(dbName);
}
};
// 清除数据源选择
const clearDataSourceAccess = () => {
setDialogMiddle(prev => ({
...prev,
dataSource: {
...prev.dataSource!,
label: '',
value: '',
},
}));
};
// 拖拽排序处理
const moveField = (dragIndex: number, hoverIndex: number) => {
const newFields = [...form.fields];
const [draggedItem] = newFields.splice(dragIndex, 1);
newFields.splice(hoverIndex, 0, draggedItem);
setForm(prev => ({ ...prev, fields: newFields }));
};
// 新增/编辑字段
const editAndAddField = (item?: FieldItem) => {
@@ -662,6 +886,11 @@ const AutoCodePage: React.FC = () => {
}
setBk(JSON.parse(JSON.stringify(item)));
setDialogMiddle(item);
// 如果有数据源表,加载字段列表
if (item.dataSource?.table) {
getDBTableListFunc(item.dataSource.dbName);
selectDataSourceTable(item.dataSource.table, true);
}
} else {
setAddFlag('add');
const newField = {
@@ -669,6 +898,8 @@ const AutoCodePage: React.FC = () => {
onlyNumber: getOnlyNumber(),
} as FieldItem;
setDialogMiddle(newField);
setDbTableList([]);
setDbColumnList([]);
}
};
@@ -767,6 +998,7 @@ const AutoCodePage: React.FC = () => {
generateWeb: true,
generateServer: true,
treeJson: '',
templateType: 'package',
fields: [],
});
sessionStorage.removeItem('autoCode');
@@ -950,7 +1182,14 @@ const AutoCodePage: React.FC = () => {
title: '',
key: 'drag',
width: 50,
render: () => <HolderOutlined className={styles.dragHandle} />,
render: (_, __, index) => (
<Tooltip title="拖拽排序">
<MenuOutlined
className={styles.dragHandle}
style={{ cursor: 'move', color: '#999' }}
/>
</Tooltip>
),
},
{ title: '序列', key: 'index', width: 60, render: (_, __, index) => index + 1 },
{
@@ -1131,11 +1370,39 @@ const AutoCodePage: React.FC = () => {
onChange={(value) => {
const newFields = [...form.fields];
newFields[index].fieldType = value;
// 清除相关选项
newFields[index].fieldSearchType = '';
newFields[index].dictType = '';
setForm(prev => ({ ...prev, fields: newFields }));
}}
/>
),
},
{
title: '字典类型',
dataIndex: 'dictType',
width: 140,
render: (val, record, index) => (
<Select
value={val}
disabled={record.disabled || (record.fieldType !== 'string' && record.fieldType !== 'array')}
allowClear
style={{ width: '100%' }}
placeholder="选择字典"
onChange={(value) => {
const newFields = [...form.fields];
newFields[index].dictType = value;
setForm(prev => ({ ...prev, fields: newFields }));
}}
>
{dictOptions.map((dict: any) => (
<Select.Option key={dict.type} value={dict.type}>
{`${dict.type}(${dict.name})`}
</Select.Option>
))}
</Select>
),
},
{
title: '索引类型',
dataIndex: 'fieldIndexType',
@@ -1497,6 +1764,30 @@ const AutoCodePage: React.FC = () => {
/>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item
label={
<Tooltip title="模板package会创建集成于项目本体中的代码包,模板plugin会创建插件包">
模板类型 <QuestionCircleOutlined />
</Tooltip>
}
required
>
<Select
value={form.templateType}
disabled={isAdd}
placeholder="请选择模板类型"
style={{ width: '100%' }}
onChange={(val) => setForm(prev => ({ ...prev, templateType: val }))}
>
{templateTypes.map((type) => (
<Select.Option key={type} value={type}>
{type === 'package' ? 'package (项目代码包)' : 'plugin (插件包)'}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={6}>
<Form.Item label="选择模板" required>
<Space.Compact style={{ width: '100%' }}>
@@ -1519,6 +1810,8 @@ const AutoCodePage: React.FC = () => {
</Space.Compact>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={6}>
<Form.Item
label={
@@ -1780,22 +2073,34 @@ const AutoCodePage: React.FC = () => {
</Space>
}
>
<Alert
message="id, created_at, updated_at, deleted_at 会自动生成请勿重复创建。搜索时如果条件为LIKE只支持字符串"
type="warning"
showIcon
style={{ marginBottom: 16 }}
/>
<Form layout="vertical">
<Row gutter={16}>
<Col span={12}>
<Form.Item label="字段名称" required>
<Input
value={dialogMiddle.fieldName}
placeholder="请输入字段名称"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, fieldName: e.target.value }))}
/>
<Space.Compact style={{ width: '100%' }}>
<Input
value={dialogMiddle.fieldName}
placeholder="请输入字段名称"
style={{ width: '80%' }}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, fieldName: e.target.value }))}
/>
<Button style={{ width: '20%' }} onClick={autoFillField}>
自动填充
</Button>
</Space.Compact>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="中文名">
<Form.Item label="字段中文名" required>
<Input
value={dialogMiddle.fieldDesc}
placeholder="请输入中文名"
placeholder="请输入字段中文名"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, fieldDesc: e.target.value }))}
/>
</Form.Item>
@@ -1803,35 +2108,25 @@ const AutoCodePage: React.FC = () => {
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="字段类型">
<Select
value={dialogMiddle.fieldType}
options={typeOptions}
placeholder="请选择字段类型"
onChange={(val) => setDialogMiddle(prev => ({ ...prev, fieldType: val }))}
<Form.Item label="字段JSON" required>
<Input
value={dialogMiddle.fieldJson}
placeholder="请输入字段JSON"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, fieldJson: e.target.value }))}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="字段Json">
<Form.Item label="数据库字段名" required>
<Input
value={dialogMiddle.fieldJson}
placeholder="请输入字段Json"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, fieldJson: e.target.value }))}
value={dialogMiddle.columnName}
placeholder="请输入数据库字段名"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, columnName: e.target.value }))}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="数据库字段">
<Input
value={dialogMiddle.columnName}
placeholder="请输入数据库字段"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, columnName: e.target.value }))}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="数据库字段描述">
<Input
@@ -1841,17 +2136,73 @@ const AutoCodePage: React.FC = () => {
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="字段类型" required>
<Select
value={dialogMiddle.fieldType}
options={typeOptions}
placeholder="请选择字段类型"
style={{ width: '100%' }}
onChange={(val) => {
setDialogMiddle(prev => ({ ...prev, fieldType: val }));
clearFieldTypeOptions();
}}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="字段长度/枚举值">
<Form.Item label={dialogMiddle.fieldType === 'enum' ? '枚举值' : '类型长度'}>
<Input
value={dialogMiddle.dataTypeLong}
placeholder="请输入字段长度或枚举值"
placeholder={dialogMiddle.fieldType === 'enum' ? "例:'北京','天津'" : '数据库类型长度'}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, dataTypeLong: e.target.value }))}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="字段查询条件">
<Select
value={dialogMiddle.fieldSearchType}
disabled={dialogMiddle.fieldType === 'json'}
allowClear
style={{ width: '100%' }}
placeholder="请选择字段查询条件"
onChange={(val) => setDialogMiddle(prev => ({ ...prev, fieldSearchType: val }))}
>
{typeSearchOptions.map(opt => (
<Select.Option
key={opt.value}
value={opt.value}
disabled={canSelect(dialogMiddle.fieldType, opt.value)}
>
{opt.label}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="关联字典">
<Select
value={dialogMiddle.dictType}
disabled={dialogMiddle.fieldType !== 'string' && dialogMiddle.fieldType !== 'array'}
allowClear
style={{ width: '100%' }}
placeholder="请选择字典"
onChange={(val) => setDialogMiddle(prev => ({ ...prev, dictType: val }))}
>
{dictOptions.map((dict: any) => (
<Select.Option key={dict.type} value={dict.type}>
{`${dict.type}(${dict.name})`}
</Select.Option>
))}
</Select>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="默认值">
<Input
@@ -1864,91 +2215,222 @@ const AutoCodePage: React.FC = () => {
</Row>
<Row gutter={16}>
<Col span={12}>
<Form.Item label="搜索条件">
<Form.Item label="索引类型">
<Select
value={dialogMiddle.fieldSearchType}
options={typeSearchOptions}
placeholder="请选择搜索条件"
value={dialogMiddle.fieldIndexType}
disabled={dialogMiddle.fieldType === 'json'}
options={typeIndexOptions}
placeholder="请选择字段索引类型"
allowClear
onChange={(val) => setDialogMiddle(prev => ({ ...prev, fieldSearchType: val }))}
style={{ width: '100%' }}
onChange={(val) => setDialogMiddle(prev => ({ ...prev, fieldIndexType: val }))}
/>
</Form.Item>
</Col>
<Col span={12}>
<Form.Item label="索引类型">
<Select
value={dialogMiddle.fieldIndexType}
options={typeIndexOptions}
placeholder="请选择索引类型"
allowClear
onChange={(val) => setDialogMiddle(prev => ({ ...prev, fieldIndexType: val }))}
<Form.Item label="校验失败文案">
<Input
value={dialogMiddle.errorText}
placeholder="请输入校验失败时的提示文案"
onChange={(e) => setDialogMiddle(prev => ({ ...prev, errorText: e.target.value }))}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={4}>
<Form.Item>
<Form.Item label="主键">
<Checkbox
checked={dialogMiddle.primaryKey}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, primaryKey: e.target.checked }))}
>
主键
</Checkbox>
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item>
<Checkbox
checked={dialogMiddle.require}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, require: e.target.checked }))}
>
必填
</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item>
<Checkbox
checked={dialogMiddle.sort}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, sort: e.target.checked }))}
>
排序
</Checkbox>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item>
<Checkbox
<Form.Item label="前端新建/编辑">
<Switch
checked={dialogMiddle.form}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, form: e.target.checked }))}
>
新建/编辑
</Checkbox>
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, form: checked }))}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item>
<Checkbox
<Form.Item label="前端表格列">
<Switch
checked={dialogMiddle.table}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, table: e.target.checked }))}
>
表格
</Checkbox>
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, table: checked }))}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item>
<Checkbox
<Form.Item label="前端详情">
<Switch
checked={dialogMiddle.desc}
onChange={(e) => setDialogMiddle(prev => ({ ...prev, desc: e.target.checked }))}
>
详情
</Checkbox>
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, desc: checked }))}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="导入/导出">
<Switch
checked={dialogMiddle.excel}
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, excel: checked }))}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="是否排序">
<Switch
checked={dialogMiddle.sort}
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, sort: checked }))}
/>
</Form.Item>
</Col>
</Row>
<Row gutter={16}>
<Col span={4}>
<Form.Item label="是否必填">
<Switch
checked={dialogMiddle.require}
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, require: checked }))}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="是否可清空">
<Switch
checked={dialogMiddle.clearable}
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, clearable: checked }))}
/>
</Form.Item>
</Col>
<Col span={4}>
<Form.Item label="隐藏查询条件">
<Switch
checked={dialogMiddle.fieldSearchHide}
disabled={!dialogMiddle.fieldSearchType}
onChange={(checked) => setDialogMiddle(prev => ({ ...prev, fieldSearchHide: checked }))}
/>
</Form.Item>
</Col>
</Row>
</Form>
{/* 数据源配置 */}
<Collapse
activeKey={dataSourceCollapsed}
onChange={(keys) => setDataSourceCollapsed(keys as string[])}
style={{ marginTop: 16 }}
>
<Panel
header="数据源配置(此配置为高级配置,如编程基础不牢,可能导致自动化代码不可用)"
key="dataSource"
>
<Row gutter={8}>
<Col span={4}>
<Select
value={dialogMiddle.dataSource?.dbName}
placeholder="数据库【不填则为KRA库】"
allowClear
style={{ width: '100%' }}
onChange={handleDataSourceDbChange}
>
{dbList.map((db) => (
<Select.Option key={db.aliasName} value={db.aliasName} disabled={db.disable}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>{db.aliasName}</span>
<span style={{ color: '#8492a6', fontSize: 13 }}>{db.dbName}</span>
</div>
</Select.Option>
))}
</Select>
</Col>
<Col span={4}>
<Select
value={dialogMiddle.dataSource?.association}
placeholder="关联模式"
style={{ width: '100%' }}
onChange={handleAssociationChange}
>
<Select.Option value={1}>一对一</Select.Option>
<Select.Option value={2}>一对多</Select.Option>
</Select>
</Col>
<Col span={5}>
<Select
value={dialogMiddle.dataSource?.table}
placeholder="请选择数据源表"
allowClear
showSearch
style={{ width: '100%' }}
onFocus={() => getDBTableListFunc()}
onChange={(val) => selectDataSourceTable(val)}
onClear={clearDataSourceAccess}
>
{dbTableList.map((t: any) => (
<Select.Option key={t.tableName} value={t.tableName}>
{t.tableName}
</Select.Option>
))}
</Select>
</Col>
<Col span={5}>
<Select
value={dialogMiddle.dataSource?.value}
placeholder="请先选择需要存储的数据"
style={{ width: '100%' }}
onChange={(val) => setDialogMiddle(prev => ({
...prev,
dataSource: { ...prev.dataSource!, value: val },
}))}
>
{dbColumnList.map((col: any) => (
<Select.Option key={col.columnName} value={col.columnName}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>
<Tag color={col.isPrimary ? 'blue' : 'default'} style={{ marginRight: 4 }}>
{col.isPrimary ? '主键' : '非主键'}
</Tag>
{col.columnName}
</span>
<span style={{ color: '#8492a6', fontSize: 12 }}>
{col.type}{col.comment ? ` - ${col.comment}` : ''}
</span>
</div>
</Select.Option>
))}
</Select>
</Col>
<Col span={5}>
<Select
value={dialogMiddle.dataSource?.label}
placeholder="请先选择需要展示的数据"
style={{ width: '100%' }}
onChange={(val) => setDialogMiddle(prev => ({
...prev,
dataSource: { ...prev.dataSource!, label: val },
}))}
>
{dbColumnList.map((col: any) => (
<Select.Option key={col.columnName} value={col.columnName}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span>
<Tag color={col.isPrimary ? 'blue' : 'default'} style={{ marginRight: 4 }}>
{col.isPrimary ? '主键' : '非主键'}
</Tag>
{col.columnName}
</span>
<span style={{ color: '#8492a6', fontSize: 12 }}>
{col.type}{col.comment ? ` - ${col.comment}` : ''}
</span>
</div>
</Select.Option>
))}
</Select>
</Col>
</Row>
</Panel>
</Collapse>
</Drawer>
{/* 预览抽屉 */}
@@ -1956,39 +2438,10 @@ const AutoCodePage: React.FC = () => {
title="代码预览"
open={previewVisible}
onClose={() => setPreviewVisible(false)}
width="80%"
extra={
<Space>
<Button
type="primary"
onClick={() => {
const pre = document.querySelector('.code-preview');
if (pre) {
const range = document.createRange();
range.selectNodeContents(pre);
const selection = window.getSelection();
selection?.removeAllRanges();
selection?.addRange(range);
}
}}
>
全选
</Button>
<Button
type="primary"
onClick={() => {
navigator.clipboard.writeText(JSON.stringify(previewCode, null, 2));
message.success('复制成功');
}}
>
复制
</Button>
</Space>
}
width="85%"
destroyOnClose
>
<pre className={`${styles.codePreview} code-preview`}>
{JSON.stringify(previewCode, null, 2)}
</pre>
<CodePreview previewCode={previewCode} isAdd={isAdd} />
</Drawer>
</PageContainer>
);