mirror of
https://github.com/CodePhiliaX/Chat2DB.git
synced 2025-08-06 01:36:46 +08:00
feat: edit table
This commit is contained in:
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -23,6 +23,7 @@
|
||||
"echart",
|
||||
"echarts",
|
||||
"favicons",
|
||||
"fulltext",
|
||||
"ghostoy",
|
||||
"iconfont",
|
||||
"jdbc",
|
||||
|
47
CHANGELOG.md
47
CHANGELOG.md
@ -7,6 +7,8 @@
|
||||
function using 'docker'
|
||||
- Added support for environment selection, better distinguishing between online and daily
|
||||
|
||||
# 2.0.14
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Fix the issue of 'Oracle' query 'Blob' reporting errors
|
||||
@ -24,15 +26,22 @@
|
||||
|
||||
# 2.0.13
|
||||
|
||||
- 修改分页逻辑,修复部分 SQL 无法查询
|
||||
|
||||
# 2.0.13
|
||||
|
||||
## ⭐ New Features
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Fixed a bug where sql formatting was not selected
|
||||
- Fixed open view lag issue
|
||||
- Solve the white screen problem of connected non-relational databases (non-relational databases are not supported)
|
||||
## ⭐ 新特性
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复不选中sql格式化的bug
|
||||
- 修复不选中 sql 格式化的 bug
|
||||
- 修复打开视图卡顿问题
|
||||
- 解决已连接的非关系型数据库打开白屏问题(暂不支持非关系性数据库)
|
||||
|
||||
@ -50,7 +59,7 @@
|
||||
- Fixed an issue where locally stored theme colors and background colors are incompatible with the new version, causing
|
||||
page crashes
|
||||
- Logs desensitize sensitive data
|
||||
- Fix the issue of 'CLOB' not displaying specific content [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- Fix the issue of 'CLOB' not displaying specific content [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- Fix the problem that non-Select does not display query results
|
||||
- Fix the problem that Oracle cannot query without schema
|
||||
- Fix the problem of special type of SQL execution error reporting
|
||||
@ -58,19 +67,19 @@
|
||||
|
||||
## ⭐ 新特性
|
||||
|
||||
- 🔥支持查看视图、函数、触发器、存储过程
|
||||
- 支持选中sql格式化
|
||||
- 🔥 支持查看视图、函数、触发器、存储过程
|
||||
- 支持选中 sql 格式化
|
||||
- 增加新的暗色主题
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复sql格式化会失败问题
|
||||
- 修复 sql 格式化会失败问题
|
||||
- 修复本地存储的主题色、背景色与新版本不兼容时会导致页面崩溃问题
|
||||
- 日志对敏感数据进行脱敏
|
||||
- 修复 `CLOB` 不展示具体内容的问题 [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- 修复非Select不展示查询结果的问题
|
||||
- 修复Oracle不带schema无法查询的问题
|
||||
- 修复特殊类型的SQL执行报错的问题
|
||||
- 修复 `CLOB` 不展示具体内容的问题 [Issue #440](https://github.com/chat2db/Chat2DB/issues/440)
|
||||
- 修复非 Select 不展示查询结果的问题
|
||||
- 修复 Oracle 不带 schema 无法查询的问题
|
||||
- 修复特殊类型的 SQL 执行报错的问题
|
||||
- 修复测试链接成功,但保存链接报错的问题
|
||||
|
||||
# 2.0.11
|
||||
@ -98,11 +107,11 @@
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 新建、开打console时激活最新操作的console、记录最后一次使用的console
|
||||
- edge等浏览器复制功能无法正常使用
|
||||
- table搜索后导出ddl报错
|
||||
- 新建、开打 console 时激活最新操作的 console、记录最后一次使用的 console
|
||||
- edge 等浏览器复制功能无法正常使用
|
||||
- table 搜索后导出 ddl 报错
|
||||
- 增加表注释以及列字段类型和注释
|
||||
- 当数据源添加了database默认选择第一个database
|
||||
- 当数据源添加了 database 默认选择第一个 database
|
||||
|
||||
# 2.0.9
|
||||
|
||||
@ -112,18 +121,18 @@
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复windows闪退的问题
|
||||
- 修复 windows 闪退的问题
|
||||
|
||||
# 2.0.8
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Repair the Scientific notation in some databases [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- Repair the Scientific notation in some databases [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- Fix some cases where data is not displayed
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复部分数据库出现科学计数法的情况 [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- 修复部分数据库出现科学计数法的情况 [Issue #378](https://github.com/chat2db/Chat2DB/issues/378)
|
||||
- 修复部分情况数据不展示
|
||||
|
||||
# 2.0.7
|
||||
@ -142,18 +151,18 @@
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- 修复ai配置 [Issue #346](https://github.com/chat2db/Chat2DB/issues/346)
|
||||
- 修复 ai 配置 [Issue #346](https://github.com/chat2db/Chat2DB/issues/346)
|
||||
|
||||
# 2.0.6
|
||||
|
||||
## 🐞 Bug Fixes
|
||||
|
||||
- Fixed: When there are too many tables under the selected library, the "New Console" button at the bottom
|
||||
disappears [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
disappears [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
|
||||
## 🐞 问题修复
|
||||
|
||||
- Fixed: 当选择的库下面表过多时最下面的“新建控制台”按钮消失 [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
- Fixed: 当选择的库下面表过多时最下面的“新建控制台”按钮消失 [Issue #314](https://github.com/chat2db/Chat2DB/issues/314)
|
||||
|
||||
# 2.0.5
|
||||
|
||||
|
2
chat2db-client/.vscode/settings.json
vendored
2
chat2db-client/.vscode/settings.json
vendored
@ -37,7 +37,9 @@
|
||||
"echarts",
|
||||
"favicons",
|
||||
"findstr",
|
||||
"fulltext",
|
||||
"gtag",
|
||||
"hexi",
|
||||
"Iconfont",
|
||||
"indexs",
|
||||
"JDBC",
|
||||
|
@ -25,6 +25,7 @@
|
||||
"start:web": "cross-env UMI_ENV=local cross-env APP_VERSION=${npm_config_app_version} umi dev"
|
||||
},
|
||||
"dependencies": {
|
||||
"@dnd-kit/modifiers": "^6.0.1",
|
||||
"ahooks": "^3.7.7",
|
||||
"ali-react-table": "^2.6.1",
|
||||
"antd": "^5.6.0",
|
||||
@ -55,7 +56,6 @@
|
||||
"@umijs/plugins": "^4.0.55",
|
||||
"concurrently": "^8.1.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"electron": "^22.3.0",
|
||||
"electron-builder": "^23.6.0",
|
||||
"electron-debug": "^3.2.0",
|
||||
"electron-reload": "^2.0.0-alpha.1",
|
||||
|
@ -0,0 +1,9 @@
|
||||
@import '../../../styles/var.less';
|
||||
|
||||
.box {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.formBox {
|
||||
width: 50%;
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
import React, { memo, useState, useContext, useEffect, useImperativeHandle, ForwardedRef, forwardRef } from 'react';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import { Form, Input } from 'antd';
|
||||
import { Context } from '../index';
|
||||
import { IBaseInfo } from '@/typings';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
|
||||
export interface IBaseInfoRef {
|
||||
getBaseInfo: () => IBaseInfo;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const BaseInfo = forwardRef((props: IProps, ref: ForwardedRef<IBaseInfoRef>) => {
|
||||
const { className } = props;
|
||||
const { tableDetails } = useContext(Context);
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
name: tableDetails.name,
|
||||
comment: tableDetails.comment,
|
||||
});
|
||||
}, [tableDetails]);
|
||||
|
||||
function getBaseInfo(): IBaseInfo {
|
||||
return form.getFieldsValue();
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getBaseInfo,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={classnames(className, styles.box)}>
|
||||
<div className={styles.formBox}>
|
||||
<Form form={form} initialValues={{ remember: true }} autoComplete="off" className={styles.form}>
|
||||
<Form.Item label={i18n('editTable.label.tableName')} name="name">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item label={i18n('editTable.label.comment')} name="comment">
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default BaseInfo
|
@ -0,0 +1,36 @@
|
||||
@import '../../../styles/var.less';
|
||||
|
||||
.box {
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.columnListHeader {
|
||||
margin: 0px -5px 10px;
|
||||
button {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.tableBox {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.editableCell {
|
||||
height: 28px;
|
||||
line-height: 26px;
|
||||
padding: 0px 7px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
.f-single-line();
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
@ -0,0 +1,361 @@
|
||||
import React, { memo, useContext, useEffect, useState, forwardRef, ForwardedRef, useImperativeHandle } from 'react';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import { MenuOutlined } from '@ant-design/icons';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { DndContext } from '@dnd-kit/core';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import { Table, InputNumber, Input, Form, Select, Checkbox, Button } from 'antd';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import sqlService from '@/service/sql';
|
||||
import { Context } from '../index';
|
||||
import { IColumnItem } from '@/typings'
|
||||
import i18n from '@/i18n';
|
||||
|
||||
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||
'data-row-key': string;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
|
||||
}
|
||||
|
||||
export interface IColumnListRef {
|
||||
getColumnListInfo: () => IColumnItem[];
|
||||
}
|
||||
|
||||
const Row = ({ children, ...props }: RowProps) => {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
setActivatorNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({
|
||||
id: props['data-row-key'],
|
||||
});
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
...props.style,
|
||||
transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),
|
||||
transition,
|
||||
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<tr {...props} ref={setNodeRef} style={style} {...attributes}>
|
||||
{React.Children.map(children, (child) => {
|
||||
if ((child as React.ReactElement).key === 'sort') {
|
||||
return React.cloneElement(child as React.ReactElement, {
|
||||
children: (
|
||||
<MenuOutlined
|
||||
ref={setActivatorNodeRef}
|
||||
style={{ touchAction: 'none', cursor: 'move' }}
|
||||
{...listeners}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
return child;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const createInitialData = () => {
|
||||
return {
|
||||
key: uuidv4(),
|
||||
name: null,
|
||||
columnSize: null,
|
||||
columnType: null,
|
||||
nullable: null,
|
||||
comment: null,
|
||||
primaryKey: null,
|
||||
defaultValue: null,
|
||||
dataType: null,
|
||||
autoIncrement: null,
|
||||
numericPrecision: null,
|
||||
numericScale: null,
|
||||
characterMaximumLength: null,
|
||||
};
|
||||
}
|
||||
|
||||
const ColumnList = forwardRef((props: IProps, ref: ForwardedRef<IColumnListRef>) => {
|
||||
const { dataSourceId, databaseName, tableDetails } = useContext(Context);
|
||||
const [dataSource, setDataSource] = useState<IColumnItem[]>([createInitialData()]);
|
||||
const [form] = Form.useForm();
|
||||
const [editingKey, setEditingKey] = useState('');
|
||||
const [databaseFieldTypeList, setDatabaseFieldTypeList] = useState<string[]>([])
|
||||
|
||||
const isEditing = (record: IColumnItem) => record.key === editingKey;
|
||||
|
||||
const edit = (record: Partial<IColumnItem> & { key: React.Key }) => {
|
||||
form.setFieldsValue({ ...record });
|
||||
setEditingKey(record.key);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (tableDetails) {
|
||||
const list = tableDetails?.columnList?.map(t => {
|
||||
return {
|
||||
...t,
|
||||
key: uuidv4(),
|
||||
}
|
||||
}) || []
|
||||
setDataSource(list)
|
||||
}
|
||||
}, [tableDetails])
|
||||
|
||||
useEffect(() => {
|
||||
// 获取数据库字段类型列表
|
||||
sqlService.getDatabaseFieldTypeList({
|
||||
dataSourceId,
|
||||
databaseName,
|
||||
}).then(res => {
|
||||
setDatabaseFieldTypeList(res.map(i => i.typeName))
|
||||
})
|
||||
}, [])
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'sort',
|
||||
width: '40px',
|
||||
align: 'center'
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.columnName'),
|
||||
dataIndex: 'name',
|
||||
width: '160px',
|
||||
render: (text: string, record: IColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item
|
||||
name="name"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.columnSize'),
|
||||
dataIndex: 'columnSize',
|
||||
width: '120px',
|
||||
render: (text: string, record: IColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item
|
||||
name="columnSize"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<InputNumber />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.columnType'),
|
||||
dataIndex: 'columnType',
|
||||
width: '200px',
|
||||
render: (text: string, record: IColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return <div>
|
||||
{
|
||||
editable ? (
|
||||
<Form.Item
|
||||
name="columnType"
|
||||
style={{ margin: 0, maxWidth: '184px' }}
|
||||
>
|
||||
<Select
|
||||
options={databaseFieldTypeList.map((i) => ({ label: i, value: i }))}
|
||||
/>
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div
|
||||
style={{ maxWidth: '184px' }}
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.nullable'),
|
||||
dataIndex: 'nullable',
|
||||
width: '100px',
|
||||
render: (nullable: number, record: IColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item
|
||||
name='nullable'
|
||||
style={{ margin: 0 }}
|
||||
valuePropName='checked'
|
||||
>
|
||||
<Checkbox checked={nullable === 1} />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
<Checkbox checked={nullable === 1} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.comment'),
|
||||
dataIndex: 'comment',
|
||||
render: (text: string, record: IColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item
|
||||
name="comment"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (active.id !== over?.id) {
|
||||
setDataSource((previous) => {
|
||||
const activeIndex = previous.findIndex((i) => i.key === active.id);
|
||||
const overIndex = previous.findIndex((i) => i.key === over?.id);
|
||||
return arrayMove(previous, activeIndex, overIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const handelFieldsChange = (field: any) => {
|
||||
let { name: nameList, value } = field[0];
|
||||
const name = nameList[0];
|
||||
if (name === 'nullable') {
|
||||
value = value ? 1 : 0
|
||||
}
|
||||
const newData = dataSource.map((item) => {
|
||||
if (item.key === editingKey) {
|
||||
return {
|
||||
...item,
|
||||
[name]: value,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setDataSource(newData);
|
||||
}
|
||||
|
||||
const addData = () => {
|
||||
const newData = createInitialData()
|
||||
setDataSource([...dataSource, newData])
|
||||
edit(newData)
|
||||
}
|
||||
|
||||
const deleteData = () => {
|
||||
setDataSource(dataSource.filter(i => i.key !== editingKey))
|
||||
}
|
||||
|
||||
const moveData = (action: 'up' | 'down') => {
|
||||
const index = dataSource.findIndex(i => i.key === editingKey)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
if (action === 'up') {
|
||||
if (index === 0) {
|
||||
return
|
||||
}
|
||||
const newData = [...dataSource]
|
||||
newData[index] = dataSource[index - 1]
|
||||
newData[index - 1] = dataSource[index]
|
||||
setDataSource(newData)
|
||||
} else {
|
||||
if (index === dataSource.length - 1) {
|
||||
return
|
||||
}
|
||||
const newData = [...dataSource]
|
||||
newData[index] = dataSource[index + 1]
|
||||
newData[index + 1] = dataSource[index]
|
||||
setDataSource(newData)
|
||||
}
|
||||
}
|
||||
|
||||
function getColumnListInfo(): IColumnItem[] {
|
||||
return dataSource
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getColumnListInfo,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={styles.box}>
|
||||
<div className={styles.columnListHeader}>
|
||||
<Button onClick={addData}>{i18n('editTable.button.add')}</Button>
|
||||
<Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button>
|
||||
<Button onClick={moveData.bind(null, 'up')}>{i18n('editTable.button.up')}</Button>
|
||||
<Button onClick={moveData.bind(null, 'down')}>{i18n('editTable.button.down')}</Button>
|
||||
</div>
|
||||
<div className={styles.tableBox}>
|
||||
<Form form={form} onFieldsChange={handelFieldsChange}>
|
||||
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
||||
<SortableContext
|
||||
items={dataSource.map((i) => i.key)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<Table
|
||||
components={{
|
||||
body: {
|
||||
row: Row,
|
||||
},
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="key"
|
||||
columns={columns as any}
|
||||
dataSource={dataSource}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
||||
export default ColumnList;
|
@ -0,0 +1,33 @@
|
||||
@import '../../../styles/var.less';
|
||||
|
||||
.box {
|
||||
max-height: 60vh;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.ant-input-number {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.indexListHeader {
|
||||
margin: 0px -10px 10px;
|
||||
button {
|
||||
margin: 0px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.editableCell {
|
||||
height: 28px;
|
||||
line-height: 26px;
|
||||
padding: 0px 7px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
.f-single-line();
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
@ -0,0 +1,179 @@
|
||||
import React, { memo, useMemo, useState, useContext, useEffect, forwardRef, ForwardedRef, useImperativeHandle } from 'react';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import { Table, InputNumber, Input, Form, Select, Checkbox, Button, Modal, message } from 'antd';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { Context } from '../index';
|
||||
import { IColumnItem, IIndexIncludeColumnItem } from '@/typings';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
interface IProps {
|
||||
includedColumnList: IIndexIncludeColumnItem[];
|
||||
}
|
||||
|
||||
const createInitialData = () => {
|
||||
return {
|
||||
key: uuidv4(),
|
||||
indexName: null,
|
||||
tableName: null,
|
||||
type: null,
|
||||
columnName: null,
|
||||
comment: null,
|
||||
ordinalPosition: null,
|
||||
collation: null,
|
||||
schemaName: null,
|
||||
databaseName: '',
|
||||
nonUnique: true,
|
||||
indexQualifier: null,
|
||||
ascOrDesc: null,
|
||||
cardinality: null,
|
||||
pages: null,
|
||||
filterCondition: null,
|
||||
prefixLength: null
|
||||
};
|
||||
};
|
||||
|
||||
export interface IIncludeColRef {
|
||||
getIncludeColInfo: () => IIndexIncludeColumnItem[];
|
||||
}
|
||||
|
||||
const InitialDataSource = [createInitialData()];
|
||||
|
||||
const IncludeCol = forwardRef((props: IProps, ref: ForwardedRef<IIncludeColRef>) => {
|
||||
const { includedColumnList } = props;
|
||||
const { columnListRef } = useContext(Context);
|
||||
const [dataSource, setDataSource] = useState<IIndexIncludeColumnItem[]>(InitialDataSource);
|
||||
const [form] = Form.useForm();
|
||||
const [editingKey, setEditingKey] = useState(dataSource[0]?.key);
|
||||
const isEditing = (record: IIndexIncludeColumnItem) => record.key === editingKey;
|
||||
|
||||
useEffect(() => {
|
||||
if (includedColumnList.length) {
|
||||
setDataSource(
|
||||
includedColumnList.map(t => {
|
||||
return {
|
||||
...t,
|
||||
key: uuidv4(),
|
||||
};
|
||||
}),
|
||||
);
|
||||
}
|
||||
}, [includedColumnList]);
|
||||
|
||||
const columnList: IColumnItem[] = useMemo(() => {
|
||||
const columnListInfo = columnListRef.current?.getColumnListInfo()?.filter(i => i.name);
|
||||
return columnListInfo || [];
|
||||
}, []);
|
||||
|
||||
const edit = (record: Partial<IIndexIncludeColumnItem> & { key: React.Key }) => {
|
||||
form.setFieldsValue({ ...record });
|
||||
setEditingKey(record.key);
|
||||
};
|
||||
|
||||
const addData = () => {
|
||||
const newData = createInitialData();
|
||||
setDataSource([...dataSource, newData]);
|
||||
edit(newData);
|
||||
};
|
||||
|
||||
const deleteData = () => {
|
||||
// if (dataSource.length === 1) {
|
||||
// message.warning('至少保留一条数据')
|
||||
// return
|
||||
// }
|
||||
|
||||
setDataSource(dataSource.filter((i) => i.key !== editingKey));
|
||||
};
|
||||
|
||||
const columns = [
|
||||
{
|
||||
title: i18n('editTable.label.index'),
|
||||
dataIndex: 'index',
|
||||
width: '10%',
|
||||
render: (text: string, record: IIndexIncludeColumnItem) => {
|
||||
return dataSource.findIndex((i) => i.key === record.key) + 1;
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.columnName'),
|
||||
dataIndex: 'columnName',
|
||||
width: '45%',
|
||||
render: (text: string, record: IIndexIncludeColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item name="columnName" style={{ margin: 0 }}>
|
||||
<Select options={columnList.map((i) => ({ label: i.name, value: i.name }))} />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className={styles.editableCell} onClick={() => edit(record)}>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.prefixLength'),
|
||||
dataIndex: 'prefixLength',
|
||||
width: '45%',
|
||||
render: (text: string, record: IIndexIncludeColumnItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item name="prefixLength" style={{ margin: 0 }}>
|
||||
<InputNumber style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<div className={styles.editableCell} onClick={() => edit(record)}>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const onValuesChange = (changedValues: any, allValues: any) => {
|
||||
const newDataSource = dataSource?.map((i) => {
|
||||
if (i.key === editingKey) {
|
||||
return {
|
||||
...i,
|
||||
...allValues,
|
||||
};
|
||||
}
|
||||
return i;
|
||||
});
|
||||
setDataSource(newDataSource);
|
||||
};
|
||||
|
||||
const getIncludeColInfo = () => {
|
||||
const includeColInfo: IIndexIncludeColumnItem[] = [];
|
||||
dataSource.forEach(t => {
|
||||
columnList.forEach(columnItem => {
|
||||
if (t.columnName === columnItem.name) {
|
||||
includeColInfo.push({
|
||||
...createInitialData(),
|
||||
...columnItem,
|
||||
columnName: t.columnName,
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
return includeColInfo;
|
||||
};
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getIncludeColInfo,
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className={classnames(styles.box)}>
|
||||
<div className={styles.indexListHeader}>
|
||||
<Button onClick={addData}>{i18n('editTable.button.add')}</Button>
|
||||
<Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button>
|
||||
</div>
|
||||
<Form form={form} onValuesChange={onValuesChange}>
|
||||
<Table pagination={false} rowKey="key" columns={columns} dataSource={dataSource} />
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default IncludeCol;
|
@ -0,0 +1,37 @@
|
||||
@import '../../../styles/var.less';
|
||||
|
||||
.box {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.indexListHeader {
|
||||
margin: 0px -5px 10px;
|
||||
button {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
}
|
||||
.editableCell {
|
||||
height: 28px;
|
||||
line-height: 26px;
|
||||
padding: 0px 7px;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 4px;
|
||||
.f-single-line();
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border: 1px solid var(--color-border);
|
||||
}
|
||||
}
|
||||
|
||||
.columnListCell {
|
||||
span {
|
||||
margin-right: 8px;
|
||||
color: var(--color-primary);
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: var(--color-primary-hover);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,337 @@
|
||||
import React, { memo, useState, forwardRef, ForwardedRef, useImperativeHandle, useContext, useRef, useMemo, useEffect } from 'react';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import { MenuOutlined } from '@ant-design/icons';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
import { DndContext } from '@dnd-kit/core';
|
||||
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
|
||||
import {
|
||||
arrayMove,
|
||||
SortableContext,
|
||||
useSortable,
|
||||
verticalListSortingStrategy,
|
||||
} from '@dnd-kit/sortable';
|
||||
import { CSS } from '@dnd-kit/utilities';
|
||||
import { Table, InputNumber, Input, Form, Select, Checkbox, Button, Modal } from 'antd';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import IncludeCol, { IIncludeColRef } from '../IncludeCol';
|
||||
import { IColumnItem, IIndexItem, IIndexIncludeColumnItem } from '@/typings';
|
||||
import { IndexesType } from '@/constants';
|
||||
import { Context } from '../index';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
const indexesTypeList = [IndexesType['Normal'], IndexesType['Unique'], IndexesType['Fulltext'], IndexesType['Spatial']]
|
||||
|
||||
interface IProps {
|
||||
|
||||
}
|
||||
|
||||
export type IIndexListInfo = IIndexItem[];
|
||||
|
||||
export interface IIndexListRef {
|
||||
getIndexListInfo: () => IIndexListInfo;
|
||||
}
|
||||
|
||||
const createInitialData = (): IIndexItem => {
|
||||
return {
|
||||
key: uuidv4(),
|
||||
columnList: [],
|
||||
name: '',
|
||||
type: null,
|
||||
columns: null,
|
||||
comment: null,
|
||||
}
|
||||
}
|
||||
|
||||
interface RowProps extends React.HTMLAttributes<HTMLTableRowElement> {
|
||||
'data-row-key': string;
|
||||
}
|
||||
|
||||
const IndexList = forwardRef((props: IProps, ref: ForwardedRef<IIndexListRef>) => {
|
||||
const { tableDetails, columnListRef } = useContext(Context);
|
||||
const [dataSource, setDataSource] = useState<IIndexItem[]>([createInitialData()]);
|
||||
const [form] = Form.useForm();
|
||||
const [editingKey, setEditingKey] = useState(dataSource[0]?.key);
|
||||
const [includeColModalOpen, setIncludeColModalOpen] = useState(false);
|
||||
const includeColRef = useRef<IIncludeColRef>(null);
|
||||
|
||||
const isEditing = (record: IIndexItem) => record.key === editingKey;
|
||||
|
||||
const edit = (record: Partial<IIndexItem> & { key?: React.Key }) => {
|
||||
form.setFieldsValue({ ...record });
|
||||
setEditingKey(record.key);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const data = tableDetails.indexList?.map(i => {
|
||||
return {
|
||||
...i,
|
||||
key: uuidv4(),
|
||||
}
|
||||
})
|
||||
setDataSource(data || [])
|
||||
}, [tableDetails])
|
||||
|
||||
const addData = () => {
|
||||
const newData = {
|
||||
key: uuidv4(),
|
||||
columnList: [],
|
||||
name: '',
|
||||
type: null,
|
||||
columns: null,
|
||||
}
|
||||
setDataSource([...dataSource, newData])
|
||||
edit(newData)
|
||||
}
|
||||
|
||||
const deleteData = () => {
|
||||
setDataSource(dataSource.filter(i => i.key !== editingKey))
|
||||
}
|
||||
|
||||
const moveData = (action: 'up' | 'down') => {
|
||||
const index = dataSource.findIndex(i => i.key === editingKey)
|
||||
if (index === -1) {
|
||||
return
|
||||
}
|
||||
if (action === 'up') {
|
||||
if (index === 0) {
|
||||
return
|
||||
}
|
||||
const newData = [...dataSource]
|
||||
newData[index] = dataSource[index - 1]
|
||||
newData[index - 1] = dataSource[index]
|
||||
setDataSource(newData)
|
||||
} else {
|
||||
if (index === dataSource.length - 1) {
|
||||
return
|
||||
}
|
||||
const newData = [...dataSource]
|
||||
newData[index] = dataSource[index + 1]
|
||||
newData[index + 1] = dataSource[index]
|
||||
setDataSource(newData)
|
||||
}
|
||||
}
|
||||
|
||||
const handelFieldsChange = (field: any) => {
|
||||
let { name: nameList, value } = field[0];
|
||||
const name = nameList[0];
|
||||
if (name === 'nullable') {
|
||||
value = value ? 1 : 0
|
||||
}
|
||||
const newData = dataSource.map((item) => {
|
||||
if (item.key === editingKey) {
|
||||
return {
|
||||
...item,
|
||||
[name]: value,
|
||||
};
|
||||
}
|
||||
return item;
|
||||
});
|
||||
setDataSource(newData);
|
||||
}
|
||||
|
||||
const onDragEnd = ({ active, over }: DragEndEvent) => {
|
||||
if (active.id !== over?.id) {
|
||||
setDataSource((previous) => {
|
||||
const activeIndex = previous.findIndex((i) => i.key === active.id);
|
||||
const overIndex = previous.findIndex((i) => i.key === over?.id);
|
||||
return arrayMove(previous, activeIndex, overIndex);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const Row = ({ children, ...props }: RowProps) => {
|
||||
const {
|
||||
attributes,
|
||||
listeners,
|
||||
setNodeRef,
|
||||
setActivatorNodeRef,
|
||||
transform,
|
||||
transition,
|
||||
isDragging,
|
||||
} = useSortable({
|
||||
id: props['data-row-key'],
|
||||
});
|
||||
|
||||
const style: React.CSSProperties = {
|
||||
...props.style,
|
||||
transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),
|
||||
transition,
|
||||
...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
|
||||
};
|
||||
|
||||
return (
|
||||
<tr {...props} ref={setNodeRef} style={style} {...attributes}>
|
||||
{React.Children.map(children, (child) => {
|
||||
if ((child as React.ReactElement).key === 'sort') {
|
||||
return React.cloneElement(child as React.ReactElement, {
|
||||
children: (
|
||||
<MenuOutlined
|
||||
ref={setActivatorNodeRef}
|
||||
style={{ touchAction: 'none', cursor: 'move' }}
|
||||
{...listeners}
|
||||
/>
|
||||
),
|
||||
});
|
||||
}
|
||||
return child;
|
||||
})}
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
function getIndexListInfo(): IIndexListInfo {
|
||||
return dataSource
|
||||
}
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
getIndexListInfo,
|
||||
}));
|
||||
|
||||
const columns = [
|
||||
{
|
||||
key: 'sort',
|
||||
width: '60px',
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.index'),
|
||||
width: '70px',
|
||||
render: (text: string, record: IIndexItem) => {
|
||||
return dataSource.findIndex(i => i.key === record.key) + 1
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.indexName'),
|
||||
dataIndex: 'name',
|
||||
width: '180px',
|
||||
render: (text: string, record: IIndexItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item
|
||||
name="name"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
) : <div
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.indexType'),
|
||||
dataIndex: 'type',
|
||||
width: '180px',
|
||||
render: (text: string, record: IIndexItem) => {
|
||||
const editable = isEditing(record);
|
||||
return editable ? (
|
||||
<Form.Item
|
||||
name="type"
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Select style={{ width: '100%' }}>
|
||||
{indexesTypeList.map(i => <Select.Option key={i} value={i}>{i}</Select.Option>)}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
) : <div
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
}
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.label.includeColumn'),
|
||||
dataIndex: 'columnList',
|
||||
render: (columnList: IIndexIncludeColumnItem[], record: IIndexItem) => {
|
||||
const editable = isEditing(record);
|
||||
const text = columnList?.map(t => {
|
||||
return `${t.columnName}`
|
||||
}).join(',')
|
||||
return editable ? (
|
||||
<div className={styles.columnListCell}>
|
||||
<span onClick={() => { setIncludeColModalOpen(true) }}>{i18n('common.button.edit')}</span>
|
||||
{text}
|
||||
</div >
|
||||
) : (
|
||||
<div
|
||||
className={styles.editableCell}
|
||||
onClick={() => edit(record)}
|
||||
>
|
||||
{text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
const getIncludeColInfo = () => {
|
||||
setDataSource(
|
||||
dataSource.map(i => {
|
||||
if (i.key === editingKey) {
|
||||
i.columnList = includeColRef.current?.getIncludeColInfo()!
|
||||
}
|
||||
return i
|
||||
})
|
||||
)
|
||||
setIncludeColModalOpen(false)
|
||||
}
|
||||
|
||||
const indexIncludedColumnList: IIndexIncludeColumnItem[] = useMemo(() => {
|
||||
let data: IIndexIncludeColumnItem[] | null = [];
|
||||
dataSource.forEach(i => {
|
||||
if (i.key === editingKey) {
|
||||
data = i.columnList
|
||||
}
|
||||
})
|
||||
return data
|
||||
}, [editingKey])
|
||||
|
||||
return <div className={classnames(styles.box)}>
|
||||
<div className={styles.indexListHeader}>
|
||||
<Button onClick={addData}>{i18n('editTable.button.add')}</Button>
|
||||
<Button onClick={deleteData}>{i18n('editTable.button.delete')}</Button>
|
||||
{/* <Button onClick={moveData.bind(null, 'up')}>上移</Button>
|
||||
<Button onClick={moveData.bind(null, 'down')}>下移</Button> */}
|
||||
</div>
|
||||
<Form form={form} onFieldsChange={handelFieldsChange}>
|
||||
<DndContext modifiers={[restrictToVerticalAxis]} onDragEnd={onDragEnd}>
|
||||
<SortableContext
|
||||
items={dataSource.map((i) => i.key)}
|
||||
strategy={verticalListSortingStrategy}
|
||||
>
|
||||
<Table
|
||||
components={{
|
||||
body: {
|
||||
row: Row,
|
||||
},
|
||||
}}
|
||||
pagination={false}
|
||||
rowKey="key"
|
||||
columns={columns}
|
||||
dataSource={dataSource}
|
||||
/>
|
||||
</SortableContext>
|
||||
</DndContext>
|
||||
</Form>
|
||||
<Modal
|
||||
open={includeColModalOpen}
|
||||
width={800}
|
||||
title={i18n('editTable.label.includeColumn')}
|
||||
onOk={getIncludeColInfo}
|
||||
onCancel={() => { setIncludeColModalOpen(false) }}
|
||||
maskClosable={false}
|
||||
destroyOnClose={true}
|
||||
>
|
||||
<IncludeCol includedColumnList={indexIncludedColumnList} ref={includeColRef} />
|
||||
</Modal>
|
||||
</div >
|
||||
})
|
||||
|
||||
export default IndexList
|
||||
|
66
chat2db-client/src/blocks/DatabaseTableEditor/index.less
Normal file
66
chat2db-client/src/blocks/DatabaseTableEditor/index.less
Normal file
@ -0,0 +1,66 @@
|
||||
@import '../../styles/var.less';
|
||||
|
||||
.box {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 10px 10px 0px 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.tabList {
|
||||
display: flex;
|
||||
border-bottom: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tabItem {
|
||||
position: relative;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
padding: 0px 18px;
|
||||
border: 1px solid var(--color-border);
|
||||
margin-right: 1px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.currentTab {
|
||||
font-weight: 500;
|
||||
color: var(--color-primary);
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -1px;
|
||||
right: 0px;
|
||||
height: 2px;
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab {
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
}
|
147
chat2db-client/src/blocks/DatabaseTableEditor/index.tsx
Normal file
147
chat2db-client/src/blocks/DatabaseTableEditor/index.tsx
Normal file
@ -0,0 +1,147 @@
|
||||
import React, { memo, useRef, useState, createContext, useEffect, forwardRef, useMemo } from 'react';
|
||||
import { Button, Form } from 'antd';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import IndexList, { IIndexListRef } from './IndexList';
|
||||
import ColumnList, { IColumnListRef } from './ColumnList';
|
||||
import BaseInfo, { IBaseInfoRef } from './BaseInfo';
|
||||
import sqlService, { IModifyTableSqlParams } from '@/service/sql';
|
||||
import { IEditTableInfo } from '@/typings';
|
||||
import i18n from '@/i18n';
|
||||
|
||||
interface IProps {
|
||||
dataSourceId: number,
|
||||
databaseName: string,
|
||||
schemaName: string | undefined,
|
||||
tableName?: string
|
||||
}
|
||||
|
||||
interface ITabItem {
|
||||
title: string;
|
||||
key: string;
|
||||
component: any; // TODO: 组件的Ts是什么
|
||||
}
|
||||
|
||||
interface IContext extends IProps {
|
||||
tableDetails: IEditTableInfo;
|
||||
baseInfoRef: React.RefObject<IBaseInfoRef>;
|
||||
columnListRef: React.RefObject<IColumnListRef>;
|
||||
indexListRef: React.RefObject<IIndexListRef>;
|
||||
}
|
||||
|
||||
export const Context = createContext<IContext>({} as any);
|
||||
|
||||
export default memo<IProps>(function DatabaseTableEditor(props) {
|
||||
const { databaseName, dataSourceId, tableName, schemaName } = props;
|
||||
const [tableDetails, setTableDetails] = useState<IEditTableInfo>({} as any);
|
||||
const [oldTableDetails, setOldTableDetails] = useState<IEditTableInfo>({} as any);
|
||||
const baseInfoRef = useRef<IBaseInfoRef>(null);
|
||||
const columnListRef = useRef<IColumnListRef>(null);
|
||||
const indexListRef = useRef<IIndexListRef>(null);
|
||||
const tabList = useMemo(() => {
|
||||
return [
|
||||
{
|
||||
title: i18n('editTable.tab.basicInfo'),
|
||||
key: 'basic',
|
||||
component: <BaseInfo ref={baseInfoRef} />
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.tab.columnInfo'),
|
||||
key: 'column',
|
||||
component: <ColumnList ref={columnListRef} />
|
||||
},
|
||||
{
|
||||
title: i18n('editTable.tab.indexInfo'),
|
||||
key: 'index',
|
||||
component: <IndexList ref={indexListRef} />
|
||||
},
|
||||
]
|
||||
}, [])
|
||||
const [currentTab, setCurrentTab] = useState<ITabItem>(tabList[0]);
|
||||
|
||||
function changeTab(item: ITabItem) {
|
||||
setCurrentTab(item)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (tableName) {
|
||||
let params = {
|
||||
databaseName,
|
||||
dataSourceId,
|
||||
tableName,
|
||||
schemaName,
|
||||
refresh: true
|
||||
}
|
||||
sqlService.getTableDetails(params).then(res => {
|
||||
setTableDetails(res || {})
|
||||
setOldTableDetails(res)
|
||||
})
|
||||
}
|
||||
}, [])
|
||||
|
||||
function submit() {
|
||||
if (baseInfoRef.current && columnListRef.current && indexListRef.current) {
|
||||
const newTable = {
|
||||
...baseInfoRef.current.getBaseInfo(),
|
||||
columnList: columnListRef.current.getColumnListInfo()!,
|
||||
indexList: indexListRef.current.getIndexListInfo()!
|
||||
}
|
||||
|
||||
let params: IModifyTableSqlParams = {
|
||||
databaseName,
|
||||
dataSourceId,
|
||||
schemaName,
|
||||
refresh: true,
|
||||
newTable: JSON.stringify(newTable),
|
||||
}
|
||||
|
||||
if (tableName) {
|
||||
params.tableName = tableName;
|
||||
params.oldTable = JSON.stringify(oldTableDetails);
|
||||
}
|
||||
console.log(newTable);
|
||||
sqlService.getModifyTableSql(params).then(res => {
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return <Context.Provider value={{
|
||||
...props,
|
||||
tableDetails,
|
||||
baseInfoRef,
|
||||
columnListRef,
|
||||
indexListRef
|
||||
}}>
|
||||
<div className={classnames(styles.box)}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.tabList}>
|
||||
{
|
||||
tabList.map((item, index) => {
|
||||
return <div
|
||||
key={item.key}
|
||||
onClick={changeTab.bind(null, item)}
|
||||
className={classnames(styles.tabItem, currentTab.key == item.key ? styles.currentTab : '')}
|
||||
>
|
||||
{item.title}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
<div className={styles.saveButton}>
|
||||
<Button type="primary" onClick={submit}>{i18n('common.button.save')}</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.main}>
|
||||
{
|
||||
tabList.map(t => {
|
||||
return <div key={t.key} className={classnames(styles.tab, { [styles.hidden]: currentTab.key !== t.key })}>
|
||||
{t.component}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</Context.Provider>
|
||||
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
@import '../../../../../styles/var.less';
|
||||
@import '../../styles/var.less';
|
||||
|
||||
.box {
|
||||
height: 100%;
|
@ -7,7 +7,7 @@ import Console, { IAppendValue } from '@/components/Console';
|
||||
import SearchResult from '@/components/SearchResult';
|
||||
import { DatabaseTypeCode, ConsoleStatus, TreeNodeType } from '@/constants';
|
||||
import { IManageResultData, IResultConfig } from '@/typings';
|
||||
import { IWorkspaceModelState } from '@/models/workspace';
|
||||
import { IWorkspaceModelState, IWorkspaceModelType } from '@/models/workspace';
|
||||
import historyServer, { IGetSavedListParams, ISaveBasicInfo } from '@/service/history';
|
||||
import { IAIState } from '@/models/ai';
|
||||
import sqlServer, { IExecuteSqlParams, IExportParams } from '@/service/sql';
|
||||
@ -41,7 +41,7 @@ const defaultResultConfig: IResultConfig = {
|
||||
hasNextPage: true,
|
||||
};
|
||||
|
||||
const WorkspaceRightItem = memo<IProps>(function (props) {
|
||||
const SQLExecute = memo<IProps>(function (props) {
|
||||
const { data, workspaceModel, aiModel, isActive, dispatch } = props;
|
||||
const draggableRef = useRef<any>();
|
||||
const [appendValue, setAppendValue] = useState<IAppendValue>();
|
||||
@ -218,4 +218,9 @@ const WorkspaceRightItem = memo<IProps>(function (props) {
|
||||
);
|
||||
});
|
||||
|
||||
export default WorkspaceRightItem;
|
||||
const dvaModel = connect(({ workspace, ai }: { workspace: IWorkspaceModelType; ai: IAIState }) => ({
|
||||
workspaceModel: workspace,
|
||||
aiModel: ai,
|
||||
}));
|
||||
|
||||
export default dvaModel(SQLExecute);
|
@ -44,7 +44,7 @@
|
||||
|
||||
.menus {
|
||||
width: 200px;
|
||||
background-color: var(--color-bg-elevated);
|
||||
// background-color: var(--color-bg-subtle);
|
||||
padding: 20px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
@ -47,13 +47,13 @@
|
||||
border-radius: 4px 12px;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
}
|
||||
}
|
||||
|
||||
.remainBlock {
|
||||
border-radius: 16px;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
padding: 4px 12px;
|
||||
|
||||
&:hover {
|
||||
|
@ -48,7 +48,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
padding: 0 16px;
|
||||
height: 30px;
|
||||
}
|
||||
@ -77,7 +77,7 @@
|
||||
justify-content: start;
|
||||
align-items: center;
|
||||
border-top: 1px solid var(--color-border-secondary);
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
|
||||
& > span {
|
||||
margin-right: 16px;
|
||||
@ -114,7 +114,7 @@
|
||||
display: none;
|
||||
align-items: center;
|
||||
// position: absolute;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
// top: 0;
|
||||
// right: 0;
|
||||
// bottom: 0;
|
||||
|
@ -36,7 +36,7 @@ interface IViewTableCellData {
|
||||
const SupportBaseTable: any = styled(BaseTable)`
|
||||
&.supportBaseTable {
|
||||
--bgcolor: var(--color-bg-base);
|
||||
--header-bgcolor: var(--color-bg-elevated);
|
||||
--header-bgcolor: var(--color-bg-subtle);
|
||||
--hover-bgcolor: var(--color-hover-bg);
|
||||
--header-hover-bgcolor: var(--color-hover-bg);
|
||||
--highlight-bgcolor: var(--color-hover-bg);
|
||||
|
@ -138,7 +138,7 @@ export default memo<IProps>(function SearchResult(props) {
|
||||
type="line"
|
||||
onEdit={onEdit}
|
||||
onChange={onChange}
|
||||
tabs={tabs}
|
||||
items={tabs}
|
||||
className={styles.tabs}
|
||||
activeTab={currentTab}
|
||||
/>
|
||||
|
@ -1,7 +1,7 @@
|
||||
@import '../../styles/var.less';
|
||||
|
||||
.tab-focus() {
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
// 添加内阴影
|
||||
border-bottom: 1px solid var(--color-primary);
|
||||
.icon {
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
.tab-focus-line() {
|
||||
color: var(--color-primary);
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
.icon {
|
||||
display: flex;
|
||||
}
|
||||
@ -155,7 +155,7 @@
|
||||
outline: none;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export interface IOnchangeProps {
|
||||
|
||||
interface IProps {
|
||||
className?: string;
|
||||
tabs: IOption[] | undefined;
|
||||
items: IOption[] | undefined;
|
||||
activeTab?: number | string;
|
||||
onChange: (key: IOption['value']) => void;
|
||||
onEdit?: (action: 'add' | 'remove', key?: IOption['value']) => void;
|
||||
@ -28,8 +28,8 @@ interface IProps {
|
||||
}
|
||||
|
||||
export default memo<IProps>(function Tab(props) {
|
||||
const { className, tabs, onChange, onEdit, activeTab, hideAdd, type, editableName, editableNameOnBlur } = props;
|
||||
const [internalTabs, setInternalTabs] = useState<IOption[]>(lodash.cloneDeep(tabs || []));
|
||||
const { className, items, onChange, onEdit, activeTab, hideAdd, type, editableName, editableNameOnBlur } = props;
|
||||
const [internalTabs, setInternalTabs] = useState<IOption[]>(lodash.cloneDeep(items || []));
|
||||
const [internalActiveTab, setInternalActiveTab] = useState<number | string | undefined>(internalTabs[0]?.value);
|
||||
const [editingTab, setEditingTab] = useState<IOption['value'] | undefined>();
|
||||
|
||||
@ -38,8 +38,8 @@ export default memo<IProps>(function Tab(props) {
|
||||
}, [activeTab])
|
||||
|
||||
useEffect(() => {
|
||||
setInternalTabs(lodash.cloneDeep(tabs || []));
|
||||
}, [tabs])
|
||||
setInternalTabs(lodash.cloneDeep(items || []));
|
||||
}, [items])
|
||||
|
||||
function deleteTab(data: IOption) {
|
||||
const newTabs = internalTabs?.filter(t => t.value !== data.value);
|
||||
|
194
chat2db-client/src/components/TabsNew/index.less
Normal file
194
chat2db-client/src/components/TabsNew/index.less
Normal file
@ -0,0 +1,194 @@
|
||||
@import '../../styles/var.less';
|
||||
|
||||
.tab-focus() {
|
||||
background-color: var(--color-bg-subtle);
|
||||
// 添加内阴影
|
||||
border-bottom: 1px solid var(--color-primary);
|
||||
.icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.tab-focus-line() {
|
||||
color: var(--color-primary);
|
||||
background-color: var(--color-bg-subtle);
|
||||
.icon {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
.tabBox {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tabsNav {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
height: 32px;
|
||||
background-color: var(--color-bg-base);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
&::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.tabsContent {
|
||||
flex: 1;
|
||||
.tabsContentItem {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
display: none;
|
||||
}
|
||||
.tabsContentItemActive {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.activeContent {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.tabList {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.tabItem {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0px 10px;
|
||||
line-height: 32px;
|
||||
height: 32px;
|
||||
width: 120px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-right: 1px solid var(--color-border);
|
||||
.textBox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.text {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
.f-single-line();
|
||||
}
|
||||
.icon {
|
||||
display: none;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
height: 16px;
|
||||
padding: 0px 2px;
|
||||
margin-left: 2px;
|
||||
border-radius: 2px;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-secondary);
|
||||
i {
|
||||
font-size: 12px;
|
||||
}
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
background-color: var(--color-hover-bg);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.tab-focus();
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.tabItemLine {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0px 10px;
|
||||
line-height: 20px;
|
||||
height: 28px;
|
||||
width: 100px;
|
||||
cursor: pointer;
|
||||
border-right: 1px solid var(--color-border);
|
||||
.textBox {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.text {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
.f-single-line();
|
||||
}
|
||||
.text {
|
||||
flex: 1;
|
||||
width: 0;
|
||||
.f-single-line();
|
||||
}
|
||||
.icon {
|
||||
display: none;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
height: 16px;
|
||||
padding: 0px 2px;
|
||||
margin-left: 2px;
|
||||
border-radius: 2px;
|
||||
color: var(--color-text-secondary);
|
||||
cursor: pointer;
|
||||
i {
|
||||
font-size: 12px;
|
||||
}
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
background-color: var(--color-hover-bg);
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
.tab-focus-line();
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.activeTab {
|
||||
.tab-focus();
|
||||
}
|
||||
|
||||
.activeTabLine {
|
||||
.tab-focus-line();
|
||||
}
|
||||
|
||||
.rightBox {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.addIcon {
|
||||
width: 30px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.input {
|
||||
border: 0;
|
||||
width: 86px;
|
||||
flex: 1;
|
||||
height: 20px;
|
||||
outline: none;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
background-color: var(--color-bg-subtle);
|
||||
input:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
.prefixIcon {
|
||||
flex-shrink: 0;
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.activeContent {
|
||||
}
|
154
chat2db-client/src/components/TabsNew/index.tsx
Normal file
154
chat2db-client/src/components/TabsNew/index.tsx
Normal file
@ -0,0 +1,154 @@
|
||||
import React, { memo, useEffect, useState, Fragment } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import Iconfont from '@/components/Iconfont';
|
||||
import styles from './index.less';
|
||||
|
||||
export interface ITabItem {
|
||||
prefixIcon?: string;
|
||||
label: React.ReactNode;
|
||||
key: number | string;
|
||||
children?: React.ReactNode;
|
||||
}
|
||||
|
||||
export interface IOnchangeProps {
|
||||
type: 'add' | 'delete' | 'switch';
|
||||
data?: ITabItem;
|
||||
}
|
||||
|
||||
interface IProps {
|
||||
className?: string;
|
||||
items: ITabItem[] | undefined;
|
||||
activeKey?: number | string;
|
||||
onChange?: (key: string | number | undefined) => void;
|
||||
onEdit?: (action: 'add' | 'remove', data?: ITabItem) => void;
|
||||
hideAdd?: boolean;
|
||||
type?: 'line';
|
||||
editableName?: boolean;
|
||||
editableNameOnBlur?: (option: ITabItem) => void;
|
||||
}
|
||||
|
||||
export default memo<IProps>(function Tabs(props) {
|
||||
const { className, items, onChange, onEdit, activeKey, hideAdd, type, editableName, editableNameOnBlur } = props;
|
||||
const [internalTabs, setInternalTabs] = useState<ITabItem[]>([]);
|
||||
const [internalActiveTab, setInternalActiveTab] = useState<number | string | undefined>();
|
||||
const [editingTab, setEditingTab] = useState<ITabItem['key'] | undefined>();
|
||||
|
||||
useEffect(() => {
|
||||
if (activeKey !== null && activeKey !== undefined) {
|
||||
setInternalActiveTab(activeKey);
|
||||
}
|
||||
}, [activeKey])
|
||||
|
||||
useEffect(() => {
|
||||
setInternalTabs(items || []);
|
||||
if (items?.length && internalActiveTab === undefined) {
|
||||
setInternalActiveTab(items[0].key);
|
||||
}
|
||||
}, [items])
|
||||
|
||||
useEffect(() => {
|
||||
onChange?.(internalActiveTab);
|
||||
}, [internalActiveTab])
|
||||
|
||||
function deleteTab(data: ITabItem) {
|
||||
const newInternalTabs = internalTabs?.filter(t => t.key !== data.key);
|
||||
let activeKey = internalActiveTab;
|
||||
// 删掉的是当前激活的tab,那么就切换到前一个,如果前一个没有就切换到后一个
|
||||
if (data.key === internalActiveTab) {
|
||||
const index = internalTabs.findIndex(t => t.key === data.key);
|
||||
if (index === 0) {
|
||||
activeKey = internalTabs[1]?.key
|
||||
} else {
|
||||
activeKey = internalTabs[index - 1]?.key
|
||||
}
|
||||
}
|
||||
changeTab(activeKey);
|
||||
setInternalTabs(newInternalTabs);
|
||||
onEdit?.('remove', data)
|
||||
}
|
||||
|
||||
function changeTab(key: string | number | undefined) {
|
||||
setInternalActiveTab(key);
|
||||
}
|
||||
|
||||
function handelAdd() {
|
||||
onEdit?.('add')
|
||||
}
|
||||
|
||||
function onDoubleClick(t: ITabItem) {
|
||||
if (editableName) {
|
||||
setEditingTab(t.key)
|
||||
}
|
||||
}
|
||||
|
||||
function renderTabItem(t: ITabItem, index: number) {
|
||||
function inputOnChange(value: string) {
|
||||
internalTabs[index].label = value
|
||||
setInternalTabs([...internalTabs])
|
||||
}
|
||||
|
||||
function onBlur() {
|
||||
editableNameOnBlur?.(t);
|
||||
setEditingTab(undefined);
|
||||
}
|
||||
|
||||
return <div
|
||||
onDoubleClick={() => { onDoubleClick(t) }}
|
||||
key={t.key}
|
||||
className={
|
||||
classnames(
|
||||
{ [styles.tabItem]: type !== 'line' },
|
||||
{ [styles.tabItemLine]: type === 'line' },
|
||||
{ [styles.activeTabLine]: t.key === internalActiveTab && type === 'line' },
|
||||
{ [styles.activeTab]: t.key === internalActiveTab && type !== 'line' },
|
||||
)
|
||||
}
|
||||
>
|
||||
{
|
||||
t.key === editingTab ?
|
||||
<input value={t.label as string} onChange={(e) => { inputOnChange(e.target.value) }} className={styles.input} autoFocus onBlur={onBlur} type="text" />
|
||||
:
|
||||
<div className={styles.textBox} key={t.key} onClick={changeTab.bind(null, t.key)}>
|
||||
{t.prefixIcon && <Iconfont className={styles.prefixIcon} code={t.prefixIcon} />}
|
||||
<div className={styles.text}>
|
||||
{t.label}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
<div className={styles.icon} onClick={deleteTab.bind(null, t)}>
|
||||
<Iconfont code='' />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
return <div className={classnames(styles.tabBox, className)}>
|
||||
<div className={styles.tabsNav}>
|
||||
{
|
||||
!!internalTabs?.length &&
|
||||
<div className={styles.tabList}>
|
||||
{
|
||||
internalTabs.map((t, index) => {
|
||||
return renderTabItem(t, index)
|
||||
})
|
||||
}
|
||||
</div>
|
||||
}
|
||||
{
|
||||
!hideAdd && <div className={styles.rightBox}>
|
||||
<div className={styles.addIcon} onClick={handelAdd}>
|
||||
<Iconfont code=''></Iconfont>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div className={styles.tabsContent}>
|
||||
{
|
||||
internalTabs?.map(t => {
|
||||
return <div key={t.key} className={classnames(styles.tabsContentItem, { [styles.tabsContentItemActive]: t.key === internalActiveTab })}>
|
||||
{t.children}
|
||||
</div>
|
||||
})
|
||||
}
|
||||
</div>
|
||||
</div >
|
||||
})
|
@ -33,37 +33,6 @@ export enum OSType {
|
||||
RESTS = 'rests',
|
||||
}
|
||||
|
||||
export enum OperationType {
|
||||
CONSOLE = 'console',
|
||||
FUNCTION = 'function',
|
||||
PROCEDURE = 'procedure',
|
||||
VIEW = 'view',
|
||||
TRIGGER = 'trigger',
|
||||
}
|
||||
|
||||
export const operationTypeConfig: {
|
||||
[key in OperationType]: {
|
||||
icon: string
|
||||
};
|
||||
} = {
|
||||
[OperationType.CONSOLE]: {
|
||||
icon: '\uec83'
|
||||
},
|
||||
[OperationType.VIEW]: {
|
||||
icon: '\ue70c'
|
||||
},
|
||||
[OperationType.FUNCTION]: {
|
||||
icon: '\ue76a'
|
||||
},
|
||||
[OperationType.PROCEDURE]: {
|
||||
icon: '\ue73c'
|
||||
},
|
||||
|
||||
[OperationType.TRIGGER]: {
|
||||
icon: '\ue64a'
|
||||
}
|
||||
}
|
||||
|
||||
export enum ConnectionKind {
|
||||
Private = 'PRIVATE',
|
||||
Shared = 'SHARED'
|
||||
|
11
chat2db-client/src/constants/editTable.ts
Normal file
11
chat2db-client/src/constants/editTable.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// 索引类型
|
||||
export enum IndexesType {
|
||||
// 普通索引
|
||||
Normal = 'normal',
|
||||
// 唯一索引
|
||||
Unique = 'unique',
|
||||
// 全文索引
|
||||
Fulltext = 'fulltext',
|
||||
// 空间索引
|
||||
Spatial = 'spatial',
|
||||
}
|
@ -6,4 +6,6 @@ export * from './monacoEditor';
|
||||
export * from './table';
|
||||
export * from './theme';
|
||||
export * from './tree';
|
||||
export * from './workspace';
|
||||
export * from './editTable';
|
||||
|
||||
|
46
chat2db-client/src/constants/workspace.ts
Normal file
46
chat2db-client/src/constants/workspace.ts
Normal file
@ -0,0 +1,46 @@
|
||||
export enum CreateTabIntroType {
|
||||
EditorTable = 'EditorTable',
|
||||
OpenTable = 'openTable',
|
||||
}
|
||||
|
||||
|
||||
// 工作台Tab的类型
|
||||
export enum WorkspaceTabType {
|
||||
CONSOLE = 'console',
|
||||
FUNCTION = 'function',
|
||||
PROCEDURE = 'procedure',
|
||||
VIEW = 'view',
|
||||
TRIGGER = 'trigger',
|
||||
EditTable = 'editTable',
|
||||
OpenTable = 'openTable',
|
||||
}
|
||||
|
||||
// 工作台Tab的类型对应的一些配置
|
||||
export const workspaceTabConfig: {
|
||||
[key in WorkspaceTabType]: {
|
||||
icon: string
|
||||
};
|
||||
} = {
|
||||
[WorkspaceTabType.CONSOLE]: {
|
||||
icon: '\uec83'
|
||||
},
|
||||
[WorkspaceTabType.VIEW]: {
|
||||
icon: '\ue70c'
|
||||
},
|
||||
[WorkspaceTabType.FUNCTION]: {
|
||||
icon: '\ue76a'
|
||||
},
|
||||
[WorkspaceTabType.PROCEDURE]: {
|
||||
icon: '\ue73c'
|
||||
},
|
||||
[WorkspaceTabType.TRIGGER]: {
|
||||
icon: '\ue64a'
|
||||
},
|
||||
[WorkspaceTabType.EditTable]: {
|
||||
icon: '\ue6b6'
|
||||
},
|
||||
[WorkspaceTabType.OpenTable]: {
|
||||
icon: '\ue618'
|
||||
}
|
||||
}
|
||||
|
21
chat2db-client/src/i18n/en-us/editTable.ts
Normal file
21
chat2db-client/src/i18n/en-us/editTable.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
'editTable.tab.basicInfo': 'Basic info',
|
||||
'editTable.tab.columnInfo': 'Column info',
|
||||
'editTable.tab.indexInfo': 'Index info',
|
||||
'editTable.label.tableName': 'Table name',
|
||||
'editTable.label.comment': 'Comment',
|
||||
'editTable.button.add': 'Add',
|
||||
'editTable.button.delete': 'Delete',
|
||||
'editTable.button.up': 'Up',
|
||||
'editTable.button.down': 'Down',
|
||||
'editTable.label.indexName': 'Index name',
|
||||
'editTable.label.indexType': 'Index type',
|
||||
'editTable.label.includeColumn': 'Include column',
|
||||
'editTable.button.createTable': 'Create table',
|
||||
'editTable.label.index': 'Index',
|
||||
'editTable.label.columnName': 'Column name',
|
||||
'editTable.label.columnSize': 'Size',
|
||||
'editTable.label.columnType': 'Type',
|
||||
'editTable.label.nullable': 'Nullable',
|
||||
'editTable.label.prefixLength': 'Prefix length',
|
||||
};
|
@ -7,6 +7,7 @@ import dashboard from './dashboard';
|
||||
import chat from './chat';
|
||||
import team from './team'
|
||||
import login from './login';
|
||||
import editTable from './editTable';
|
||||
|
||||
export default {
|
||||
lang: 'en',
|
||||
@ -18,5 +19,6 @@ export default {
|
||||
...dashboard,
|
||||
...chat,
|
||||
...team,
|
||||
...login
|
||||
...login,
|
||||
...editTable
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ export default {
|
||||
'workspace.title.saved': 'Saved',
|
||||
'workspace.menu.exportDDL': 'Export DDL',
|
||||
'workspace.menu.deleteTable': 'Delete Table',
|
||||
'workspace.menu.editTable': 'Edit Table',
|
||||
'workspace.menu.pin': 'Pin',
|
||||
'workspace.menu.unPin': 'Unpin',
|
||||
'workspace.menu.deleteTablePlaceHolder': 'Please enter the name of the table you want to delete',
|
||||
|
21
chat2db-client/src/i18n/zh-cn/editTable.ts
Normal file
21
chat2db-client/src/i18n/zh-cn/editTable.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export default {
|
||||
'editTable.tab.basicInfo': '基本信息',
|
||||
'editTable.tab.columnInfo': '列信息',
|
||||
'editTable.tab.indexInfo': '索引信息',
|
||||
'editTable.label.tableName': '表名',
|
||||
'editTable.label.comment': '注释',
|
||||
'editTable.button.add': '新增',
|
||||
'editTable.button.delete': '删除',
|
||||
'editTable.button.up': '上移',
|
||||
'editTable.button.down': '下移',
|
||||
'editTable.label.indexName': '索引名称',
|
||||
'editTable.label.indexType': '索引类型',
|
||||
'editTable.label.includeColumn': '包含列',
|
||||
'editTable.button.createTable': '新建表',
|
||||
'editTable.label.index': '序号',
|
||||
'editTable.label.columnName': '列名',
|
||||
'editTable.label.columnSize': '长度',
|
||||
'editTable.label.columnType': '类型',
|
||||
'editTable.label.nullable': '可空',
|
||||
'editTable.label.prefixLength': '前缀长度',
|
||||
};
|
@ -8,6 +8,7 @@ import dashboard from './dashboard';
|
||||
import chat from './chat';
|
||||
import team from './team'
|
||||
import login from './login';
|
||||
import editTable from './editTable';
|
||||
|
||||
export default {
|
||||
lang: LangType.ZH_CN,
|
||||
@ -20,5 +21,6 @@ export default {
|
||||
...dashboard,
|
||||
...chat,
|
||||
...team,
|
||||
...login
|
||||
...login,
|
||||
...editTable
|
||||
};
|
||||
|
@ -4,6 +4,7 @@ export default {
|
||||
'workspace.title.saved': '保存记录',
|
||||
'workspace.menu.exportDDL': '导出DDL',
|
||||
'workspace.menu.deleteTable': '删除表',
|
||||
'workspace.menu.editTable': '编辑表',
|
||||
'workspace.menu.pin': '置顶',
|
||||
'workspace.menu.unPin': '取消置顶',
|
||||
'workspace.menu.deleteTablePlaceHolder': '请输入你要删除的表名',
|
||||
@ -18,5 +19,4 @@ export default {
|
||||
'workspace.tree.trigger': '触发器',
|
||||
'workspace.tree.function': '函数',
|
||||
'workspace.tree.procedure': '存储过程',
|
||||
|
||||
};
|
||||
|
@ -3,7 +3,7 @@ import sqlService, { MetaSchemaVO } from '@/service/sql';
|
||||
import historyService from '@/service/history';
|
||||
import { DatabaseTypeCode, ConsoleStatus, TreeNodeType } from '@/constants';
|
||||
import { Effect, Reducer } from 'umi';
|
||||
import { ITreeNode, IConsole, IPageResponse } from '@/typings';
|
||||
import { ITreeNode, IConsole, IPageResponse, ICreateTabIntro, IWorkspaceTab } from '@/typings';
|
||||
import { treeConfig } from '@/pages/main/workspace/components/Tree/treeConfig';
|
||||
|
||||
export type ICurWorkspaceParams = {
|
||||
@ -25,7 +25,10 @@ export interface IWorkspaceModelState {
|
||||
curConsoleId: number | null;
|
||||
openConsoleList: IConsole[];
|
||||
curTableList: ITreeNode[];
|
||||
curViewList: ITreeNode[];
|
||||
// 触发tab编辑表或打开表
|
||||
createTabIntro: ICreateTabIntro | undefined;
|
||||
// 触发新增console
|
||||
createConsoleIntro: IWorkspaceTab | undefined;
|
||||
}
|
||||
|
||||
export interface IWorkspaceModelType {
|
||||
@ -40,7 +43,8 @@ export interface IWorkspaceModelType {
|
||||
setOpenConsoleList: Reducer<IWorkspaceModelState>;
|
||||
setCurConsoleId: Reducer<IWorkspaceModelState>;
|
||||
setCurTableList: Reducer<IWorkspaceModelState>;
|
||||
setCurViewList: Reducer<IWorkspaceModelState>;
|
||||
setCreateTabIntro: Reducer<IWorkspaceModelState>;
|
||||
setCreateConsoleIntro: Reducer<IWorkspaceModelState>;
|
||||
};
|
||||
effects: {
|
||||
fetchDatabaseAndSchema: Effect;
|
||||
@ -61,8 +65,9 @@ const WorkspaceModel: IWorkspaceModelType = {
|
||||
consoleList: [],
|
||||
openConsoleList: [],
|
||||
curTableList: [],
|
||||
curViewList: [],
|
||||
curConsoleId: null
|
||||
curConsoleId: null,
|
||||
createTabIntro: undefined,
|
||||
createConsoleIntro: undefined,
|
||||
},
|
||||
|
||||
reducers: {
|
||||
@ -117,14 +122,21 @@ const WorkspaceModel: IWorkspaceModelType = {
|
||||
curTableList: payload,
|
||||
};
|
||||
},
|
||||
|
||||
// 视图列表
|
||||
setCurViewList(state, { payload }) {
|
||||
// 创建tab的引子
|
||||
setCreateTabIntro(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
curViewList: payload,
|
||||
createTabIntro: payload,
|
||||
};
|
||||
},
|
||||
// 创建console的引子
|
||||
setCreateConsoleIntro(state, { payload }) {
|
||||
return {
|
||||
...state,
|
||||
createConsoleIntro: payload,
|
||||
};
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
effects: {
|
||||
|
@ -13,7 +13,7 @@
|
||||
flex-direction: column;
|
||||
width: 220px;
|
||||
overflow: hidden;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
border-right: 1px solid var(--color-border-secondary);
|
||||
border-top: 0px;
|
||||
border-bottom: 0px;
|
||||
|
@ -16,14 +16,13 @@
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.title {}
|
||||
.title {
|
||||
}
|
||||
|
||||
.edit {
|
||||
cursor: pointer;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.left_overlay_add {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -33,7 +32,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.right_overlay_add {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@ -68,7 +66,6 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
|
||||
.add_chart_icon {
|
||||
opacity: 0;
|
||||
width: 16px;
|
||||
@ -97,7 +94,6 @@
|
||||
filter: var(--filter-color-gray-100);
|
||||
}
|
||||
|
||||
|
||||
.emptyChartBlock {
|
||||
width: 100%;
|
||||
padding: 20px 0;
|
||||
@ -122,7 +118,7 @@
|
||||
|
||||
.editBlock {
|
||||
border-radius: var(--border-radius-l-g);
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
max-height: 600px;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
padding: 10px 8px 0px;
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
|
@ -15,7 +15,7 @@
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 68px;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
border-right: 1px solid var(--color-border-secondary);
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
|
@ -1,14 +1,14 @@
|
||||
import React, { memo, useState, useEffect, useRef, useContext, useMemo } from 'react';
|
||||
import React, { memo, useState, useEffect, useRef } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import i18n from '@/i18n';
|
||||
import { connect } from 'umi';
|
||||
import { Cascader, Divider, Input, Dropdown, Button, Spin } from 'antd';
|
||||
import { Input, Dropdown } from 'antd';
|
||||
import Iconfont from '@/components/Iconfont';
|
||||
import LoadingContent from '@/components/Loading/LoadingContent';
|
||||
import { IConnectionModelType } from '@/models/connection';
|
||||
import { IWorkspaceModelType } from '@/models/workspace';
|
||||
import historyServer from '@/service/history';
|
||||
import { ConsoleStatus, ConsoleOpenedStatus } from '@/constants';
|
||||
import { ConsoleStatus, ConsoleOpenedStatus, WorkspaceTabType } from '@/constants';
|
||||
import { IConsole, ITreeNode } from '@/typings';
|
||||
import styles from './index.less';
|
||||
import { approximateList } from '@/utils';
|
||||
@ -17,16 +17,11 @@ interface IProps {
|
||||
className?: string;
|
||||
workspaceModel: IWorkspaceModelType['state'],
|
||||
dispatch: any;
|
||||
tableLoading: boolean;
|
||||
databaseLoading: boolean;
|
||||
}
|
||||
|
||||
const dvaModel = connect(
|
||||
({ connection, workspace, loading }: { connection: IConnectionModelType; workspace: IWorkspaceModelType, loading: any }) => ({
|
||||
connectionModel: connection,
|
||||
({ workspace }: { workspace: IWorkspaceModelType }) => ({
|
||||
workspaceModel: workspace,
|
||||
tableLoading: loading.effects['workspace/fetchGetCurTableList'],
|
||||
databaseLoading: loading.effects['workspace/fetchDatabaseAndSchema'],
|
||||
}),
|
||||
);
|
||||
|
||||
@ -59,7 +54,6 @@ const SaveList = dvaModel(function (props: any) {
|
||||
});
|
||||
}, [curWorkspaceParams]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (searching) {
|
||||
inputRef.current!.focus({
|
||||
@ -85,31 +79,19 @@ const SaveList = dvaModel(function (props: any) {
|
||||
}
|
||||
|
||||
function openConsole(data: IConsole) {
|
||||
|
||||
let p: any = {
|
||||
id: data.id,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN
|
||||
};
|
||||
historyServer.updateSavedConsole(p).then((res) => {
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/setCurConsoleId',
|
||||
payload: data.id,
|
||||
});
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
type: 'workspace/setCreateConsoleIntro',
|
||||
payload: {
|
||||
orderByDesc: false,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
...curWorkspaceParams
|
||||
id: data.id,
|
||||
type: WorkspaceTabType.CONSOLE,
|
||||
title: data.name,
|
||||
uniqueData: data,
|
||||
},
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: res.data,
|
||||
})
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -119,20 +101,20 @@ const SaveList = dvaModel(function (props: any) {
|
||||
id: data.id,
|
||||
};
|
||||
historyServer.deleteSavedConsole(p).then((res) => {
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: {
|
||||
orderByDesc: true,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
...curWorkspaceParams
|
||||
},
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: res.data,
|
||||
})
|
||||
}
|
||||
})
|
||||
// dispatch({
|
||||
// type: 'workspace/fetchGetSavedConsole',
|
||||
// payload: {
|
||||
// orderByDesc: true,
|
||||
// tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
// ...curWorkspaceParams
|
||||
// },
|
||||
// callback: (res: any) => {
|
||||
// dispatch({
|
||||
// type: 'workspace/setOpenConsoleList',
|
||||
// payload: res.data,
|
||||
// })
|
||||
// }
|
||||
// })
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: {
|
||||
|
@ -25,9 +25,15 @@
|
||||
.iconBox {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0px -5px;
|
||||
}
|
||||
.itemIcon {
|
||||
margin: 0px 5px;
|
||||
}
|
||||
.refreshIcon {
|
||||
margin-right: 10px;
|
||||
}
|
||||
.moreIcon {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.refreshIcon,
|
||||
.searchIcon {
|
||||
@ -49,16 +55,16 @@
|
||||
margin: 0px -10px;
|
||||
}
|
||||
|
||||
.refreshIconBox{
|
||||
.refreshIconBox {
|
||||
cursor: pointer;
|
||||
&:hover{
|
||||
color: var(--color-primary)
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.cascaderPopup{
|
||||
:global{
|
||||
.ant-cascader-menu{
|
||||
.cascaderPopup {
|
||||
:global {
|
||||
.ant-cascader-menu {
|
||||
height: auto;
|
||||
}
|
||||
}
|
||||
|
@ -10,10 +10,11 @@ import { IWorkspaceModelType } from '@/models/workspace';
|
||||
import Tree from '../Tree';
|
||||
import { treeConfig } from '../Tree/treeConfig';
|
||||
import { ITreeNode } from '@/typings';
|
||||
import { TreeNodeType } from '@/constants';
|
||||
import { TreeNodeType, CreateTabIntroType, WorkspaceTabType } from '@/constants';
|
||||
import styles from './index.less';
|
||||
import { approximateTreeNode } from '@/utils';
|
||||
import { useUpdateEffect } from '@/hooks/useUpdateEffect';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
|
||||
interface IOption {
|
||||
value: TreeNodeType;
|
||||
@ -32,16 +33,21 @@ const optionsList: IOption[] = [
|
||||
]
|
||||
|
||||
const dvaModel = connect(
|
||||
({ connection, workspace, loading }: { connection: IConnectionModelType; workspace: IWorkspaceModelType, loading: any }) => ({
|
||||
({ connection, workspace }: { connection: IConnectionModelType; workspace: IWorkspaceModelType }) => ({
|
||||
connectionModel: connection,
|
||||
workspaceModel: workspace,
|
||||
databaseLoading: loading.effects['workspace/fetchDatabaseAndSchema'],
|
||||
}),
|
||||
);
|
||||
|
||||
interface Option {
|
||||
value: string;
|
||||
label: string;
|
||||
children?: Option[];
|
||||
}
|
||||
|
||||
const TableList = dvaModel(function (props: any) {
|
||||
const { workspaceModel, dispatch } = props;
|
||||
const { curWorkspaceParams, curTableList, curViewList } = workspaceModel;
|
||||
const { curWorkspaceParams } = workspaceModel;
|
||||
const [searching, setSearching] = useState<boolean>(false);
|
||||
const inputRef = useRef<any>();
|
||||
const [searchedTableList, setSearchedTableList] = useState<ITreeNode[] | undefined>();
|
||||
@ -62,8 +68,6 @@ const TableList = dvaModel(function (props: any) {
|
||||
}
|
||||
}, [curWorkspaceParams]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (searching) {
|
||||
inputRef.current!.focus({
|
||||
@ -118,6 +122,27 @@ const TableList = dvaModel(function (props: any) {
|
||||
setCurType(selectedOptions[0]);
|
||||
}
|
||||
|
||||
const cascaderOnChange: any = (_: string[], selectedOptions: Option[]) => {
|
||||
dispatch({
|
||||
type: 'workspace/setCreateConsoleIntro',
|
||||
payload: {
|
||||
id: uuidV4(),
|
||||
type: WorkspaceTabType.EditTable,
|
||||
title: 'create-table',
|
||||
uniqueData: {
|
||||
|
||||
}
|
||||
},
|
||||
})
|
||||
};
|
||||
|
||||
const options = [
|
||||
{
|
||||
value: 'createTable',
|
||||
label: i18n('editTable.button.createTable'),
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<div className={styles.tableModule}>
|
||||
<div className={styles.leftModuleTitle}>
|
||||
@ -148,12 +173,20 @@ const TableList = dvaModel(function (props: any) {
|
||||
</div>
|
||||
</Cascader>
|
||||
<div className={styles.iconBox} >
|
||||
<div className={styles.refreshIcon} onClick={() => refreshTableList()}>
|
||||
<div className={classnames(styles.refreshIcon, styles.itemIcon)} onClick={() => refreshTableList()}>
|
||||
<Iconfont code="" />
|
||||
</div>
|
||||
<div className={styles.searchIcon} onClick={() => openSearch()}>
|
||||
<div className={classnames(styles.searchIcon, styles.itemIcon)} onClick={() => openSearch()}>
|
||||
<Iconfont code="" />
|
||||
</div>
|
||||
{
|
||||
curType.value === TreeNodeType.TABLES &&
|
||||
<Cascader options={options} onChange={cascaderOnChange}>
|
||||
<div className={classnames(styles.moreIcon, styles.itemIcon)}>
|
||||
<Iconfont code="" />
|
||||
</div>
|
||||
</Cascader>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -161,7 +194,7 @@ const TableList = dvaModel(function (props: any) {
|
||||
<LoadingContent className={styles.treeBox} isLoading={tableLoading}>
|
||||
<Tree className={styles.tree} initialData={searchedTableList || curList}></Tree>
|
||||
</LoadingContent>
|
||||
</div>
|
||||
</div >
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -4,21 +4,16 @@ import classnames from 'classnames';
|
||||
import styles from './index.less';
|
||||
import Iconfont from '@/components/Iconfont';
|
||||
import { MenuProps, message, Modal, Input, Dropdown, notification } from 'antd';
|
||||
import { TreeNodeType, DatabaseTypeCode } from '@/constants';
|
||||
import { TreeNodeType, CreateTabIntroType, WorkspaceTabType } from '@/constants';
|
||||
import { ITreeConfigItem, ITreeConfig, treeConfig } from '@/pages/main/workspace/components/Tree/treeConfig';
|
||||
import { ITreeNode } from '@/typings';
|
||||
import connectionServer from '@/service/connection';
|
||||
import historyService from '@/service/history';
|
||||
import mysqlServer from '@/service/sql';
|
||||
import { OperationColumn } from '../treeConfig';
|
||||
import { dataSourceFormConfigs } from '@/components/ConnectionEdit/config/dataSource';
|
||||
import { IConnectionConfig } from '@/components/ConnectionEdit/config/types';
|
||||
import { IWorkspaceModelType } from '@/models/workspace';
|
||||
import EditDialog from '@/components/EditDialog';
|
||||
import { ConsoleStatus, ConsoleOpenedStatus } from '@/constants';
|
||||
import MonacoEditor, { IExportRefFunction, IRangeType } from '@/components/Console/MonacoEditor';
|
||||
|
||||
type MenuItem = Required<MenuProps>['items'][number];
|
||||
import MonacoEditor from '@/components/Console/MonacoEditor';
|
||||
|
||||
export type IProps = {
|
||||
className?: string;
|
||||
@ -116,6 +111,22 @@ function TreeNodeRightClick(props: IProps) {
|
||||
}
|
||||
}
|
||||
},
|
||||
[OperationColumn.EditTable]: (data) => {
|
||||
return {
|
||||
text: i18n('workspace.menu.editTable'),
|
||||
icon: '\ue602',
|
||||
handle: () => {
|
||||
dispatch({
|
||||
type: 'workspace/setCreateTabIntro',
|
||||
payload: {
|
||||
type: CreateTabIntroType.EditorTable,
|
||||
workspaceTabType: WorkspaceTabType.EditTable,
|
||||
treeNodeData: data,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
[OperationColumn.EditSource]: (data) => {
|
||||
return {
|
||||
text: '编辑数据源',
|
||||
@ -148,6 +159,7 @@ function TreeNodeRightClick(props: IProps) {
|
||||
payload: {
|
||||
...curWorkspaceParams,
|
||||
extraParams: curWorkspaceParams,
|
||||
refresh: true,
|
||||
},
|
||||
callback: () => {
|
||||
message.success(i18n('common.text.submittedSuccessfully'))
|
||||
|
@ -62,7 +62,8 @@ export enum OperationColumn {
|
||||
DeleteTable = 'deleteTable',
|
||||
ExportDDL = 'exportDDL',
|
||||
EditSource = 'editSource',
|
||||
Top = 'top'
|
||||
Top = 'top',
|
||||
EditTable = 'editTable',
|
||||
}
|
||||
|
||||
export interface ITreeConfigItem {
|
||||
@ -104,7 +105,7 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = {
|
||||
getChildren: (params) => {
|
||||
return new Promise((r: (value: ITreeNode[]) => void, j) => {
|
||||
connectionService.getDBList(params).then(res => {
|
||||
const data: ITreeNode[] = res.map((t:any)=> {
|
||||
const data: ITreeNode[] = res.map((t: any) => {
|
||||
return {
|
||||
key: t.name,
|
||||
name: t.name,
|
||||
@ -238,7 +239,7 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = {
|
||||
})
|
||||
},
|
||||
operationColumn: [
|
||||
OperationColumn.ExportDDL, OperationColumn.DeleteTable, OperationColumn.Top
|
||||
OperationColumn.Top, OperationColumn.ExportDDL, OperationColumn.EditTable, OperationColumn.DeleteTable,
|
||||
],
|
||||
},
|
||||
|
||||
|
@ -1,25 +1,47 @@
|
||||
@import '../../../../../styles/var.less';
|
||||
|
||||
.workspaceHeader{
|
||||
.workspaceHeader {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-left: 10px;
|
||||
flex-shrink: 0;
|
||||
font-size: 14px;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
border-bottom: 1px solid var(--color-border-secondary);
|
||||
font-weight: bold;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.changeBox{
|
||||
.workspaceHeaderRight{
|
||||
width: 0px;
|
||||
flex: 1;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.workspaceHeaderLeft{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.refreshBox{
|
||||
.workspaceHeaderCenter{
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 0px 40px;
|
||||
}
|
||||
|
||||
.databaseTypeIcon{
|
||||
margin-right: 10px;
|
||||
font-weight: 400;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.refreshBox {
|
||||
flex-shrink: 0;
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
height: 18px;
|
||||
@ -30,52 +52,53 @@
|
||||
font-weight: normal;
|
||||
cursor: pointer;
|
||||
color: var(--color-text-secondary);
|
||||
&:hover{
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
i{
|
||||
i {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.spin{
|
||||
.spin {
|
||||
transform: scale(0.6);
|
||||
}
|
||||
}
|
||||
|
||||
.crumbsItem{
|
||||
.crumbsItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 26px;
|
||||
cursor: pointer;
|
||||
padding: 0px 6px;
|
||||
border-radius: 6px;
|
||||
.f-single-line();
|
||||
|
||||
&:hover{
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
background-color: var(--color-hover-bg);
|
||||
}
|
||||
|
||||
.typeIcon{
|
||||
.typeIcon {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
.arrow{
|
||||
flex-shrink: 0;
|
||||
font-size: 10px;
|
||||
margin: 0px 4px;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
|
||||
.refreshIcon{
|
||||
.refreshIcon {
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.noConnectionModal{
|
||||
.noConnectionModal {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.mainText{
|
||||
|
||||
.mainText {
|
||||
}
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { IMainPageType } from '@/models/mainPage';
|
||||
import { Cascader, Spin, Modal, Button, Tag } from 'antd';
|
||||
import { databaseMap, TreeNodeType } from '@/constants';
|
||||
import { treeConfig } from '../Tree/treeConfig';
|
||||
import { useUpdateEffect } from '@/hooks/useUpdateEffect'
|
||||
import { useUpdateEffect } from '@/hooks/useUpdateEffect';
|
||||
import styles from './index.less';
|
||||
|
||||
interface IProps {
|
||||
@ -39,20 +39,20 @@ const WorkspaceHeader = memo<IProps>((props) => {
|
||||
|
||||
useEffect(() => {
|
||||
if (curPage !== 'workspace') {
|
||||
return
|
||||
return;
|
||||
}
|
||||
// 如果没有curConnection默认选第一个
|
||||
if (!curConnection?.id && connectionList.length) {
|
||||
connectionChange([connectionList[0].id], [connectionList[0]]);
|
||||
return
|
||||
return;
|
||||
}
|
||||
// 如果都有的话
|
||||
if (curConnection?.id && connectionList.length) {
|
||||
// 如果curConnection不再connectionList里,也是默认选第一个
|
||||
const flag = connectionList.findIndex((t: any) => t.id === curConnection?.id)
|
||||
const flag = connectionList.findIndex((t: any) => t.id === curConnection?.id);
|
||||
if (flag === -1) {
|
||||
connectionChange([connectionList[0].id], [connectionList[0]]);
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果切换了curConnection 导致curWorkspaceParams与curConnection不同
|
||||
@ -61,9 +61,9 @@ const WorkspaceHeader = memo<IProps>((props) => {
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.alias,
|
||||
databaseType: curConnection.type,
|
||||
})
|
||||
setCurDBOptions([])
|
||||
setCurSchemaOptions([])
|
||||
});
|
||||
setCurDBOptions([]);
|
||||
setCurSchemaOptions([]);
|
||||
}
|
||||
|
||||
// 获取database列表
|
||||
@ -71,104 +71,120 @@ const WorkspaceHeader = memo<IProps>((props) => {
|
||||
setIsRefresh(false);
|
||||
}
|
||||
// connectionList转换成可用的ConnectionOptions
|
||||
setConnectionOptions(connectionList?.map(t => {
|
||||
return {
|
||||
value: t.id,
|
||||
label: t.alias,
|
||||
}
|
||||
}));
|
||||
}, [connectionList, curConnection, curPage])
|
||||
setConnectionOptions(
|
||||
connectionList?.map((t) => {
|
||||
return {
|
||||
value: t.id,
|
||||
label: t.alias,
|
||||
};
|
||||
}),
|
||||
);
|
||||
}, [connectionList, curConnection, curPage]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (!connectionList.length) {
|
||||
dispatch({
|
||||
type: 'workspace/setCurWorkspaceParams',
|
||||
payload: {}
|
||||
})
|
||||
payload: {},
|
||||
});
|
||||
dispatch({
|
||||
type: 'connection/setCurConnection',
|
||||
payload: {}
|
||||
})
|
||||
payload: {},
|
||||
});
|
||||
}
|
||||
}, [connectionList])
|
||||
}, [connectionList]);
|
||||
|
||||
function getDatabaseList(refresh = false) {
|
||||
setCascaderLoading(true);
|
||||
if (!curConnection?.id) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
treeConfig[TreeNodeType.DATA_SOURCE].getChildren?.({
|
||||
dataSourceId: curConnection.id,
|
||||
refresh,
|
||||
extraParams: {
|
||||
databaseType: curConnection.type,
|
||||
treeConfig[TreeNodeType.DATA_SOURCE]
|
||||
.getChildren?.({
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.name,
|
||||
}
|
||||
}).then(res => {
|
||||
const dbList = res?.map((t) => {
|
||||
return {
|
||||
value: t.key,
|
||||
label: t.name,
|
||||
}
|
||||
}) || []
|
||||
setCurDBOptions(dbList);
|
||||
// 如果是切换那么就默认取列表的第一个database, 如果不是切换那么就取缓存的,如果缓存没有还是取列表第一个(这里是兜底,如果原先他并没有database,后来他加了database,如果还是取缓存的空就不对了)
|
||||
const databaseName = curWorkspaceParams.dataSourceId !== curConnection?.id ? dbList[0]?.label : curWorkspaceParams.databaseName || dbList[0]?.label
|
||||
getSchemaList(databaseName, refresh);
|
||||
}).catch(error => {
|
||||
setCascaderLoading(false);
|
||||
})
|
||||
refresh,
|
||||
extraParams: {
|
||||
databaseType: curConnection.type,
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.name,
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
const dbList =
|
||||
res?.map((t) => {
|
||||
return {
|
||||
value: t.key,
|
||||
label: t.name,
|
||||
};
|
||||
}) || [];
|
||||
setCurDBOptions(dbList);
|
||||
// 如果是切换那么就默认取列表的第一个database, 如果不是切换那么就取缓存的,如果缓存没有还是取列表第一个(这里是兜底,如果原先他并没有database,后来他加了database,如果还是取缓存的空就不对了)
|
||||
const databaseName =
|
||||
curWorkspaceParams.dataSourceId !== curConnection?.id
|
||||
? dbList[0]?.label
|
||||
: curWorkspaceParams.databaseName || dbList[0]?.label;
|
||||
getSchemaList(databaseName, refresh);
|
||||
})
|
||||
.catch((error) => {
|
||||
setCascaderLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
function getSchemaList(databaseName: string | null | undefined, refresh = false) {
|
||||
if (!curConnection?.id) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
treeConfig[TreeNodeType.DATABASE].getChildren?.({
|
||||
dataSourceId: curConnection.id,
|
||||
databaseName: databaseName,
|
||||
refresh,
|
||||
extraParams: {
|
||||
treeConfig[TreeNodeType.DATABASE]
|
||||
.getChildren?.({
|
||||
dataSourceId: curConnection.id,
|
||||
databaseName: databaseName,
|
||||
databaseType: curConnection.type,
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.name,
|
||||
}
|
||||
}).then(res => {
|
||||
const schemaList = res?.map((t) => {
|
||||
return {
|
||||
value: t.key,
|
||||
label: t.name,
|
||||
}
|
||||
}) || []
|
||||
setCurSchemaOptions(schemaList);
|
||||
const schemaName = curWorkspaceParams.dataSourceId !== curConnection?.id ? schemaList[0]?.label : curWorkspaceParams.schemaName || schemaList[0]?.label
|
||||
const data: any = {
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.alias,
|
||||
databaseType: curConnection.type,
|
||||
databaseName: databaseName || null,
|
||||
schemaName: schemaName || null
|
||||
}
|
||||
|
||||
setCurWorkspaceParams(data)
|
||||
|
||||
}).catch(() => {
|
||||
setCurWorkspaceParams({
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.alias,
|
||||
databaseType: curConnection.type,
|
||||
databaseName: databaseName || null,
|
||||
refresh,
|
||||
extraParams: {
|
||||
databaseName: databaseName,
|
||||
databaseType: curConnection.type,
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.name,
|
||||
},
|
||||
})
|
||||
}).finally(() => {
|
||||
setCascaderLoading(false)
|
||||
})
|
||||
.then((res) => {
|
||||
const schemaList =
|
||||
res?.map((t) => {
|
||||
return {
|
||||
value: t.key,
|
||||
label: t.name,
|
||||
};
|
||||
}) || [];
|
||||
setCurSchemaOptions(schemaList);
|
||||
const schemaName =
|
||||
curWorkspaceParams.dataSourceId !== curConnection?.id
|
||||
? schemaList[0]?.label
|
||||
: curWorkspaceParams.schemaName || schemaList[0]?.label;
|
||||
const data: any = {
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.alias,
|
||||
databaseType: curConnection.type,
|
||||
databaseName: databaseName || null,
|
||||
schemaName: schemaName || null,
|
||||
};
|
||||
|
||||
setCurWorkspaceParams(data);
|
||||
})
|
||||
.catch(() => {
|
||||
setCurWorkspaceParams({
|
||||
dataSourceId: curConnection.id,
|
||||
dataSourceName: curConnection.alias,
|
||||
databaseType: curConnection.type,
|
||||
databaseName: databaseName || null,
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
setCascaderLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
function setCurWorkspaceParams(payload: IWorkspaceModelType['state']['curWorkspaceParams']) {
|
||||
if (lodash.isEqual(curWorkspaceParams, payload)) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
@ -183,21 +199,21 @@ const WorkspaceHeader = memo<IProps>((props) => {
|
||||
dispatch({
|
||||
type: 'connection/fetchConnectionList',
|
||||
payload: {
|
||||
refresh: true
|
||||
refresh: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
// 连接切换
|
||||
function connectionChange(id: any, data: any) {
|
||||
connectionList.map(t => {
|
||||
connectionList.map((t) => {
|
||||
if (t.id === id[0] && curWorkspaceParams.dataSourceId !== id[0]) {
|
||||
dispatch({
|
||||
type: 'connection/setCurConnection',
|
||||
payload: t
|
||||
payload: t,
|
||||
});
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// 数据库切换
|
||||
@ -205,12 +221,12 @@ const WorkspaceHeader = memo<IProps>((props) => {
|
||||
if (selectedOptions[0].label !== curWorkspaceParams.databaseName) {
|
||||
getSchemaList(selectedOptions[0].label);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// schema切换
|
||||
function schemaChange(valueArr: any, selectedOptions: any) {
|
||||
if (selectedOptions[0].label !== curWorkspaceParams.schemaName) {
|
||||
setCurWorkspaceParams({ ...curWorkspaceParams, schemaName: selectedOptions[0].value })
|
||||
setCurWorkspaceParams({ ...curWorkspaceParams, schemaName: selectedOptions[0].value });
|
||||
}
|
||||
}
|
||||
|
||||
@ -218,89 +234,108 @@ const WorkspaceHeader = memo<IProps>((props) => {
|
||||
getConnectionList();
|
||||
}
|
||||
|
||||
return <>
|
||||
{
|
||||
!!connectionList.length &&
|
||||
<div className={styles.workspaceHeader}>
|
||||
<div className={styles.changeBox}>
|
||||
<Cascader
|
||||
popupClassName={styles.cascaderPopup}
|
||||
options={connectionOptions}
|
||||
onChange={connectionChange}
|
||||
bordered={false}
|
||||
defaultValue={[curConnection?.id || '']}
|
||||
>
|
||||
<div className={styles.crumbsItem}>
|
||||
<div className={styles.connectionTag}>
|
||||
{(curConnection?.id && curConnection?.environment?.shortName) && <Tag color={curConnection?.environment?.color?.toLocaleLowerCase()}>{curConnection?.environment?.shortName}</Tag>}
|
||||
return (
|
||||
<>
|
||||
{!!connectionList.length && (
|
||||
<div className={styles.workspaceHeader}>
|
||||
<div className={styles.workspaceHeaderLeft}>
|
||||
<Cascader
|
||||
popupClassName={styles.cascaderPopup}
|
||||
options={connectionOptions}
|
||||
onChange={connectionChange}
|
||||
bordered={false}
|
||||
defaultValue={[curConnection?.id || '']}
|
||||
>
|
||||
<div className={styles.crumbsItem}>
|
||||
<Iconfont className={styles.databaseTypeIcon} code={databaseMap[curWorkspaceParams.databaseType]?.icon} />
|
||||
<div className={styles.text}>{curWorkspaceParams.dataSourceName}</div>
|
||||
</div>
|
||||
<div className={styles.text}>{curWorkspaceParams.dataSourceName}</div>
|
||||
</Cascader>
|
||||
<Iconfont className={styles.arrow} code="" />
|
||||
{!!curDBOptions?.length && (
|
||||
<Cascader
|
||||
popupClassName={styles.cascaderPopup}
|
||||
options={curDBOptions}
|
||||
onChange={databaseChange}
|
||||
bordered={false}
|
||||
defaultValue={[curWorkspaceParams?.databaseName || '']}
|
||||
>
|
||||
<div className={styles.crumbsItem}>
|
||||
<div className={styles.text}>{curWorkspaceParams.databaseName}</div>
|
||||
</div>
|
||||
</Cascader>
|
||||
)}
|
||||
{!!curSchemaOptions.length && <Iconfont className={styles.arrow} code="" />}
|
||||
{!!curSchemaOptions.length && (
|
||||
<Cascader
|
||||
popupClassName={styles.cascaderPopup}
|
||||
options={curSchemaOptions}
|
||||
onChange={schemaChange}
|
||||
bordered={false}
|
||||
defaultValue={[curWorkspaceParams?.schemaName || '']}
|
||||
>
|
||||
<div className={styles.crumbsItem}>
|
||||
<div className={styles.text}>{curWorkspaceParams.schemaName}</div>
|
||||
</div>
|
||||
</Cascader>
|
||||
)}
|
||||
<div className={styles.refreshBox} onClick={handelRefresh}>
|
||||
{cascaderLoading ? (
|
||||
<Spin className={styles.spin} />
|
||||
) : (
|
||||
<Iconfont className={styles.typeIcon} code="" />
|
||||
)}
|
||||
</div>
|
||||
</Cascader>
|
||||
<Iconfont className={styles.arrow} code="" />
|
||||
{
|
||||
!!curDBOptions?.length &&
|
||||
<Cascader
|
||||
popupClassName={styles.cascaderPopup}
|
||||
options={curDBOptions}
|
||||
onChange={databaseChange}
|
||||
bordered={false}
|
||||
defaultValue={[curWorkspaceParams?.databaseName || '']}
|
||||
>
|
||||
<div className={styles.crumbsItem}>
|
||||
<div className={styles.text}>{curWorkspaceParams.databaseName}</div>
|
||||
</div>
|
||||
</Cascader>
|
||||
}
|
||||
{
|
||||
!!curSchemaOptions.length && <Iconfont className={styles.arrow} code="" />
|
||||
}
|
||||
{
|
||||
!!curSchemaOptions.length &&
|
||||
<Cascader
|
||||
popupClassName={styles.cascaderPopup}
|
||||
options={curSchemaOptions}
|
||||
onChange={schemaChange}
|
||||
bordered={false}
|
||||
defaultValue={[curWorkspaceParams?.schemaName || '']}
|
||||
>
|
||||
<div className={styles.crumbsItem}>
|
||||
<div className={styles.text}>{curWorkspaceParams.schemaName}</div>
|
||||
</div>
|
||||
</Cascader>
|
||||
}
|
||||
<div className={styles.refreshBox} onClick={handelRefresh}>
|
||||
{cascaderLoading ? <Spin className={styles.spin} /> : <Iconfont className={styles.typeIcon} code='' />}
|
||||
</div>
|
||||
<div className={classnames(styles.connectionTag,styles.workspaceHeaderCenter)}>
|
||||
{curConnection?.id && curConnection?.environment?.shortName && (
|
||||
<Tag color={curConnection?.environment?.color?.toLocaleLowerCase()}>
|
||||
{curConnection?.environment?.shortName}
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.workspaceHeaderRight}></div>
|
||||
</div>
|
||||
</div >
|
||||
}
|
||||
<Modal
|
||||
open={noConnectionModal}
|
||||
closeIcon={<></>}
|
||||
keyboard={false}
|
||||
maskClosable={false}
|
||||
title='温馨提示'
|
||||
footer={[]}
|
||||
>
|
||||
<div className={styles.noConnectionModal}>
|
||||
<div className={styles.mainText}>
|
||||
您当前还没有创建任何连接
|
||||
)}
|
||||
<Modal
|
||||
open={noConnectionModal}
|
||||
closeIcon={<></>}
|
||||
keyboard={false}
|
||||
maskClosable={false}
|
||||
title="温馨提示"
|
||||
footer={[]}
|
||||
>
|
||||
<div className={styles.noConnectionModal}>
|
||||
<div className={styles.mainText}>您当前还没有创建任何连接</div>
|
||||
<Button
|
||||
type="primary"
|
||||
className={styles.createButton}
|
||||
onClick={() => {
|
||||
setNoConnectionModal(false);
|
||||
dispatch({
|
||||
type: 'mainPage/updateCurPage',
|
||||
payload: 'connections',
|
||||
});
|
||||
}}
|
||||
>
|
||||
创建连接
|
||||
</Button>
|
||||
</div>
|
||||
<Button type='primary' className={styles.createButton} onClick={() => {
|
||||
setNoConnectionModal(false);
|
||||
dispatch({
|
||||
type: 'mainPage/updateCurPage',
|
||||
payload: 'connections',
|
||||
});
|
||||
}}>创建连接</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
})
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
});
|
||||
|
||||
export default connect(
|
||||
({ connection, workspace, mainPage }: { connection: IConnectionModelType; workspace: IWorkspaceModelType, mainPage: IMainPageType }) => ({
|
||||
({
|
||||
connection,
|
||||
workspace,
|
||||
mainPage,
|
||||
}: {
|
||||
connection: IConnectionModelType;
|
||||
workspace: IWorkspaceModelType;
|
||||
mainPage: IMainPageType;
|
||||
}) => ({
|
||||
connectionModel: connection,
|
||||
workspaceModel: workspace,
|
||||
mainPageModel: mainPage,
|
||||
|
@ -4,7 +4,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background-color: var(--color-bg-elevated);
|
||||
background-color: var(--color-bg-subtle);
|
||||
padding: 10px 20px 0px;
|
||||
box-sizing: border-box;
|
||||
min-width: 200px;
|
||||
|
@ -1,18 +1,13 @@
|
||||
import React, { memo, useState, useEffect, useRef, useContext, useMemo } from 'react';
|
||||
import React, { memo } from 'react';
|
||||
import classnames from 'classnames';
|
||||
import i18n from '@/i18n';
|
||||
import { connect } from 'umi';
|
||||
import { Cascader, Divider, Input, Dropdown, Button, Spin } from 'antd';
|
||||
import { Divider, Button } from 'antd';
|
||||
import Iconfont from '@/components/Iconfont';
|
||||
import LoadingContent from '@/components/Loading/LoadingContent';
|
||||
import { IConnectionModelType } from '@/models/connection';
|
||||
import { IWorkspaceModelType } from '@/models/workspace';
|
||||
import historyServer from '@/service/history';
|
||||
import Tree from '../Tree';
|
||||
import { TreeNodeType, ConsoleStatus, ConsoleOpenedStatus, OperationType } from '@/constants';
|
||||
import { IConsole, ITreeNode, ICreateConsole } from '@/typings';
|
||||
import { ConsoleStatus, ConsoleOpenedStatus, WorkspaceTabType } from '@/constants';
|
||||
import styles from './index.less';
|
||||
import { approximateTreeNode, approximateList } from '@/utils';
|
||||
import historyService from '@/service/history';
|
||||
import TableList from '../TableList';
|
||||
import SaveList from '../SaveList';
|
||||
@ -21,16 +16,12 @@ interface IProps {
|
||||
className?: string;
|
||||
workspaceModel: IWorkspaceModelType['state'],
|
||||
dispatch: any;
|
||||
tableLoading: boolean;
|
||||
databaseLoading: boolean;
|
||||
}
|
||||
|
||||
const dvaModel = connect(
|
||||
({ connection, workspace, loading }: { connection: IConnectionModelType; workspace: IWorkspaceModelType, loading: any }) => ({
|
||||
({ connection, workspace }: { connection: IConnectionModelType; workspace: IWorkspaceModelType }) => ({
|
||||
connectionModel: connection,
|
||||
workspaceModel: workspace,
|
||||
tableLoading: loading.effects['workspace/fetchGetCurTableList'],
|
||||
databaseLoading: loading.effects['workspace/fetchDatabaseAndSchema'],
|
||||
}),
|
||||
);
|
||||
|
||||
@ -38,31 +29,10 @@ const WorkspaceLeft = memo<IProps>(function (props) {
|
||||
const { className, workspaceModel, dispatch } = props;
|
||||
const { curWorkspaceParams, openConsoleList } = workspaceModel;
|
||||
|
||||
function getConsoleList() {
|
||||
let p: any = {
|
||||
pageNo: 1,
|
||||
pageSize: 999,
|
||||
orderByDesc: false,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
...curWorkspaceParams,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: p,
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: res.data,
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const addConsole = (params?: ICreateConsole) => {
|
||||
const addConsole = () => {
|
||||
const { dataSourceId, databaseName, schemaName, databaseType } = curWorkspaceParams
|
||||
let p = {
|
||||
name: `new console${openConsoleList?.length || ''}`,
|
||||
let params = {
|
||||
name: `new console`,
|
||||
ddl: '',
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
@ -70,29 +40,31 @@ const WorkspaceLeft = memo<IProps>(function (props) {
|
||||
type: databaseType,
|
||||
status: ConsoleStatus.DRAFT,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
operationType: OperationType.CONSOLE
|
||||
operationType: WorkspaceTabType.CONSOLE,
|
||||
tabType: WorkspaceTabType.CONSOLE,
|
||||
}
|
||||
historyService.saveConsole(params || p).then(res => {
|
||||
|
||||
historyService.saveConsole(params).then(res => {
|
||||
dispatch({
|
||||
type: 'workspace/setCurConsoleId',
|
||||
payload: res,
|
||||
});
|
||||
getConsoleList();
|
||||
type: 'workspace/setCreateConsoleIntro',
|
||||
payload: {
|
||||
id: res,
|
||||
type: WorkspaceTabType.CONSOLE,
|
||||
title: params.name,
|
||||
uniqueData: params,
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function createConsole() {
|
||||
addConsole();
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 't') {
|
||||
e.preventDefault();
|
||||
addConsole();
|
||||
}
|
||||
}, false)
|
||||
}, [])
|
||||
// useEffect(() => {
|
||||
// document.addEventListener('keydown', (e) => {
|
||||
// if ((e.ctrlKey || e.metaKey) && e.key === 't') {
|
||||
// e.preventDefault();
|
||||
// addConsole();
|
||||
// }
|
||||
// }, false)
|
||||
// }, [])
|
||||
|
||||
return (
|
||||
<div className={classnames(styles.box, className)}>
|
||||
@ -100,7 +72,7 @@ const WorkspaceLeft = memo<IProps>(function (props) {
|
||||
<Divider className={styles.divider} />
|
||||
<TableList />
|
||||
<div className={styles.createButtonBox}>
|
||||
<Button className={styles.createButton} type="primary" onClick={createConsole}>
|
||||
<Button className={styles.createButton} type="primary" onClick={addConsole}>
|
||||
<Iconfont code="" />
|
||||
{i18n('common.button.createConsole')}
|
||||
</Button>
|
||||
|
@ -1,457 +0,0 @@
|
||||
import React, { memo, useRef, useEffect, useState } from 'react';
|
||||
import { connect } from 'umi';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import { ConsoleOpenedStatus, ConsoleStatus, DatabaseTypeCode, TreeNodeType, operationTypeConfig, OperationType } from '@/constants';
|
||||
import { IConsole, ICreateConsole } from '@/typings';
|
||||
import historyService from '@/service/history';
|
||||
import sqlService from '@/service/sql';
|
||||
import Tabs, { IOption } from '@/components/Tabs';
|
||||
import LoadingContent from '@/components/Loading/LoadingContent';
|
||||
import ShortcutKey from '@/components/ShortcutKey';
|
||||
import WorkspaceRightItem from '../WorkspaceRightItem';
|
||||
import { IWorkspaceModelState, IWorkspaceModelType } from '@/models/workspace';
|
||||
import { IAIState } from '@/models/ai';
|
||||
import { handleLocalStorageSavedConsole } from '@/utils';
|
||||
import { useUpdateEffect } from '@/hooks/useUpdateEffect';
|
||||
import Tree from 'antd/es/tree/Tree';
|
||||
import Iconfont from '@/components/Iconfont';
|
||||
|
||||
interface IProps {
|
||||
className?: string;
|
||||
workspaceModel: IWorkspaceModelState;
|
||||
aiModel: IAIState;
|
||||
dispatch: any;
|
||||
}
|
||||
|
||||
const WorkspaceRight = memo<IProps>(function (props) {
|
||||
const [activeConsoleId, setActiveConsoleId] = useState<number>();
|
||||
const { className, aiModel, workspaceModel, dispatch } = props;
|
||||
const { curWorkspaceParams, doubleClickTreeNodeData, openConsoleList, curConsoleId } = workspaceModel;
|
||||
const openConsoleListRef = useRef(openConsoleList);
|
||||
|
||||
useEffect(() => {
|
||||
if (!doubleClickTreeNodeData) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/setConsoleList',
|
||||
payload: [],
|
||||
})
|
||||
|
||||
// 打开视图
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.VIEW) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, tableName, dataSourceId } = extraParams || {};
|
||||
const callback = (consoleId: number) => {
|
||||
sqlService.getViewDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
tableName: tableName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = openConsoleListRef.current?.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
ddl: res.ddl
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: newList,
|
||||
});
|
||||
})
|
||||
}
|
||||
const name = doubleClickTreeNodeData.name;
|
||||
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.TRIGGER) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, triggerName, dataSourceId, } = extraParams || {};
|
||||
const name = doubleClickTreeNodeData.name
|
||||
const callback = (consoleId: number) => {
|
||||
sqlService.getTriggerDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
triggerName: triggerName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = openConsoleListRef.current?.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
ddl: res.triggerBody
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: newList,
|
||||
});
|
||||
})
|
||||
}
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.PROCEDURE) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, procedureName, dataSourceId } = extraParams || {};
|
||||
const name = doubleClickTreeNodeData.name
|
||||
const callback = (consoleId: number) => {
|
||||
sqlService.getProcedureDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
procedureName: procedureName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = openConsoleListRef.current?.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
ddl: res.procedureBody
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: newList,
|
||||
});
|
||||
})
|
||||
}
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.FUNCTION) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, dataSourceId, functionName } = extraParams || {};
|
||||
const name = doubleClickTreeNodeData.name
|
||||
const callback = (consoleId: number) => {
|
||||
sqlService.getFunctionDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
functionName: functionName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = openConsoleListRef.current?.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
ddl: res.functionBody
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: newList,
|
||||
});
|
||||
})
|
||||
}
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.TABLE && !openConsoleList?.length) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, tableName, } = extraParams || {};
|
||||
const ddl = `SELECT * FROM ${tableName};\n`;
|
||||
const name = [databaseName, schemaName, 'console'].filter((t) => t).join('-');
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
name,
|
||||
ddl
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/setDoubleClickTreeNodeData',
|
||||
payload: '',
|
||||
});
|
||||
}, [doubleClickTreeNodeData]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (activeConsoleId) {
|
||||
localStorage.setItem('active-console-id', activeConsoleId.toString())
|
||||
} else {
|
||||
localStorage.removeItem('active-console-id')
|
||||
}
|
||||
}, [activeConsoleId])
|
||||
|
||||
useEffect(() => {
|
||||
openConsoleListRef.current = openConsoleList;
|
||||
const newActiveConsoleId = curConsoleId || activeConsoleId || Number(localStorage.getItem('active-console-id') || 0);
|
||||
// 用完之后就清掉curConsoleId
|
||||
if (!openConsoleList?.length) {
|
||||
setActiveConsoleId(undefined);
|
||||
} else if (!newActiveConsoleId) {
|
||||
setActiveConsoleId(openConsoleList[0].id);
|
||||
} else {
|
||||
// 如果你指定了让我打开哪个那我就打开哪个
|
||||
if (curConsoleId) {
|
||||
setActiveConsoleId(curConsoleId);
|
||||
dispatch({
|
||||
type: 'workspace/setCurConsoleId',
|
||||
payload: null,
|
||||
});
|
||||
return
|
||||
}
|
||||
|
||||
let flag = false;
|
||||
openConsoleList?.forEach((t) => {
|
||||
if (t.id === newActiveConsoleId) {
|
||||
flag = true;
|
||||
}
|
||||
});
|
||||
if (flag) {
|
||||
setActiveConsoleId(newActiveConsoleId);
|
||||
} else {
|
||||
// 如果发现当前列表里并没有newActiveConsoleId
|
||||
setActiveConsoleId(openConsoleList?.[openConsoleList?.length - 1].id);
|
||||
}
|
||||
}
|
||||
}, [openConsoleList]);
|
||||
|
||||
|
||||
function createConsole(params: {
|
||||
doubleClickTreeNodeData: any,
|
||||
name: string,
|
||||
callback?: Function,
|
||||
ddl?: string,
|
||||
}) {
|
||||
const { doubleClickTreeNodeData, name, callback, ddl } = params;
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, dataSourceId, dataSourceName, databaseType } = extraParams || {};
|
||||
let p: any = {
|
||||
name,
|
||||
type: databaseType!,
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName,
|
||||
schemaName: schemaName,
|
||||
dataSourceName: dataSourceName!,
|
||||
status: ConsoleStatus.DRAFT,
|
||||
operationType: doubleClickTreeNodeData.treeNodeType,
|
||||
ddl: ddl || '',
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
};
|
||||
addConsole({
|
||||
newConsole: p,
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
function getConsoleList(callback?: Function) {
|
||||
let p: any = {
|
||||
pageNo: 1,
|
||||
pageSize: 999,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
...curWorkspaceParams,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: p,
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: res.data,
|
||||
});
|
||||
callback?.();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function onChange(key: number | string) {
|
||||
setActiveConsoleId(+key);
|
||||
}
|
||||
|
||||
const onEdit = (action: 'add' | 'remove', key?: number) => {
|
||||
if (action === 'remove') {
|
||||
closeWindowTab(key!);
|
||||
}
|
||||
if (action === 'add') {
|
||||
addConsole();
|
||||
}
|
||||
};
|
||||
|
||||
const addConsole = (params?: {
|
||||
newConsole?: ICreateConsole;
|
||||
callback?: Function;
|
||||
}) => {
|
||||
const { dataSourceId, databaseName, schemaName, databaseType } = curWorkspaceParams;
|
||||
let p = {
|
||||
name: `new console${openConsoleList?.length}`,
|
||||
ddl: '',
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
schemaName: schemaName!,
|
||||
type: databaseType,
|
||||
status: ConsoleStatus.DRAFT,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
operationType: OperationType.CONSOLE,
|
||||
};
|
||||
historyService.saveConsole(params?.newConsole || p).then((res) => {
|
||||
params?.callback?.(res);
|
||||
const callback = () => {
|
||||
dispatch({
|
||||
type: 'workspace/setCurConsoleId',
|
||||
payload: res,
|
||||
});
|
||||
}
|
||||
getConsoleList(callback);
|
||||
});
|
||||
};
|
||||
|
||||
const closeWindowTab = (key: number) => {
|
||||
let newActiveKey = activeConsoleId;
|
||||
let lastIndex = -1;
|
||||
openConsoleList?.forEach((item, i) => {
|
||||
if (item.id === key) {
|
||||
lastIndex = i - 1;
|
||||
}
|
||||
});
|
||||
|
||||
const newPanes = openConsoleList?.filter((item) => item.id !== key) || [];
|
||||
if (newPanes.length && newActiveKey === key) {
|
||||
if (lastIndex >= 0) {
|
||||
newActiveKey = newPanes[lastIndex].id;
|
||||
} else {
|
||||
newActiveKey = newPanes[0].id;
|
||||
}
|
||||
}
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: newPanes,
|
||||
});
|
||||
setActiveConsoleId(newActiveKey);
|
||||
|
||||
let p: any = {
|
||||
id: key,
|
||||
tabOpened: 'n',
|
||||
};
|
||||
|
||||
const window = openConsoleList?.find((t) => t.id === key);
|
||||
if (!window?.status) {
|
||||
return;
|
||||
}
|
||||
// if (window!.status === 'DRAFT') {
|
||||
// historyService.deleteSavedConsole({ id: window!.id });
|
||||
// } else {
|
||||
historyService.updateSavedConsole(p).then(() => {
|
||||
handleLocalStorageSavedConsole(p.id, 'delete');
|
||||
});
|
||||
// }
|
||||
};
|
||||
|
||||
function renderEmpty() {
|
||||
return <div className={styles.ears}><ShortcutKey /></div>;
|
||||
}
|
||||
|
||||
function editableNameOnBlur(t: IOption) {
|
||||
let p: any = {
|
||||
id: t.value,
|
||||
name: t.label
|
||||
}
|
||||
historyService.updateSavedConsole(p).then(() => {
|
||||
getConsoleList();
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: {
|
||||
pageNo: 1,
|
||||
pageSize: 999,
|
||||
orderByDesc: true,
|
||||
status: ConsoleStatus.RELEASE,
|
||||
...curWorkspaceParams,
|
||||
},
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setConsoleList',
|
||||
payload: res.data,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={classnames(styles.box, className)}>
|
||||
<LoadingContent data={openConsoleList} handleEmpty empty={renderEmpty()}>
|
||||
<div className={styles.tabBox}>
|
||||
<Tabs
|
||||
className={styles.tabs}
|
||||
onChange={onChange}
|
||||
onEdit={onEdit as any}
|
||||
editableName={true}
|
||||
editableNameOnBlur={editableNameOnBlur}
|
||||
activeTab={activeConsoleId}
|
||||
tabs={(openConsoleList || [])?.map((t, i) => {
|
||||
return {
|
||||
prefixIcon: operationTypeConfig[t.operationType]?.icon || operationTypeConfig.console.icon,
|
||||
label: t.name,
|
||||
value: t.id,
|
||||
};
|
||||
})}
|
||||
/>
|
||||
</div>
|
||||
{openConsoleList?.map((t, index) => {
|
||||
return (
|
||||
<div
|
||||
key={t.id}
|
||||
className={classnames(styles.consoleBox, { [styles.activeConsoleBox]: activeConsoleId === t.id })}
|
||||
>
|
||||
<WorkspaceRightItem
|
||||
isActive={activeConsoleId === t.id}
|
||||
data={{
|
||||
initDDL: t.ddl,
|
||||
databaseName: curWorkspaceParams.databaseName!,
|
||||
dataSourceId: curWorkspaceParams.dataSourceId!,
|
||||
type: curWorkspaceParams.databaseType!,
|
||||
schemaName: curWorkspaceParams?.schemaName!,
|
||||
consoleId: t.id,
|
||||
consoleName: t.name,
|
||||
}}
|
||||
workspaceModel={workspaceModel}
|
||||
aiModel={aiModel}
|
||||
dispatch={dispatch}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</LoadingContent>
|
||||
</div >
|
||||
);
|
||||
});
|
||||
|
||||
const dvaModel = connect(({ workspace, ai }: { workspace: IWorkspaceModelType; ai: IAIState }) => ({
|
||||
workspaceModel: workspace,
|
||||
aiModel: ai,
|
||||
}));
|
||||
|
||||
export default dvaModel(WorkspaceRight);
|
@ -1,6 +1,6 @@
|
||||
@import '../../../../../styles/var.less';
|
||||
|
||||
.box {
|
||||
.workspaceRight {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -15,21 +15,16 @@
|
||||
}
|
||||
|
||||
.tabs {
|
||||
}
|
||||
|
||||
.consoleBox {
|
||||
position: absolute;
|
||||
top: 32px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: none;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tabBox {
|
||||
height: 32px;
|
||||
background-color: var(--color-bg-base);
|
||||
border-bottom: 1px solid var(--color-border);
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.activeConsoleBox {
|
||||
@ -48,7 +43,7 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tab-item{
|
||||
.tab-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
@ -0,0 +1,513 @@
|
||||
import React, { memo, useEffect, useState, useMemo } from 'react';
|
||||
import { connect } from 'umi';
|
||||
import styles from './index.less';
|
||||
import classnames from 'classnames';
|
||||
import { ConsoleOpenedStatus, ConsoleStatus, TreeNodeType } from '@/constants';
|
||||
import historyService from '@/service/history';
|
||||
import sqlService from '@/service/sql';
|
||||
import TabsNew, { ITabItem } from '@/components/TabsNew';
|
||||
import LoadingContent from '@/components/Loading/LoadingContent';
|
||||
import ShortcutKey from '@/components/ShortcutKey';
|
||||
import DatabaseTableEditor from '@/blocks/DatabaseTableEditor';
|
||||
import SQLExecute from '@/blocks/SQLExecute';
|
||||
import { IWorkspaceModelState, IWorkspaceModelType } from '@/models/workspace';
|
||||
import { IAIState } from '@/models/ai';
|
||||
import { handleLocalStorageSavedConsole } from '@/utils';
|
||||
import { useUpdateEffect } from '@/hooks/useUpdateEffect';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import { IWorkspaceTab } from '@/typings'
|
||||
import { WorkspaceTabType, workspaceTabConfig } from '@/constants';
|
||||
|
||||
interface IProps {
|
||||
className?: string;
|
||||
workspaceModel: IWorkspaceModelState;
|
||||
aiModel: IAIState;
|
||||
dispatch: any;
|
||||
}
|
||||
|
||||
const WorkspaceRight = memo<IProps>(function (props) {
|
||||
const { className, aiModel, workspaceModel, dispatch } = props;
|
||||
// 活跃的TabID
|
||||
const [activeConsoleId, setActiveConsoleId] = useState<number | string>();
|
||||
// 工作台tab列表
|
||||
const [workspaceTabList, setWorkspaceTabList] = useState<IWorkspaceTab[]>([]);
|
||||
|
||||
const { curWorkspaceParams, doubleClickTreeNodeData, createTabIntro, openConsoleList, curConsoleId, createConsoleIntro } = workspaceModel;
|
||||
|
||||
// 根据保存的console列表生成tab列表
|
||||
useEffect(() => {
|
||||
const newTabList = openConsoleList?.map(t => {
|
||||
return {
|
||||
id: t.id,
|
||||
title: t.name,
|
||||
type: t.operationType,
|
||||
uniqueData: t
|
||||
}
|
||||
})
|
||||
setWorkspaceTabList(newTabList || [])
|
||||
}, [openConsoleList])
|
||||
|
||||
useEffect(() => {
|
||||
if (createConsoleIntro) {
|
||||
if (workspaceTabList.findIndex(t => t.id === createConsoleIntro.id) === -1) {
|
||||
setWorkspaceTabList([...workspaceTabList, createConsoleIntro])
|
||||
}
|
||||
setActiveConsoleId(createConsoleIntro.id)
|
||||
}
|
||||
dispatch({
|
||||
type: 'workspace/setCreateConsoleIntro',
|
||||
payload: undefined,
|
||||
})
|
||||
}, [createConsoleIntro])
|
||||
|
||||
// 监听编辑表事件
|
||||
useEffect(() => {
|
||||
if (createTabIntro) {
|
||||
// 如果已经打开了这个表的编辑页面,那么就切换到这个页面
|
||||
const flag = workspaceTabList?.find(t => t.uniqueData?.tableName === createTabIntro.treeNodeData.name);
|
||||
if (flag) {
|
||||
setActiveConsoleId(flag.id);
|
||||
return
|
||||
}
|
||||
|
||||
const id = uuidV4();
|
||||
const newData = {
|
||||
id,
|
||||
type: createTabIntro.workspaceTabType,
|
||||
title: `edit-${createTabIntro.treeNodeData.name}`,
|
||||
uniqueData: {
|
||||
tableName: createTabIntro.treeNodeData.name,
|
||||
}
|
||||
}
|
||||
setWorkspaceTabList([...workspaceTabList, newData])
|
||||
setActiveConsoleId(id);
|
||||
|
||||
// 用完之后就清掉createTabIntro
|
||||
dispatch({
|
||||
type: 'workspace/setCreateTabIntro',
|
||||
payload: null,
|
||||
})
|
||||
}
|
||||
}, [createTabIntro])
|
||||
|
||||
// 监听双击树节点事件 生成console
|
||||
useEffect(() => {
|
||||
if (!doubleClickTreeNodeData) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 打开视图
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.VIEW) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, tableName, dataSourceId } = extraParams || {};
|
||||
const callback = (consoleId: number, workspaceTabList: IWorkspaceTab[]) => {
|
||||
sqlService.getViewDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
tableName: tableName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = workspaceTabList.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
uniqueData: {
|
||||
...t.uniqueData,
|
||||
ddl: res.ddl
|
||||
}
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
setWorkspaceTabList(newList || [])
|
||||
})
|
||||
}
|
||||
const name = doubleClickTreeNodeData.name;
|
||||
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
workSpaceTabType: WorkspaceTabType.VIEW,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.TRIGGER) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, triggerName, dataSourceId } = extraParams || {};
|
||||
const name = doubleClickTreeNodeData.name
|
||||
const callback = (consoleId: number, workspaceTabList: IWorkspaceTab[]) => {
|
||||
sqlService.getTriggerDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
triggerName: triggerName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = workspaceTabList.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
uniqueData: {
|
||||
...t.uniqueData,
|
||||
ddl: res.triggerBody
|
||||
}
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
setWorkspaceTabList(newList || [])
|
||||
})
|
||||
}
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
workSpaceTabType: WorkspaceTabType.TRIGGER,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.PROCEDURE) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, procedureName, dataSourceId } = extraParams || {};
|
||||
const name = doubleClickTreeNodeData.name
|
||||
const callback = (consoleId: number, workspaceTabList: IWorkspaceTab[]) => {
|
||||
sqlService.getProcedureDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
procedureName: procedureName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = workspaceTabList.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
uniqueData: {
|
||||
...t.uniqueData,
|
||||
ddl: res.procedureBody
|
||||
}
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
setWorkspaceTabList(newList || [])
|
||||
})
|
||||
}
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
workSpaceTabType: WorkspaceTabType.PROCEDURE,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.FUNCTION) {
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, dataSourceId, functionName } = extraParams || {};
|
||||
const name = doubleClickTreeNodeData.name
|
||||
const callback = (consoleId: number, workspaceTabList: IWorkspaceTab[]) => {
|
||||
sqlService.getFunctionDetail({
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
functionName: functionName!,
|
||||
schemaName,
|
||||
}).then(res => {
|
||||
// 更新ddl
|
||||
const newList = workspaceTabList?.map(t => {
|
||||
if (t.id === consoleId) {
|
||||
return {
|
||||
...t,
|
||||
uniqueData: {
|
||||
...t.uniqueData,
|
||||
ddl: res.functionBody
|
||||
}
|
||||
}
|
||||
}
|
||||
return t
|
||||
})
|
||||
setWorkspaceTabList(newList || [])
|
||||
})
|
||||
}
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
workSpaceTabType: WorkspaceTabType.FUNCTION,
|
||||
name,
|
||||
callback
|
||||
});
|
||||
}
|
||||
|
||||
if (doubleClickTreeNodeData.treeNodeType === TreeNodeType.TABLE) {
|
||||
// 如果workspaceTabList没有可以添加select * from table的地方那么才需要创建
|
||||
const flag = workspaceTabList.some(t => [WorkspaceTabType.CONSOLE, WorkspaceTabType.FUNCTION, WorkspaceTabType.PROCEDURE, WorkspaceTabType.TRIGGER, WorkspaceTabType.VIEW].includes(t.type))
|
||||
if (flag) {
|
||||
return
|
||||
}
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, tableName, } = extraParams || {};
|
||||
const ddl = `SELECT * FROM ${tableName};\n`;
|
||||
const name = [databaseName, schemaName, 'console'].filter((t) => t).join('-');
|
||||
createConsole({
|
||||
doubleClickTreeNodeData,
|
||||
workSpaceTabType: WorkspaceTabType.CONSOLE,
|
||||
name,
|
||||
ddl
|
||||
});
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/setDoubleClickTreeNodeData',
|
||||
payload: '',
|
||||
});
|
||||
}, [doubleClickTreeNodeData]);
|
||||
|
||||
useUpdateEffect(() => {
|
||||
if (activeConsoleId) {
|
||||
localStorage.setItem('active-console-id', activeConsoleId.toString())
|
||||
} else {
|
||||
localStorage.removeItem('active-console-id')
|
||||
}
|
||||
}, [activeConsoleId])
|
||||
|
||||
// useEffect(() => {
|
||||
// openConsoleListRef.current = openConsoleList;
|
||||
// const newActiveConsoleId = curConsoleId || activeConsoleId || Number(localStorage.getItem('active-console-id') || 0);
|
||||
// // 用完之后就清掉curConsoleId
|
||||
// if (!openConsoleList?.length) {
|
||||
// setActiveConsoleId(undefined);
|
||||
// } else if (!newActiveConsoleId) {
|
||||
// setActiveConsoleId(openConsoleList[0].id);
|
||||
// } else {
|
||||
// // 如果你指定了让我打开哪个那我就打开哪个
|
||||
// if (curConsoleId) {
|
||||
// setActiveConsoleId(curConsoleId);
|
||||
// dispatch({
|
||||
// type: 'workspace/setCurConsoleId',
|
||||
// payload: null,
|
||||
// });
|
||||
// return
|
||||
// }
|
||||
|
||||
// let flag = false;
|
||||
// openConsoleList?.forEach((t) => {
|
||||
// if (t.id === newActiveConsoleId) {
|
||||
// flag = true;
|
||||
// }
|
||||
// });
|
||||
// if (flag) {
|
||||
// setActiveConsoleId(newActiveConsoleId);
|
||||
// } else {
|
||||
// // 如果发现当前列表里并没有newActiveConsoleId
|
||||
// setActiveConsoleId(openConsoleList?.[openConsoleList?.length - 1].id);
|
||||
// }
|
||||
// }
|
||||
// }, [openConsoleList]);
|
||||
|
||||
function createConsole(params: {
|
||||
doubleClickTreeNodeData: any,
|
||||
workSpaceTabType: WorkspaceTabType,
|
||||
name: string,
|
||||
callback?: Function,
|
||||
ddl?: string,
|
||||
}) {
|
||||
const { doubleClickTreeNodeData, workSpaceTabType, name, callback, ddl } = params;
|
||||
const { extraParams } = doubleClickTreeNodeData;
|
||||
const { databaseName, schemaName, dataSourceId, dataSourceName, databaseType } = extraParams || {};
|
||||
let newConsole: any = {
|
||||
name,
|
||||
type: databaseType!,
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName,
|
||||
schemaName: schemaName,
|
||||
dataSourceName: dataSourceName!,
|
||||
status: ConsoleStatus.DRAFT,
|
||||
operationType: workSpaceTabType,
|
||||
ddl: ddl || '',
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
};
|
||||
historyService.saveConsole(newConsole).then(res => {
|
||||
const newList = [...workspaceTabList, {
|
||||
id: res,
|
||||
title: newConsole.name,
|
||||
type: workSpaceTabType,
|
||||
uniqueData: newConsole
|
||||
}]
|
||||
setWorkspaceTabList(newList)
|
||||
callback?.(res, newList);
|
||||
setActiveConsoleId(res);
|
||||
});
|
||||
}
|
||||
|
||||
function getConsoleList(callback?: Function) {
|
||||
let p: any = {
|
||||
pageNo: 1,
|
||||
pageSize: 999,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
...curWorkspaceParams,
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: p,
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setOpenConsoleList',
|
||||
payload: res.data,
|
||||
});
|
||||
callback?.();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// 切换tab
|
||||
function onTabChange(key: string | number | undefined) {
|
||||
setActiveConsoleId(key);
|
||||
}
|
||||
|
||||
// 删除 新增tab
|
||||
const onEdit = (action: 'add' | 'remove', data: ITabItem) => {
|
||||
if (action === 'remove') {
|
||||
setWorkspaceTabList(workspaceTabList.filter(t => t.id !== data.key));
|
||||
const editData = workspaceTabList?.find(t => t.id === data.key);
|
||||
if (editData?.type !== WorkspaceTabType.EditTable) {
|
||||
closeWindowTab(data.key as number);
|
||||
}
|
||||
}
|
||||
if (action === 'add') {
|
||||
addConsole();
|
||||
}
|
||||
};
|
||||
|
||||
const addConsole = () => {
|
||||
const { dataSourceId, databaseName, schemaName, databaseType } = curWorkspaceParams;
|
||||
let newConsole = {
|
||||
name: `new console`,
|
||||
ddl: '',
|
||||
dataSourceId: dataSourceId!,
|
||||
databaseName: databaseName!,
|
||||
schemaName: schemaName!,
|
||||
type: databaseType,
|
||||
status: ConsoleStatus.DRAFT,
|
||||
tabOpened: ConsoleOpenedStatus.IS_OPEN,
|
||||
operationType: WorkspaceTabType.CONSOLE,
|
||||
};
|
||||
historyService.saveConsole(newConsole).then((res) => {
|
||||
const newList = [...workspaceTabList, {
|
||||
id: res,
|
||||
title: newConsole.name,
|
||||
type: newConsole.operationType,
|
||||
uniqueData: newConsole
|
||||
}]
|
||||
setWorkspaceTabList(newList)
|
||||
setActiveConsoleId(res);
|
||||
});
|
||||
};
|
||||
|
||||
const closeWindowTab = (key: number) => {
|
||||
let p: any = {
|
||||
id: key,
|
||||
tabOpened: 'n',
|
||||
};
|
||||
// 这行干嘛的?TODO:
|
||||
// const window = openConsoleList?.find((t) => t.id === key);
|
||||
// if (!window?.status) {
|
||||
// return;
|
||||
// }
|
||||
historyService.updateSavedConsole(p).then(() => {
|
||||
handleLocalStorageSavedConsole(p.id, 'delete');
|
||||
});
|
||||
};
|
||||
|
||||
function renderEmpty() {
|
||||
return <div className={styles.ears}><ShortcutKey /></div>;
|
||||
}
|
||||
|
||||
function editableNameOnBlur(t: ITabItem) {
|
||||
let p: any = {
|
||||
id: t.key,
|
||||
name: t.label
|
||||
}
|
||||
historyService.updateSavedConsole(p).then(() => {
|
||||
getConsoleList();
|
||||
dispatch({
|
||||
type: 'workspace/fetchGetSavedConsole',
|
||||
payload: {
|
||||
pageNo: 1,
|
||||
pageSize: 999,
|
||||
orderByDesc: true,
|
||||
status: ConsoleStatus.RELEASE,
|
||||
...curWorkspaceParams,
|
||||
},
|
||||
callback: (res: any) => {
|
||||
dispatch({
|
||||
type: 'workspace/setConsoleList',
|
||||
payload: res.data,
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
const tabsList = useMemo(() => {
|
||||
return workspaceTabList.map(t => {
|
||||
const { uniqueData } = t;
|
||||
return {
|
||||
prefixIcon: workspaceTabConfig[t.type]?.icon,
|
||||
label: t.title,
|
||||
key: t.id,
|
||||
// 这里还缺一个参数 是否可编辑tab名称, 编辑表不可编辑名称 TODO:
|
||||
children: <>
|
||||
{
|
||||
[WorkspaceTabType.CONSOLE, WorkspaceTabType.FUNCTION, WorkspaceTabType.PROCEDURE, WorkspaceTabType.TRIGGER, WorkspaceTabType.VIEW].includes(t.type) && <SQLExecute
|
||||
isActive={activeConsoleId === t.id}
|
||||
data={{
|
||||
initDDL: uniqueData?.ddl,
|
||||
databaseName: curWorkspaceParams.databaseName!,
|
||||
dataSourceId: curWorkspaceParams.dataSourceId!,
|
||||
type: curWorkspaceParams.databaseType!,
|
||||
schemaName: curWorkspaceParams?.schemaName!,
|
||||
consoleId: t.id as number,
|
||||
consoleName: uniqueData.name,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
{
|
||||
t.type === WorkspaceTabType.EditTable && <DatabaseTableEditor
|
||||
dataSourceId={curWorkspaceParams.dataSourceId}
|
||||
databaseName={curWorkspaceParams.databaseName!}
|
||||
schemaName={curWorkspaceParams?.schemaName!}
|
||||
tableName={uniqueData.tableName}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
};
|
||||
})
|
||||
}, [workspaceTabList, activeConsoleId, curWorkspaceParams])
|
||||
|
||||
return (
|
||||
<div className={classnames(styles.workspaceRight, className)}>
|
||||
<LoadingContent data={workspaceTabList} handleEmpty empty={renderEmpty()}>
|
||||
<div className={styles.tabBox}>
|
||||
<TabsNew
|
||||
className={styles.tabs}
|
||||
onChange={onTabChange}
|
||||
onEdit={onEdit as any}
|
||||
editableName={true}
|
||||
activeKey={activeConsoleId}
|
||||
editableNameOnBlur={editableNameOnBlur}
|
||||
items={tabsList}
|
||||
/>
|
||||
</div>
|
||||
</LoadingContent>
|
||||
</div >
|
||||
);
|
||||
});
|
||||
|
||||
const dvaModel = connect(({ workspace, ai }: { workspace: IWorkspaceModelType; ai: IAIState }) => ({
|
||||
workspaceModel: workspace,
|
||||
aiModel: ai,
|
||||
}));
|
||||
|
||||
export default dvaModel(WorkspaceRight);
|
@ -3,7 +3,7 @@ import { connect } from 'umi';
|
||||
import styles from './index.less';
|
||||
import DraggableContainer from '@/components/DraggableContainer';
|
||||
import WorkspaceLeft from './components/WorkspaceLeft';
|
||||
import WorkspaceRight from './components/WorkspaceRight';
|
||||
import WorkspaceRightNew from './components/WorkspaceRightNew';
|
||||
import WorkspaceHeader from './components/WorkspaceHeader';
|
||||
import { IConnectionModelType } from '@/models/connection';
|
||||
import { IWorkspaceModelType } from '@/models/workspace';
|
||||
@ -27,20 +27,13 @@ const dvaModel = connect(
|
||||
}),
|
||||
);
|
||||
|
||||
interface Option {
|
||||
value: string;
|
||||
label: string;
|
||||
children?: Option[];
|
||||
}
|
||||
|
||||
const workspace = memo<IProps>((props) => {
|
||||
const workspacePage = memo<IProps>((props) => {
|
||||
const draggableRef = useRef<any>();
|
||||
const { workspaceModel, connectionModel, dispatch, pageLoading } = props;
|
||||
const { curConnection } = connectionModel;
|
||||
const { curWorkspaceParams } = workspaceModel;
|
||||
const [loading, setLoading] = useState(true);
|
||||
const isReady = curWorkspaceParams?.dataSourceId && ((curWorkspaceParams?.databaseName || curWorkspaceParams?.schemaName) || (curWorkspaceParams?.databaseName === null && curWorkspaceParams?.schemaName == null))
|
||||
|
||||
useEffect(() => {
|
||||
if (pageLoading === true) {
|
||||
setLoading(true);
|
||||
@ -107,7 +100,7 @@ const workspace = memo<IProps>((props) => {
|
||||
<WorkspaceLeft />
|
||||
</div>
|
||||
<div className={styles.boxRight}>
|
||||
<WorkspaceRight />
|
||||
<WorkspaceRightNew />
|
||||
</div>
|
||||
</DraggableContainer>
|
||||
</LoadingContent >
|
||||
@ -115,4 +108,4 @@ const workspace = memo<IProps>((props) => {
|
||||
);
|
||||
});
|
||||
|
||||
export default dvaModel(workspace)
|
||||
export default dvaModel(workspacePage)
|
@ -1,5 +1,5 @@
|
||||
import createRequest from './base';
|
||||
import { IPageResponse, IPageParams, IUniversalTableParams, IManageResultData, IRoutines } from '@/typings';
|
||||
import { IPageResponse, IPageParams, IUniversalTableParams, IManageResultData, IRoutines, IDatabaseFieldType, IEditTableInfo } from '@/typings';
|
||||
import { DatabaseTypeCode } from '@/constants';
|
||||
import { ExportSizeEnum, ExportTypeEnum } from '@/typings/resultTable';
|
||||
|
||||
@ -174,12 +174,43 @@ const getProcedureDetail = createRequest<{
|
||||
}, { procedureBody: string }>('/api/rdb/procedure/detail', { method: 'get' });
|
||||
|
||||
/** 格式化sql */
|
||||
const sqlFormat = createRequest<{
|
||||
const sqlFormat = createRequest<{
|
||||
sql: string;
|
||||
dbType: DatabaseTypeCode;
|
||||
}, string>('/api/sql/format', { method: 'get' });
|
||||
|
||||
/** 数据库支持的数据类型 */
|
||||
const getDatabaseFieldTypeList = createRequest<{
|
||||
dataSourceId: number;
|
||||
databaseName: string;
|
||||
}, IDatabaseFieldType[]>('/api/rdb/table/type_list', { method: 'get' });
|
||||
|
||||
/** 数据库支持的数据类型 */
|
||||
const getTableDetails = createRequest<{
|
||||
dataSourceId: number;
|
||||
databaseName: string;
|
||||
schemaName?: string;
|
||||
tableName: string;
|
||||
refresh: boolean;
|
||||
}, IEditTableInfo>('/api/rdb/table/query', { method: 'get' });
|
||||
|
||||
export interface IModifyTableSqlParams {
|
||||
dataSourceId: number;
|
||||
databaseName: string;
|
||||
schemaName?: string;
|
||||
tableName?: string;
|
||||
oldTable?: string;
|
||||
newTable: string;
|
||||
refresh: boolean;
|
||||
}
|
||||
|
||||
/** 数据库支持的数据类型 */
|
||||
const getModifyTableSql = createRequest<IModifyTableSqlParams, string>('/api/rdb/table/modify/sql', { method: 'post' });
|
||||
|
||||
export default {
|
||||
getModifyTableSql,
|
||||
getTableDetails,
|
||||
getDatabaseFieldTypeList,
|
||||
sqlFormat,
|
||||
getTriggerDetail,
|
||||
getProcedureDetail,
|
||||
@ -208,3 +239,5 @@ export default {
|
||||
getDMLCount,
|
||||
// exportResultTable
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,19 +1,34 @@
|
||||
:root {
|
||||
:global {
|
||||
// There is some animation when switching the theme color causing a delay in switching background
|
||||
.ant-input,
|
||||
.ant-input-password {
|
||||
transition: all 0.2s, background-color 0s;
|
||||
}
|
||||
.ant-btn {
|
||||
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1), background-color 0s;
|
||||
}
|
||||
|
||||
.ant-select-single:not(.ant-select-customize-input) .ant-select-selector {
|
||||
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1), background-color 0s;
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
border-bottom: 0px;
|
||||
}
|
||||
.ant-modal-content {
|
||||
background-color: var(--color-bg-elevated) !important;
|
||||
|
||||
.ant-modal-confirm-title {
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
// .ant-modal-content {
|
||||
// background-color: var(--color-bg-elevated) !important;
|
||||
|
||||
// .ant-modal-confirm-title {
|
||||
// color: var(--color-text) !important;
|
||||
// }
|
||||
|
||||
// .ant-modal-confirm-content {
|
||||
// color: var(--color-text) !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
.ant-modal-confirm-content {
|
||||
color: var(--color-text) !important;
|
||||
}
|
||||
}
|
||||
.ant-modal-footer {
|
||||
border-top: 0px;
|
||||
padding: 8px;
|
||||
@ -42,5 +57,10 @@
|
||||
// top: 10px !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
.ant-cascader-dropdown .ant-cascader-menu {
|
||||
height: auto;
|
||||
max-height: 80vh;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,29 +17,29 @@ input:-webkit-autofill {
|
||||
caret-color: var(--color-text); // 光标的颜色
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
// ::-webkit-scrollbar {
|
||||
// width: 6px;
|
||||
// height: 6px;
|
||||
// }
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: var(--color-fill);
|
||||
border-radius: 3px;
|
||||
}
|
||||
// ::-webkit-scrollbar-thumb {
|
||||
// background-color: var(--color-fill);
|
||||
// border-radius: 3px;
|
||||
// }
|
||||
|
||||
::-webkit-scrollbar-button {
|
||||
width: 0;
|
||||
height: 0;
|
||||
background: transparent;
|
||||
}
|
||||
// ::-webkit-scrollbar-button {
|
||||
// width: 0;
|
||||
// height: 0;
|
||||
// background: transparent;
|
||||
// }
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
// ::-webkit-scrollbar-track {
|
||||
// background: transparent;
|
||||
// }
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
// ::-webkit-scrollbar-corner {
|
||||
// background: transparent;
|
||||
// }
|
||||
|
||||
html,
|
||||
body,
|
||||
@ -133,9 +133,8 @@ li {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
// 覆盖antd 的一些样式
|
||||
|
||||
button{
|
||||
button {
|
||||
box-shadow: none !important;
|
||||
}
|
@ -305,7 +305,7 @@ html[primary-color='polar-blue'] {
|
||||
--lime-9: #e4f88b;
|
||||
--lime9: #e4f88b;
|
||||
--lime-10: #f0fab5;
|
||||
--lime10: #f0fab5;
|
||||
--lime10: #1c2128;
|
||||
--color-text: rgba(255, 255, 255, 0.85);
|
||||
--color-text-secondary: rgba(255, 255, 255, 0.65);
|
||||
--color-text-tertiary: rgba(255, 255, 255, 0.45);
|
||||
|
@ -44,7 +44,8 @@ const antDarkTheme = {
|
||||
colorBgBase: '#0a0b0c',
|
||||
colorHoverBg: 'hsla(0, 0%, 100%, 0.03)',
|
||||
colorBgContainer: '#0a0b0c',
|
||||
colorBgElevated: '#131418',
|
||||
colorBgSubtle: '#131418',
|
||||
colorBgElevated: '#0a0b0c',
|
||||
colorBorder: 'rgba(54, 55, 58,0.4)',
|
||||
colorBorderSecondary: 'rgba(54, 55, 58,0.4)',
|
||||
},
|
||||
|
@ -41,13 +41,14 @@ const antdLightTheme = {
|
||||
token: {
|
||||
...commonToken,
|
||||
colorTextBase: 'rgb(241, 241, 244)',
|
||||
colorBgBase: 'rgb(28, 33, 40)',
|
||||
colorBgBase: '#1c2128',
|
||||
colorHoverBg: 'hsla(0, 0%, 100%, 0.03)',
|
||||
colorBgContainer: 'rgb(28, 33, 40)',
|
||||
colorBgElevated: 'rgb(34, 39, 46)',
|
||||
colorBgContainer: '#1c2128',
|
||||
colorBgSubtle: '#22272e',
|
||||
colorBgElevated: '#1c2128',
|
||||
colorBorder: 'rgba(55, 62, 71, 0.4)',
|
||||
colorBorderSecondary: 'rgba(55, 62, 71, 0.4)',
|
||||
controlItemBgActive: 'rgba(241, 241, 244, 0.08);',
|
||||
controlItemBgActive: 'rgba(241, 241, 244, 0.08)',
|
||||
// ...commonToken,
|
||||
// colorText: "rgb(241, 241, 244)",
|
||||
// colorBgBase: '#191a23',
|
||||
|
@ -44,7 +44,8 @@ const antdLightTheme = {
|
||||
colorBgBase: '#fff',
|
||||
colorHoverBg: 'rgba(0, 0, 0, 0.03)',
|
||||
colorBgContainer: '#fff',
|
||||
colorBgElevated: '#f6f8fa',
|
||||
colorBgSubtle: '#f6f8fa',
|
||||
colorBgElevated: '#fff',
|
||||
colorBorder: 'rgba(211, 211, 212, 0.4)',
|
||||
colorBorderSecondary: 'rgba(211, 211, 212, 0.4)',
|
||||
},
|
||||
|
@ -1,3 +1,3 @@
|
||||
html[theme='light'] {
|
||||
--custom-color-icon: #383D4B;
|
||||
--custom-color-icon: #383d4b;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { ConsoleOpenedStatus, ConsoleStatus, DatabaseTypeCode, OperationType } from '@/constants';
|
||||
import { ConsoleOpenedStatus, ConsoleStatus, DatabaseTypeCode, WorkspaceTabType } from '@/constants';
|
||||
|
||||
export interface IPageResponse<T> {
|
||||
data: T[];
|
||||
@ -27,7 +27,7 @@ export interface IConsole {
|
||||
status: ConsoleStatus; // 控制台状态
|
||||
connectable: boolean; // 是否可连接
|
||||
tabOpened?: ConsoleOpenedStatus; // 控制台tab是否打开
|
||||
operationType: OperationType; // 操作类型
|
||||
operationType: WorkspaceTabType; // 操作类型
|
||||
}
|
||||
|
||||
export interface Option {
|
||||
|
@ -34,3 +34,8 @@ export interface IResultConfig {
|
||||
total: number | string;
|
||||
hasNextPage: boolean;
|
||||
}
|
||||
|
||||
/** 不同数据库支持的列字段类型*/
|
||||
export interface IDatabaseFieldType {
|
||||
typeName: string;
|
||||
}
|
||||
|
60
chat2db-client/src/typings/editTable.ts
Normal file
60
chat2db-client/src/typings/editTable.ts
Normal file
@ -0,0 +1,60 @@
|
||||
import { IndexesType } from '@/constants';
|
||||
|
||||
// 编辑表时表的基础数据
|
||||
export interface IBaseInfo {
|
||||
name: string;
|
||||
comment?: string;
|
||||
}
|
||||
|
||||
// 编辑表时列的数据结构
|
||||
export interface IColumnItem {
|
||||
key?: string; // 列的key 前端自己给的
|
||||
name: string | null; // 列名
|
||||
columnType: string | null; // 列的类型 比如 varchar(100) ,double(10,6)
|
||||
columnSize: number | null; // 列的长度
|
||||
nullable: number | null; // 是否为空
|
||||
primaryKey: boolean | null; // 是否主键
|
||||
defaultValue: string | null; // 默认值
|
||||
dataType: string | null; // 数据类型
|
||||
autoIncrement: boolean | null; // 是否自增
|
||||
numericPrecision: number | null; // 数字精度
|
||||
numericScale: number | null; // 数字比例
|
||||
characterMaximumLength: number | null; // 字符串最大长度
|
||||
comment: string | null; // 注释
|
||||
}
|
||||
|
||||
export interface IIndexIncludeColumnItem {
|
||||
key?: string; // 列的key 前端自己给的
|
||||
ascOrDesc: string | null; // 升序还是降序
|
||||
cardinality: number | null; // 基数
|
||||
collation: string | null; // 排序规则
|
||||
columnName: string | null; // 列名
|
||||
comment: string | null; // 注释
|
||||
databaseName: string | null; // 数据库名
|
||||
filterCondition: string | null; // 过滤条件
|
||||
indexName: string | null; // 索引名
|
||||
indexQualifier: string | null; // 索引限定符
|
||||
nonUnique: boolean | null; // 是否唯一
|
||||
ordinalPosition: number | null; // 位置
|
||||
schemaName: string | null; // 模式名
|
||||
tableName: string | null; // 表名
|
||||
type: string | null; // 类型
|
||||
pages: number | null; // 页数
|
||||
prefixLength: number | null; //
|
||||
}
|
||||
|
||||
// 编辑表时索引的数据结构
|
||||
export interface IIndexItem {
|
||||
key?: string;
|
||||
name: string | null;
|
||||
columns: string | null;
|
||||
comment?: string | null;
|
||||
type: IndexesType | null;
|
||||
columnList: IIndexIncludeColumnItem[];
|
||||
}
|
||||
|
||||
// 编辑表时整体的数据结构
|
||||
export interface IEditTableInfo extends IBaseInfo {
|
||||
columnList: IColumnItem[];
|
||||
indexList: IIndexItem[];
|
||||
}
|
@ -5,5 +5,7 @@ export * from './database';
|
||||
export * from './main';
|
||||
export * from './theme';
|
||||
export * from './tree';
|
||||
export * from './setting'
|
||||
export * from './team'
|
||||
export * from './setting';
|
||||
export * from './team';
|
||||
export * from './workspace';
|
||||
export * from './editTable';
|
||||
|
17
chat2db-client/src/typings/workspace.ts
Normal file
17
chat2db-client/src/typings/workspace.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { CreateTabIntroType, WorkspaceTabType } from '@/constants';
|
||||
import { ITreeNode } from '@/typings';
|
||||
|
||||
|
||||
export interface ICreateTabIntro {
|
||||
type: CreateTabIntroType;
|
||||
workspaceTabType: WorkspaceTabType;
|
||||
treeNodeData: ITreeNode;
|
||||
}
|
||||
|
||||
export interface IWorkspaceTab {
|
||||
id: number | string; // Tab的id
|
||||
type: WorkspaceTabType; // 工作区tab的类型
|
||||
title: string; // 工作区tab的名称
|
||||
uniqueData?: any;
|
||||
}
|
||||
|
@ -1532,6 +1532,14 @@
|
||||
"@dnd-kit/utilities" "^3.2.1"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/modifiers@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/@dnd-kit/modifiers/-/modifiers-6.0.1.tgz#9e39b25fd6e323659604cc74488fe044d33188c8"
|
||||
integrity sha512-rbxcsg3HhzlcMHVHWDuh9LCjpOVAgqbV78wLGI8tziXY3+qcMQ61qVXIvNKQFuhj75dSfD+o+PYZQ/NUk2A23A==
|
||||
dependencies:
|
||||
"@dnd-kit/utilities" "^3.2.1"
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@dnd-kit/sortable@^7.0.2":
|
||||
version "7.0.2"
|
||||
resolved "https://registry.npmmirror.com/@dnd-kit/sortable/-/sortable-7.0.2.tgz#791d550872457f3f3c843e00d159b640f982011c"
|
||||
@ -1547,21 +1555,6 @@
|
||||
dependencies:
|
||||
tslib "^2.0.0"
|
||||
|
||||
"@electron/get@^2.0.0":
|
||||
version "2.0.2"
|
||||
resolved "https://registry.npmmirror.com/@electron/get/-/get-2.0.2.tgz#ae2a967b22075e9c25aaf00d5941cd79c21efd7e"
|
||||
integrity sha512-eFZVFoRXb3GFGd7Ak7W4+6jBl9wBtiZ4AaYOse97ej6mKj5tkyO0dUnUChs1IhJZtx1BENo4/p4WUTXpi6vT+g==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
env-paths "^2.2.0"
|
||||
fs-extra "^8.1.0"
|
||||
got "^11.8.5"
|
||||
progress "^2.0.3"
|
||||
semver "^6.2.0"
|
||||
sumchecker "^3.0.1"
|
||||
optionalDependencies:
|
||||
global-agent "^3.0.0"
|
||||
|
||||
"@electron/universal@1.2.1":
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmmirror.com/@electron/universal/-/universal-1.2.1.tgz#3c2c4ff37063a4e9ab1e6ff57db0bc619bc82339"
|
||||
@ -2108,11 +2101,6 @@
|
||||
resolved "https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e"
|
||||
integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==
|
||||
|
||||
"@sindresorhus/is@^4.0.0":
|
||||
version "4.6.0"
|
||||
resolved "https://registry.npmmirror.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
|
||||
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
|
||||
|
||||
"@stylelint/postcss-css-in-js@^0.38.0":
|
||||
version "0.38.0"
|
||||
resolved "https://registry.npmmirror.com/@stylelint/postcss-css-in-js/-/postcss-css-in-js-0.38.0.tgz#eabb061df932744db766f11a153ae1c465b6263c"
|
||||
@ -2212,13 +2200,6 @@
|
||||
deepmerge "^4.2.2"
|
||||
svgo "^2.8.0"
|
||||
|
||||
"@szmarczak/http-timer@^4.0.5":
|
||||
version "4.0.6"
|
||||
resolved "https://registry.npmmirror.com/@szmarczak/http-timer/-/http-timer-4.0.6.tgz#b4a914bb62e7c272d4e5989fe4440f812ab1d807"
|
||||
integrity sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==
|
||||
dependencies:
|
||||
defer-to-connect "^2.0.0"
|
||||
|
||||
"@tanstack/match-sorter-utils@^8.7.0":
|
||||
version "8.8.4"
|
||||
resolved "https://registry.npmmirror.com/@tanstack/match-sorter-utils/-/match-sorter-utils-8.8.4.tgz#0b2864d8b7bac06a9f84cb903d405852cc40a457"
|
||||
@ -2291,16 +2272,6 @@
|
||||
dependencies:
|
||||
"@babel/types" "^7.20.7"
|
||||
|
||||
"@types/cacheable-request@^6.0.1":
|
||||
version "6.0.3"
|
||||
resolved "https://registry.npmmirror.com/@types/cacheable-request/-/cacheable-request-6.0.3.tgz#a430b3260466ca7b5ca5bfd735693b36e7a9d183"
|
||||
integrity sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==
|
||||
dependencies:
|
||||
"@types/http-cache-semantics" "*"
|
||||
"@types/keyv" "^3.1.4"
|
||||
"@types/node" "*"
|
||||
"@types/responselike" "^1.0.0"
|
||||
|
||||
"@types/classnames@^2.2.9":
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmmirror.com/@types/classnames/-/classnames-2.3.1.tgz#3c2467aa0f1a93f1f021e3b9bcf938bd5dfdc0dd"
|
||||
@ -2360,11 +2331,6 @@
|
||||
resolved "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
|
||||
integrity sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==
|
||||
|
||||
"@types/http-cache-semantics@*":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmmirror.com/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz#0ea7b61496902b95890dc4c3a116b60cb8dae812"
|
||||
integrity sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==
|
||||
|
||||
"@types/invariant@^2.2.31":
|
||||
version "2.2.35"
|
||||
resolved "https://registry.npmmirror.com/@types/invariant/-/invariant-2.2.35.tgz#cd3ebf581a6557452735688d8daba6cf0bd5a3be"
|
||||
@ -2399,13 +2365,6 @@
|
||||
resolved "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb"
|
||||
integrity sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==
|
||||
|
||||
"@types/keyv@^3.1.4":
|
||||
version "3.1.4"
|
||||
resolved "https://registry.npmmirror.com/@types/keyv/-/keyv-3.1.4.tgz#3ccdb1c6751b0c7e52300bcdacd5bcbf8faa75b6"
|
||||
integrity sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@^4.14.195":
|
||||
version "4.14.195"
|
||||
resolved "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.195.tgz#bafc975b252eb6cea78882ce8a7b6bf22a6de632"
|
||||
@ -2426,11 +2385,6 @@
|
||||
resolved "https://registry.npmmirror.com/@types/node/-/node-20.4.2.tgz#129cc9ae69f93824f92fac653eebfb4812ab4af9"
|
||||
integrity sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==
|
||||
|
||||
"@types/node@^16.11.26":
|
||||
version "16.18.39"
|
||||
resolved "https://registry.npmmirror.com/@types/node/-/node-16.18.39.tgz#aa39a1a87a40ef6098ee69689a1acb0c1b034832"
|
||||
integrity sha512-8q9ZexmdYYyc5/cfujaXb4YOucpQxAV4RMG0himLyDUOEr8Mr79VrqsFI+cQ2M2h89YIuy95lbxuYjxT4Hk4kQ==
|
||||
|
||||
"@types/parse-json@^4.0.0":
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
@ -2465,13 +2419,6 @@
|
||||
"@types/scheduler" "*"
|
||||
csstype "^3.0.2"
|
||||
|
||||
"@types/responselike@^1.0.0":
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29"
|
||||
integrity sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.3"
|
||||
resolved "https://registry.npmmirror.com/@types/scheduler/-/scheduler-0.16.3.tgz#cef09e3ec9af1d63d2a6cc5b383a737e24e6dcf5"
|
||||
@ -2521,13 +2468,6 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.10.0"
|
||||
resolved "https://registry.npmmirror.com/@types/yauzl/-/yauzl-2.10.0.tgz#b3248295276cf8c6f153ebe6a9aba0c988cb2599"
|
||||
integrity sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@5.48.1":
|
||||
version "5.48.1"
|
||||
resolved "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.48.1.tgz#deee67e399f2cb6b4608c935777110e509d8018c"
|
||||
@ -3592,11 +3532,6 @@ boolbase@^1.0.0:
|
||||
resolved "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e"
|
||||
integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==
|
||||
|
||||
boolean@^3.0.1:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmmirror.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b"
|
||||
integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==
|
||||
|
||||
bplist-parser@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmmirror.com/bplist-parser/-/bplist-parser-0.2.0.tgz#43a9d183e5bf9d545200ceac3e712f79ebbe8d0e"
|
||||
@ -3722,11 +3657,6 @@ buffer-alloc@^1.2.0:
|
||||
buffer-alloc-unsafe "^1.1.0"
|
||||
buffer-fill "^1.0.0"
|
||||
|
||||
buffer-crc32@~0.2.3:
|
||||
version "0.2.13"
|
||||
resolved "https://registry.npmmirror.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
|
||||
integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==
|
||||
|
||||
buffer-equal@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe"
|
||||
@ -3807,24 +3737,6 @@ bundle-name@^3.0.0:
|
||||
dependencies:
|
||||
run-applescript "^5.0.0"
|
||||
|
||||
cacheable-lookup@^5.0.3:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.npmmirror.com/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz#5a6b865b2c44357be3d5ebc2a467b032719a7005"
|
||||
integrity sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==
|
||||
|
||||
cacheable-request@^7.0.2:
|
||||
version "7.0.4"
|
||||
resolved "https://registry.npmmirror.com/cacheable-request/-/cacheable-request-7.0.4.tgz#7a33ebf08613178b403635be7b899d3e69bbe817"
|
||||
integrity sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==
|
||||
dependencies:
|
||||
clone-response "^1.0.2"
|
||||
get-stream "^5.1.0"
|
||||
http-cache-semantics "^4.0.0"
|
||||
keyv "^4.0.0"
|
||||
lowercase-keys "^2.0.0"
|
||||
normalize-url "^6.0.1"
|
||||
responselike "^2.0.0"
|
||||
|
||||
call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
@ -3969,13 +3881,6 @@ cliui@^8.0.1:
|
||||
strip-ansi "^6.0.1"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clone-response@^1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmmirror.com/clone-response/-/clone-response-1.0.3.tgz#af2032aa47816399cf5f0a1d0db902f517abb8c3"
|
||||
integrity sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
@ -4386,13 +4291,6 @@ decode-uri-component@^0.2.0:
|
||||
resolved "https://registry.npmmirror.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
|
||||
integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
|
||||
|
||||
decompress-response@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.npmmirror.com/decompress-response/-/decompress-response-6.0.0.tgz#ca387612ddb7e104bd16d85aab00d5ecf09c66fc"
|
||||
integrity sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==
|
||||
dependencies:
|
||||
mimic-response "^3.1.0"
|
||||
|
||||
deepmerge@^4.2.2:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a"
|
||||
@ -4416,11 +4314,6 @@ default-browser@^4.0.0:
|
||||
execa "^7.1.1"
|
||||
titleize "^3.0.0"
|
||||
|
||||
defer-to-connect@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587"
|
||||
integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==
|
||||
|
||||
define-lazy-prop@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
|
||||
@ -4767,15 +4660,6 @@ electron-to-chromium@^1.4.431:
|
||||
resolved "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.4.464.tgz#2f94bad78dff34e527aacbfc5d0b1a33cf046507"
|
||||
integrity sha512-guZ84yoou4+ILNdj0XEbmGs6DEWj6zpVOWYpY09GU66yEb0DSYvP/biBPzHn0GuW/3RC/pnaYNUWlQE1fJYtgA==
|
||||
|
||||
electron@^22.3.0:
|
||||
version "22.3.18"
|
||||
resolved "https://registry.npmmirror.com/electron/-/electron-22.3.18.tgz#5ee55633b3912fec9df6d8f039acf2c016274cfc"
|
||||
integrity sha512-JgjB966ghTBszAX/GgVgDY/2CktWCjTZWGJI0WISRHDudBZ8/WPkI/hIjsMiLQLe0wSTk6S+WHOYbIqyw0I/sg==
|
||||
dependencies:
|
||||
"@electron/get" "^2.0.0"
|
||||
"@types/node" "^16.11.26"
|
||||
extract-zip "^2.0.1"
|
||||
|
||||
elliptic@^6.5.3:
|
||||
version "6.5.4"
|
||||
resolved "https://registry.npmmirror.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb"
|
||||
@ -4806,7 +4690,7 @@ encoding@^0.1.11:
|
||||
dependencies:
|
||||
iconv-lite "^0.6.2"
|
||||
|
||||
end-of-stream@^1.1.0, end-of-stream@^1.4.1:
|
||||
end-of-stream@^1.4.1:
|
||||
version "1.4.4"
|
||||
resolved "https://registry.npmmirror.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
|
||||
integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
|
||||
@ -4831,11 +4715,6 @@ entities@^4.4.0:
|
||||
resolved "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
|
||||
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
|
||||
|
||||
env-paths@^2.2.0:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmmirror.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2"
|
||||
integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==
|
||||
|
||||
errno@^0.1.1:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"
|
||||
@ -4956,11 +4835,6 @@ es5-imcompatible-versions@^0.1.78:
|
||||
resolved "https://registry.npmmirror.com/es5-imcompatible-versions/-/es5-imcompatible-versions-0.1.86.tgz#e9583ad3a7a93c1b13835fb804a3bbd05e30a662"
|
||||
integrity sha512-Lbrsn5bCL4iVMBdundiFVNIKlnnoBiIMrjtLRe1Snt92s60WHotw83S2ijp5ioqe6pDil3iBPY634VDwBcb1rg==
|
||||
|
||||
es6-error@^4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmmirror.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d"
|
||||
integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==
|
||||
|
||||
es6-iterator@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmmirror.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7"
|
||||
@ -5016,11 +4890,6 @@ escape-string-regexp@^1.0.5:
|
||||
resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
||||
|
||||
escape-string-regexp@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
eslint-plugin-jest@27.2.1:
|
||||
version "27.2.1"
|
||||
resolved "https://registry.npmmirror.com/eslint-plugin-jest/-/eslint-plugin-jest-27.2.1.tgz#b85b4adf41c682ea29f1f01c8b11ccc39b5c672c"
|
||||
@ -5169,17 +5038,6 @@ ext@^1.1.2:
|
||||
dependencies:
|
||||
type "^2.7.2"
|
||||
|
||||
extract-zip@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
|
||||
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
get-stream "^5.1.0"
|
||||
yauzl "^2.10.0"
|
||||
optionalDependencies:
|
||||
"@types/yauzl" "^2.9.1"
|
||||
|
||||
extsprintf@^1.2.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmmirror.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07"
|
||||
@ -5247,13 +5105,6 @@ fb-watchman@^2.0.0:
|
||||
dependencies:
|
||||
bser "2.1.1"
|
||||
|
||||
fd-slicer@~1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmmirror.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
|
||||
integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==
|
||||
dependencies:
|
||||
pend "~1.2.0"
|
||||
|
||||
fetch-blob@^3.1.2, fetch-blob@^3.1.4:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmmirror.com/fetch-blob/-/fetch-blob-3.2.0.tgz#f09b8d4bbd45adc6f0c20b7e787e793e309dcce9"
|
||||
@ -5362,15 +5213,6 @@ fs-extra@^10.0.0, fs-extra@^10.1.0:
|
||||
jsonfile "^6.0.1"
|
||||
universalify "^2.0.0"
|
||||
|
||||
fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.0"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^9.0.0, fs-extra@^9.0.1:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
|
||||
@ -5458,13 +5300,6 @@ get-stdin@^9.0.0:
|
||||
resolved "https://registry.npmmirror.com/get-stdin/-/get-stdin-9.0.0.tgz#3983ff82e03d56f1b2ea0d3e60325f39d703a575"
|
||||
integrity sha512-dVKBjfWisLAicarI2Sf+JuBE/DghV4UzNAVe9yhEJuzeREd3JhOTE9cUaJTeSa77fsbQUK3pcOpJfM59+VKZaA==
|
||||
|
||||
get-stream@^5.1.0:
|
||||
version "5.2.0"
|
||||
resolved "https://registry.npmmirror.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
|
||||
integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==
|
||||
dependencies:
|
||||
pump "^3.0.0"
|
||||
|
||||
get-stream@^6.0.0, get-stream@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmmirror.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
|
||||
@ -5528,18 +5363,6 @@ glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
global-agent@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/global-agent/-/global-agent-3.0.0.tgz#ae7cd31bd3583b93c5a16437a1afe27cc33a1ab6"
|
||||
integrity sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==
|
||||
dependencies:
|
||||
boolean "^3.0.1"
|
||||
es6-error "^4.1.1"
|
||||
matcher "^3.0.0"
|
||||
roarr "^2.15.3"
|
||||
semver "^7.3.2"
|
||||
serialize-error "^7.0.1"
|
||||
|
||||
global@^4.3.2:
|
||||
version "4.4.0"
|
||||
resolved "https://registry.npmmirror.com/global/-/global-4.4.0.tgz#3e7b105179006a323ed71aafca3e9c57a5cc6406"
|
||||
@ -5553,7 +5376,7 @@ globals@^11.1.0:
|
||||
resolved "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e"
|
||||
integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==
|
||||
|
||||
globalthis@^1.0.1, globalthis@^1.0.3:
|
||||
globalthis@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmmirror.com/globalthis/-/globalthis-1.0.3.tgz#5852882a52b80dc301b0660273e1ed082f0b6ccf"
|
||||
integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==
|
||||
@ -5590,23 +5413,6 @@ gopd@^1.0.1:
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
|
||||
got@^11.8.5:
|
||||
version "11.8.6"
|
||||
resolved "https://registry.npmmirror.com/got/-/got-11.8.6.tgz#276e827ead8772eddbcfc97170590b841823233a"
|
||||
integrity sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^4.0.0"
|
||||
"@szmarczak/http-timer" "^4.0.5"
|
||||
"@types/cacheable-request" "^6.0.1"
|
||||
"@types/responselike" "^1.0.0"
|
||||
cacheable-lookup "^5.0.3"
|
||||
cacheable-request "^7.0.2"
|
||||
decompress-response "^6.0.0"
|
||||
http2-wrapper "^1.0.0-beta.5.2"
|
||||
lowercase-keys "^2.0.0"
|
||||
p-cancelable "^2.0.0"
|
||||
responselike "^2.0.0"
|
||||
|
||||
graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3"
|
||||
@ -5779,11 +5585,6 @@ htmlparser2@^6.1.0:
|
||||
domutils "^2.5.2"
|
||||
entities "^2.0.0"
|
||||
|
||||
http-cache-semantics@^4.0.0:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.npmmirror.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a"
|
||||
integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==
|
||||
|
||||
http-deceiver@^1.2.7:
|
||||
version "1.2.7"
|
||||
resolved "https://registry.npmmirror.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
|
||||
@ -5798,14 +5599,6 @@ http-proxy-agent@^5.0.0:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
http2-wrapper@^1.0.0-beta.5.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmmirror.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d"
|
||||
integrity sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==
|
||||
dependencies:
|
||||
quick-lru "^5.1.1"
|
||||
resolve-alpn "^1.0.0"
|
||||
|
||||
https-browserify@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
|
||||
@ -6438,11 +6231,6 @@ jsesc@~0.5.0:
|
||||
resolved "https://registry.npmmirror.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
|
||||
integrity sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==
|
||||
|
||||
json-buffer@3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13"
|
||||
integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==
|
||||
|
||||
json-parse-even-better-errors@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.npmmirror.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
||||
@ -6453,11 +6241,6 @@ json-schema-traverse@^0.4.1:
|
||||
resolved "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
|
||||
integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
|
||||
|
||||
json-stringify-safe@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmmirror.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb"
|
||||
integrity sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==
|
||||
|
||||
json2mq@^0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmmirror.com/json2mq/-/json2mq-0.2.0.tgz#b637bd3ba9eabe122c83e9720483aeb10d2c904a"
|
||||
@ -6470,13 +6253,6 @@ json5@^2.1.2, json5@^2.2.0, json5@^2.2.2:
|
||||
resolved "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283"
|
||||
integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==
|
||||
|
||||
jsonfile@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb"
|
||||
integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==
|
||||
optionalDependencies:
|
||||
graceful-fs "^4.1.6"
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
|
||||
@ -6506,13 +6282,6 @@ keyboardevents-areequal@^0.2.1:
|
||||
resolved "https://registry.npmmirror.com/keyboardevents-areequal/-/keyboardevents-areequal-0.2.2.tgz#88191ec738ce9f7591c25e9056de928b40277194"
|
||||
integrity sha512-Nv+Kr33T0mEjxR500q+I6IWisOQ0lK1GGOncV0kWE6n4KFmpcu7RUX5/2B0EUtX51Cb0HjZ9VJsSY3u4cBa0kw==
|
||||
|
||||
keyv@^4.0.0:
|
||||
version "4.5.3"
|
||||
resolved "https://registry.npmmirror.com/keyv/-/keyv-4.5.3.tgz#00873d2b046df737963157bd04f294ca818c9c25"
|
||||
integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==
|
||||
dependencies:
|
||||
json-buffer "3.0.1"
|
||||
|
||||
kolorist@^1.6.0:
|
||||
version "1.8.0"
|
||||
resolved "https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c"
|
||||
@ -6673,11 +6442,6 @@ lower-case@^2.0.2:
|
||||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
lowercase-keys@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479"
|
||||
integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==
|
||||
|
||||
lru-cache@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920"
|
||||
@ -6712,13 +6476,6 @@ markdown-it-link-attributes@^4.0.1:
|
||||
resolved "https://registry.npmmirror.com/markdown-it-link-attributes/-/markdown-it-link-attributes-4.0.1.tgz#25751f2cf74fd91f0a35ba7b3247fa45f2056d88"
|
||||
integrity sha512-pg5OK0jPLg62H4k7M9mRJLT61gUp9nvG0XveKYHMOOluASo9OEF13WlXrpAp2aj35LbedAy3QOCgQCw0tkLKAQ==
|
||||
|
||||
matcher@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca"
|
||||
integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==
|
||||
dependencies:
|
||||
escape-string-regexp "^4.0.0"
|
||||
|
||||
md5.js@^1.3.4:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.npmmirror.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||
@ -6798,16 +6555,6 @@ mimic-fn@^4.0.0:
|
||||
resolved "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-4.0.0.tgz#60a90550d5cb0b239cca65d893b1a53b29871ecc"
|
||||
integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==
|
||||
|
||||
mimic-response@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
|
||||
mimic-response@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.npmmirror.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9"
|
||||
integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==
|
||||
|
||||
min-document@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.npmmirror.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||
@ -7055,11 +6802,6 @@ normalize-range@^0.1.2:
|
||||
resolved "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
|
||||
integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==
|
||||
|
||||
normalize-url@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmmirror.com/normalize-url/-/normalize-url-6.1.0.tgz#40d0885b535deffe3f3147bec877d05fe4c5668a"
|
||||
integrity sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==
|
||||
|
||||
npm-run-path@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmmirror.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
|
||||
@ -7171,7 +6913,7 @@ on-exit-leak-free@^0.2.0:
|
||||
resolved "https://registry.npmmirror.com/on-exit-leak-free/-/on-exit-leak-free-0.2.0.tgz#b39c9e3bf7690d890f4861558b0d7b90a442d209"
|
||||
integrity sha512-dqaz3u44QbRXQooZLTUKU41ZrzYrcvLISVgbrzbyCMxpmSLJvZ3ZamIJIZ29P6OhZIkNIQKosdeM6t1LYbA9hg==
|
||||
|
||||
once@^1.3.0, once@^1.3.1, once@^1.4.0:
|
||||
once@^1.3.0, once@^1.4.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmmirror.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
|
||||
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
|
||||
@ -7216,11 +6958,6 @@ os-browserify@^0.3.0:
|
||||
resolved "https://registry.npmmirror.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27"
|
||||
integrity sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==
|
||||
|
||||
p-cancelable@^2.0.0:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmmirror.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf"
|
||||
integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==
|
||||
|
||||
p-limit@^2.2.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmmirror.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1"
|
||||
@ -7366,11 +7103,6 @@ pbkdf2@^3.0.3:
|
||||
safe-buffer "^5.0.1"
|
||||
sha.js "^2.4.8"
|
||||
|
||||
pend@~1.2.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmmirror.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50"
|
||||
integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==
|
||||
|
||||
picocolors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
@ -7824,11 +7556,6 @@ process@^0.11.10:
|
||||
resolved "https://registry.npmmirror.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
|
||||
integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==
|
||||
|
||||
progress@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
||||
prop-types@^15.5.10, prop-types@^15.7.2, prop-types@^15.8.1:
|
||||
version "15.8.1"
|
||||
resolved "https://registry.npmmirror.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
|
||||
@ -7860,14 +7587,6 @@ public-encrypt@^4.0.0:
|
||||
randombytes "^2.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
pump@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmmirror.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64"
|
||||
integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==
|
||||
dependencies:
|
||||
end-of-stream "^1.1.0"
|
||||
once "^1.3.1"
|
||||
|
||||
punycode@^1.2.4, punycode@^1.4.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmmirror.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
|
||||
@ -7925,11 +7644,6 @@ quick-format-unescaped@^4.0.3:
|
||||
resolved "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7"
|
||||
integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==
|
||||
|
||||
quick-lru@^5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.npmmirror.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932"
|
||||
integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==
|
||||
|
||||
railroad-diagrams@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz#eb7e6267548ddedfb899c1b90e57374559cddb7e"
|
||||
@ -8623,11 +8337,6 @@ resize-observer-polyfill@^1.5.1:
|
||||
resolved "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||
|
||||
resolve-alpn@^1.0.0:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.npmmirror.com/resolve-alpn/-/resolve-alpn-1.2.1.tgz#b7adbdac3546aaaec20b45e7d8265927072726f9"
|
||||
integrity sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==
|
||||
|
||||
resolve-from@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
|
||||
@ -8670,13 +8379,6 @@ resolve@^2.0.0-next.4:
|
||||
path-parse "^1.0.7"
|
||||
supports-preserve-symlinks-flag "^1.0.0"
|
||||
|
||||
responselike@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmmirror.com/responselike/-/responselike-2.0.1.tgz#9a0bc8fdc252f3fb1cca68b016591059ba1422bc"
|
||||
integrity sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==
|
||||
dependencies:
|
||||
lowercase-keys "^2.0.0"
|
||||
|
||||
ret@~0.1.10:
|
||||
version "0.1.15"
|
||||
resolved "https://registry.npmmirror.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc"
|
||||
@ -8702,18 +8404,6 @@ ripemd160@^2.0.0, ripemd160@^2.0.1:
|
||||
hash-base "^3.0.0"
|
||||
inherits "^2.0.1"
|
||||
|
||||
roarr@^2.15.3:
|
||||
version "2.15.4"
|
||||
resolved "https://registry.npmmirror.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd"
|
||||
integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==
|
||||
dependencies:
|
||||
boolean "^3.0.1"
|
||||
detect-node "^2.0.4"
|
||||
globalthis "^1.0.1"
|
||||
json-stringify-safe "^5.0.1"
|
||||
semver-compare "^1.0.0"
|
||||
sprintf-js "^1.1.2"
|
||||
|
||||
rollup-plugin-visualizer@5.9.0:
|
||||
version "5.9.0"
|
||||
resolved "https://registry.npmmirror.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.9.0.tgz#013ac54fb6a9d7c9019e7eb77eced673399e5a0b"
|
||||
@ -8843,22 +8533,17 @@ select-hose@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca"
|
||||
integrity sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==
|
||||
|
||||
semver-compare@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmmirror.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc"
|
||||
integrity sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==
|
||||
|
||||
semver@^5.6.0:
|
||||
version "5.7.2"
|
||||
resolved "https://registry.npmmirror.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8"
|
||||
integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==
|
||||
|
||||
semver@^6.2.0, semver@^6.3.0, semver@^6.3.1:
|
||||
semver@^6.3.0, semver@^6.3.1:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
|
||||
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
|
||||
|
||||
semver@^7.3.2, semver@^7.3.5, semver@^7.3.7:
|
||||
semver@^7.3.5, semver@^7.3.7:
|
||||
version "7.5.4"
|
||||
resolved "https://registry.npmmirror.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
|
||||
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
|
||||
@ -8870,13 +8555,6 @@ semver@~7.0.0:
|
||||
resolved "https://registry.npmmirror.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
|
||||
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
|
||||
|
||||
serialize-error@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.npmmirror.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18"
|
||||
integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==
|
||||
dependencies:
|
||||
type-fest "^0.13.1"
|
||||
|
||||
setimmediate@^1.0.4:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
@ -9075,11 +8753,6 @@ split2@^4.0.0:
|
||||
resolved "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4"
|
||||
integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==
|
||||
|
||||
sprintf-js@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673"
|
||||
integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug==
|
||||
|
||||
sprintf-js@~1.0.2:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||
@ -9322,13 +8995,6 @@ sucrase@^3.32.0:
|
||||
pirates "^4.0.1"
|
||||
ts-interface-checker "^0.1.9"
|
||||
|
||||
sumchecker@^3.0.1:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.npmmirror.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42"
|
||||
integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==
|
||||
dependencies:
|
||||
debug "^4.1.0"
|
||||
|
||||
superjson@^1.10.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.npmmirror.com/superjson/-/superjson-1.13.1.tgz#a0b6ab5d22876f6207fcb9d08b0cb2acad8ee5cd"
|
||||
@ -9611,11 +9277,6 @@ tty-browserify@0.0.0:
|
||||
resolved "https://registry.npmmirror.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6"
|
||||
integrity sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==
|
||||
|
||||
type-fest@^0.13.1:
|
||||
version "0.13.1"
|
||||
resolved "https://registry.npmmirror.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934"
|
||||
integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==
|
||||
|
||||
type@^1.0.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.npmmirror.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0"
|
||||
@ -9734,11 +9395,6 @@ unicode-property-aliases-ecmascript@^2.0.0:
|
||||
resolved "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd"
|
||||
integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.npmmirror.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
|
||||
|
||||
universalify@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmmirror.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
|
||||
@ -10034,14 +9690,6 @@ yargs@^17.5.1, yargs@^17.7.2:
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^21.1.1"
|
||||
|
||||
yauzl@^2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.npmmirror.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9"
|
||||
integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
@ -166,7 +166,7 @@ public class TableController {
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
@GetMapping("/modify/sql")
|
||||
@PostMapping("/modify/sql")
|
||||
public ListResult<SqlVO> modifySql(@Valid TableModifySqlRequest request) {
|
||||
return tableService.buildSql(
|
||||
rdbWebConverter.tableRequest2param(request.getOldTable()),
|
||||
|
@ -2,6 +2,7 @@ package ai.chat2db.server.web.api.controller.rdb.vo;
|
||||
|
||||
import ai.chat2db.spi.enums.ColumnTypeEnum;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonAlias;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
@ -18,27 +19,52 @@ import lombok.experimental.SuperBuilder;
|
||||
@AllArgsConstructor
|
||||
public class ColumnVO {
|
||||
/**
|
||||
* 名称
|
||||
* 旧的列名,在修改列的时候需要这个参数
|
||||
* 在返回的时候oldName=name
|
||||
*/
|
||||
private String oldName;
|
||||
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 列的类型
|
||||
*
|
||||
* @see ColumnTypeEnum
|
||||
* 表名
|
||||
*/
|
||||
private String dataType;
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 列的类型
|
||||
* 比如 varchar(100) ,double(10,6)
|
||||
*/
|
||||
|
||||
private String columnType;
|
||||
|
||||
/**
|
||||
* 是否为空
|
||||
* 列的数据类型
|
||||
* 比如 varchar ,double
|
||||
*/
|
||||
private Integer nullable;
|
||||
|
||||
private Integer dataType;
|
||||
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
*/
|
||||
|
||||
private String defaultValue;
|
||||
|
||||
/**
|
||||
* 是否自增
|
||||
* 为空 代表没有值 数据库的实际语义是 false
|
||||
*/
|
||||
private Boolean autoIncrement;
|
||||
|
||||
/**
|
||||
* 注释
|
||||
*/
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* 是否主键
|
||||
@ -46,33 +72,85 @@ public class ColumnVO {
|
||||
private Boolean primaryKey;
|
||||
|
||||
/**
|
||||
* 默认值
|
||||
* 空间名
|
||||
*/
|
||||
private String defaultValue;
|
||||
private String schemaName;
|
||||
|
||||
/**
|
||||
* 是否自增
|
||||
* 数据库名
|
||||
*/
|
||||
private Boolean autoIncrement;
|
||||
private String databaseName;
|
||||
|
||||
/**
|
||||
* 数字精度
|
||||
* Data source dependent type name, for a UDT the type name is fully qualified
|
||||
*/
|
||||
private Integer numericPrecision;
|
||||
private String typeName;
|
||||
|
||||
/**
|
||||
* 数字比例
|
||||
* column size.
|
||||
*/
|
||||
private Integer numericScale;
|
||||
|
||||
private Integer columnSize;
|
||||
|
||||
/**
|
||||
* 字符串最大长度
|
||||
* is not used.
|
||||
*/
|
||||
private Integer characterMaximumLength;
|
||||
private Integer bufferLength;
|
||||
|
||||
/**
|
||||
* 注释
|
||||
* the number of fractional digits. Null is returned for data types where DECIMAL_DIGITS is not applicable.
|
||||
*/
|
||||
private String comment;
|
||||
|
||||
private Integer decimalDigits;
|
||||
|
||||
/**
|
||||
* Radix (typically either 10 or 2)
|
||||
*/
|
||||
|
||||
private Integer numPrecRadix;
|
||||
|
||||
/**
|
||||
* is NULL allowed.
|
||||
* columnNoNulls - might not allow NULL values
|
||||
* columnNullable - definitely allows NULL values
|
||||
* columnNullableUnknown - nullability unknown
|
||||
*/
|
||||
private Integer nullableInt;
|
||||
|
||||
/**
|
||||
* unused
|
||||
*/
|
||||
private Integer sqlDataType;
|
||||
|
||||
|
||||
/**
|
||||
* unused
|
||||
*/
|
||||
private Integer sqlDatetimeSub;
|
||||
|
||||
/**
|
||||
* for char types the maximum number of bytes in the column
|
||||
*/
|
||||
private Integer charOctetLength;
|
||||
|
||||
/**
|
||||
* index of column in table (starting at 1)
|
||||
*/
|
||||
|
||||
private Integer ordinalPosition;
|
||||
|
||||
/**
|
||||
* ISO rules are used to determine the nullability for a column.
|
||||
*/
|
||||
|
||||
private Integer nullable;
|
||||
|
||||
/**
|
||||
* String => Indicates whether this is a generated column
|
||||
* * YES --- if this a generated column
|
||||
* * NO --- if this not a generated column
|
||||
*/
|
||||
private Boolean generatedColumn;
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,99 @@
|
||||
package ai.chat2db.server.web.api.controller.rdb.vo;
|
||||
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.experimental.SuperBuilder;
|
||||
|
||||
/**
|
||||
* 列信息
|
||||
*
|
||||
* @author Jiaju Zhuang
|
||||
*/
|
||||
@Data
|
||||
@SuperBuilder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class IndexColumnVO {
|
||||
|
||||
/**
|
||||
* 索引名称
|
||||
*/
|
||||
private String indexName;
|
||||
|
||||
/**
|
||||
* 表名
|
||||
*/
|
||||
private String tableName;
|
||||
|
||||
/**
|
||||
* 索引类型
|
||||
*
|
||||
* @see
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 注释
|
||||
*/
|
||||
private String comment;
|
||||
|
||||
/**
|
||||
* 列名
|
||||
*/
|
||||
private String columnName;
|
||||
|
||||
/**
|
||||
* 顺序
|
||||
*/
|
||||
private Short ordinalPosition;
|
||||
|
||||
/**
|
||||
* 排序
|
||||
*
|
||||
*/
|
||||
private String collation;
|
||||
|
||||
|
||||
/**
|
||||
* 索引所属schema
|
||||
*/
|
||||
private String schemaName;
|
||||
|
||||
/**
|
||||
* 数据库名
|
||||
*/
|
||||
private String databaseName;
|
||||
|
||||
/**
|
||||
* 是否唯一
|
||||
*/
|
||||
private Boolean nonUnique;
|
||||
|
||||
/**
|
||||
* index catalog (may be null); null when TYPE is tableIndexStatistic
|
||||
*/
|
||||
private String indexQualifier;
|
||||
|
||||
/**
|
||||
* ASC_OR_DESC String => column sort sequence, "A" => ascending, "D" => descending, may be null if sort sequence is not supported; null when TYPE is tableIndexStatistic
|
||||
*/
|
||||
private String ascOrDesc;
|
||||
|
||||
/**
|
||||
* CARDINALITY long => When TYPE is tableIndexStatistic, then this is the number of rows in the table; otherwise, it is the number of unique values in the index.
|
||||
*/
|
||||
private Long cardinality;
|
||||
|
||||
/**
|
||||
* When TYPE is tableIndexStatistic then this is the number of pages used for the table, otherwise it is the number of pages used for the current index.
|
||||
*/
|
||||
private Long pages;
|
||||
|
||||
/**
|
||||
* Filter condition, if any. (may be null)
|
||||
*/
|
||||
private String filterCondition;
|
||||
}
|
||||
|
@ -39,5 +39,5 @@ public class IndexVO {
|
||||
/**
|
||||
* 索引包含的列
|
||||
*/
|
||||
private List<ColumnVO> columnList;
|
||||
private List<IndexColumnVO> columnList;
|
||||
}
|
||||
|
Reference in New Issue
Block a user