mirror of
https://github.com/tencentmusic/supersonic.git
synced 2026-04-22 06:44:18 +08:00
[improvement][headless-fe] Restructured the data for the dimension value settings interface. (#760)
* [improvement][semantic-fe] Add model alias setting & Add view permission restrictions to the model permission management tab. [improvement][semantic-fe] Add permission control to the action buttons for the main domain; apply high sensitivity filtering to the authorization of metrics/dimensions. [improvement][semantic-fe] Optimize the editing mode in the dimension/metric/datasource components to use the modelId stored in the database for data, instead of relying on the data from the state manager. * [improvement][semantic-fe] Add time granularity setting in the data source configuration. * [improvement][semantic-fe] Dictionary import for dimension values supported in Q&A visibility * [improvement][semantic-fe] Modification of data source creation prompt wording" * [improvement][semantic-fe] metric market experience optimization * [improvement][semantic-fe] enhance the analysis of metric trends * [improvement][semantic-fe] optimize the presentation of metric trend permissions * [improvement][semantic-fe] add metric trend download functionality * [improvement][semantic-fe] fix the dimension initialization issue in metric correlation * [improvement][semantic-fe] Fix the issue of database changes not taking effect when creating based on an SQL data source. * [improvement][semantic-fe] Optimizing pagination logic and some CSS styles * [improvement][semantic-fe] Fixing the API for the indicator list by changing "current" to "pageNum" * [improvement][semantic-fe] Fixing the default value setting for the indicator list * [improvement][semantic-fe] Adding batch operations for indicators/dimensions/models * [improvement][semantic-fe] Replacing the single status update API for indicators/dimensions with a batch update API * [improvement][semantic-fe] Redesigning the indicator homepage to incorporate trend charts and table functionality for indicators * [improvement][semantic-fe] Optimizing the logic for setting dimension values and editing data sources, and adding system settings functionality * [improvement][semantic-fe] Upgrading antd version to 5.x, extracting the batch operation button component, optimizing the interaction for system settings, and expanding the configuration generation types for list-to-select component. * [improvement][semantic-fe] Adding the ability to filter dimensions based on whether they are tags or not. * [improvement][semantic-fe] Adding the ability to edit relationships between models in the canvas. * [improvement][semantic-fe] Updating the datePicker component to use dayjs instead. * [improvement][semantic-fe] Fixing the issue with passing the model ID for dimensions in the indicator market. * [improvement][semantic-fe] Fixing the abnormal state of the popup when creating a model. * [improvement][semantic-fe] Adding permission logic for bulk operations in the indicator market. * [improvement][semantic-fe] Adding the ability to download and transpose data. * [improvement][semantic-fe] Fixing the initialization issue with the date selection component in the indicator details page when switching time granularity. * [improvement][semantic-fe] Fixing the logic error in the dimension value setting. * [improvement][semantic-fe] Fixing the synchronization issue with the question and answer settings information. * [improvement][semantic-fe] Optimizing the canvas functionality for better performance and user experience. * [improvement][semantic-fe] Optimizing the update process for drawing model relationship edges in the canvas. * [improvement][semantic-fe] Changing the line type for canvas connections. * [improvement][semantic-fe] Replacing the initialization variable from "semantic" to "headless". * [improvement][semantic-fe] Fixing the missing migration issue for default drill-down dimension configuration in model editing. Additionally, optimizing the data retrieval method for initializing fields in the model. * [improvement][semantic-fe] Updating the logic for the fieldName. * [improvement][semantic-fe] Adjusting the position of the metrics tab. * [improvement][semantic-fe] Changing the 字段名称 to 英文名称. * [improvement][semantic-fe] Fix metric measurement deletion. * [improvement][semantic-fe] UI optimization for metric details page. * [improvement][semantic-fe] UI optimization for metric details page. * [improvement][semantic-fe] UI adjustment for metric details page. * [improvement][semantic-fe] The granularity field in the time type of model editing now supports setting it as empty. * [improvement][semantic-fe] Added field type and metric type to the metric creation options. * [improvement][semantic-fe] The organization structure selection feature has been added to the permission management. * [improvement][semantic-fe] Improved user experience for the metric list. * [improvement][semantic-fe] fix update the metric list. * [improvement][headless-fe] Added view management functionality. * [improvement][headless-fe] The view management functionality has been added. This feature allows users to create, edit, and manage different views within the system. * [improvement][headless-fe] Added model editing side effect detection. * [improvement][headless-fe] Fixed the logic error in view editing. * [improvement][headless-fe] Fixed the issue with initializing dimension associations in metric settings. * [improvement][headless-fe] Added the ability to hide the Q&A settings entry point. * [improvement][headless-fe] Fixed the issue with selecting search results in metric field creation. * [improvement][headless-fe] Added search functionality to the field list in model editing. * [improvement][headless-fe] fix the field list in model editing * [improvement][headless-fe] Restructured the data for the dimension value settings interface.
This commit is contained in:
@@ -12,17 +12,17 @@ import {
|
||||
SwapOutlined,
|
||||
PlayCircleOutlined,
|
||||
CloudServerOutlined,
|
||||
ApiOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { isFunction } from 'lodash';
|
||||
import FullScreen from '@/components/FullScreen';
|
||||
import SqlEditor from '@/components/SqlEditor';
|
||||
import type { TaskResultItem, TaskResultColumn } from '../data';
|
||||
import { excuteSql } from '@/pages/SemanticModel/service';
|
||||
// import DataSourceCreateForm from './DataSourceCreateForm';
|
||||
import type { Dispatch } from 'umi';
|
||||
import type { StateType } from '../../model';
|
||||
import SqlParams from './SqlParams';
|
||||
import styles from '../style.less';
|
||||
|
||||
import 'ace-builds/src-min-noconflict/ext-searchbox';
|
||||
import 'ace-builds/src-min-noconflict/theme-sqlserver';
|
||||
import 'ace-builds/src-min-noconflict/theme-monokai';
|
||||
@@ -77,7 +77,6 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
});
|
||||
const [dataBaseItems, setDataBaseItems] = useState<DatabaseItem[]>([]);
|
||||
const [currentDatabaseItem, setCurrentDatabaseItem] = useState<DatabaseItem>();
|
||||
// const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
|
||||
|
||||
const [tableScroll, setTableScroll] = useState({
|
||||
scrollToFirstRowOnChange: true,
|
||||
@@ -88,7 +87,7 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
const [runState, setRunState] = useState<boolean | undefined>();
|
||||
|
||||
const [taskLog, setTaskLog] = useState('');
|
||||
const [isSqlExcLocked, setIsSqlExcLocked] = useState(false);
|
||||
const [isSqlExcLocked, setIsSqlExcLocked] = useState<boolean>(false);
|
||||
const [screenSize, setScreenSize] = useState<ScreenSize>('middle');
|
||||
|
||||
const [isSqlIdeFullScreen, setIsSqlIdeFullScreen] = useState<boolean>(false);
|
||||
@@ -100,8 +99,11 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
const DEFAULT_FULLSCREEN_TOP = 0;
|
||||
|
||||
const [partialSql, setPartialSql] = useState('');
|
||||
const [isPartial, setIsPartial] = useState(false);
|
||||
const [isRight, setIsRight] = useState(false);
|
||||
const [isPartial, setIsPartial] = useState<boolean>(false);
|
||||
const [isRight, setIsRight] = useState<boolean>(false);
|
||||
|
||||
const [variableCollapsed, setVariableCollapsed] = useState<boolean>(true);
|
||||
const [sqlParams, setSqlParams] = useState<IDataSource.ISqlParamsItem[]>([]);
|
||||
|
||||
const [scriptColumns, setScriptColumns] = useState<any[]>([]);
|
||||
|
||||
@@ -125,6 +127,10 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
setCurrentDatabaseItem(targetDataBase);
|
||||
}, [dataSourceItem, databaseConfigList]);
|
||||
|
||||
useEffect(() => {
|
||||
setSqlParams(dataSourceItem?.modelDetail?.sqlVariables || []);
|
||||
}, [dataSourceItem]);
|
||||
|
||||
useEffect(() => {
|
||||
setRunState(undefined);
|
||||
}, [currentDatabaseItem, sql]);
|
||||
@@ -138,6 +144,11 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
return line;
|
||||
}
|
||||
|
||||
const handleVariable = () => {
|
||||
const collapsedValue = !variableCollapsed;
|
||||
setVariableCollapsed(collapsedValue);
|
||||
};
|
||||
|
||||
// 计算每列的宽度,通过容器插入文档中动态得到该列数据(包括表头)的最长宽度,设为列宽度,保证每列的数据都能一行展示完
|
||||
function getKeyWidthMap(list: TaskResultItem[]): TaskResultItem {
|
||||
const widthMap = {};
|
||||
@@ -417,6 +428,9 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
<Tooltip title="格式化SQL语句">
|
||||
<EditOutlined className={styles.sqlOprIcon} onClick={formatSQL} />
|
||||
</Tooltip>
|
||||
{/* <Tooltip title="动态变量">
|
||||
<ApiOutlined className={styles.sqlOprIcon} onClick={handleVariable} />
|
||||
</Tooltip> */}
|
||||
<Tooltip title="改变主题">
|
||||
<SwapOutlined className={styles.sqlOprIcon} onClick={handleThemeChange} />
|
||||
</Tooltip>
|
||||
@@ -463,6 +477,12 @@ const SqlDetail: React.FC<IProps> = ({
|
||||
onSelect={onSelect}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={variableCollapsed ? styles.hideSqlParams : styles.sqlParams}
|
||||
// style={{ height: sqlEditorHeight }}
|
||||
>
|
||||
<SqlParams value={sqlParams} onChange={setSqlParams} />
|
||||
</div>
|
||||
</div>
|
||||
</Pane>
|
||||
<div className={`${styles.sqlBottmWrap} ${screenSize}`}>
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
import { useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import type { SqlParamsItem, OprType } from '../data';
|
||||
|
||||
import styles from '../style.less';
|
||||
import { AppstoreAddOutlined, DeleteTwoTone, EditTwoTone } from '@ant-design/icons';
|
||||
import SqlParamsDetailModal from './SqlParamsDetailModal';
|
||||
import { List } from 'antd';
|
||||
|
||||
type Props = {
|
||||
value?: SqlParamsItem[];
|
||||
onChange?: (e: SqlParamsItem[]) => void;
|
||||
};
|
||||
|
||||
const defalutItem: SqlParamsItem = {
|
||||
name: '',
|
||||
type: 'query',
|
||||
defaultValues: [],
|
||||
valueType: 'string',
|
||||
udf: false,
|
||||
};
|
||||
|
||||
const SqlParams: FC<Props> = ({ value, onChange }) => {
|
||||
const [oprType, setOprType] = useState<OprType>('add');
|
||||
const [visible, setVisible] = useState<boolean>(false);
|
||||
const [initValue, setInitValue] = useState<SqlParamsItem>();
|
||||
const paramsChange = (params: SqlParamsItem[]) => {
|
||||
if (onChange) {
|
||||
onChange(params);
|
||||
}
|
||||
};
|
||||
const handleAdd = () => {
|
||||
setOprType('add');
|
||||
setVisible(true);
|
||||
setInitValue(defalutItem);
|
||||
};
|
||||
const handleSave = async (values: SqlParamsItem) => {
|
||||
const newValue = value ? [...value] : [];
|
||||
const { index, ...rest } = values;
|
||||
if (index || index === 0) {
|
||||
newValue[index] = rest;
|
||||
} else {
|
||||
newValue.push(rest);
|
||||
}
|
||||
setVisible(false);
|
||||
setInitValue(undefined);
|
||||
paramsChange(newValue);
|
||||
};
|
||||
|
||||
const handleDelete = (index: number) => {
|
||||
const newValue = value ? [...value] : [];
|
||||
newValue.splice(index, 1);
|
||||
paramsChange(newValue);
|
||||
};
|
||||
const handleEdit = (index: number) => {
|
||||
const paramsItem = value ? value[index] : defalutItem;
|
||||
setInitValue({ ...paramsItem, index });
|
||||
setOprType('edit');
|
||||
setVisible(true);
|
||||
};
|
||||
return (
|
||||
<>
|
||||
<div className={styles.sqlParamsBody}>
|
||||
<div className={styles.header}>
|
||||
<span className={styles.title}>变量</span>
|
||||
<AppstoreAddOutlined className={styles.icon} onClick={handleAdd} />
|
||||
</div>
|
||||
<List
|
||||
className={styles.paramsList}
|
||||
dataSource={value}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item
|
||||
title={item.name}
|
||||
className={styles.paramsItem}
|
||||
key={item.name}
|
||||
actions={[
|
||||
<>
|
||||
<EditTwoTone
|
||||
className={styles.icon}
|
||||
onClick={() => {
|
||||
handleEdit(index);
|
||||
}}
|
||||
/>
|
||||
<DeleteTwoTone
|
||||
className={styles.icon}
|
||||
onClick={() => {
|
||||
handleDelete(index);
|
||||
}}
|
||||
/>
|
||||
</>,
|
||||
]}
|
||||
>
|
||||
<div className={styles.name}>{item.name}</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<SqlParamsDetailModal
|
||||
nameList={value?.map((item) => item.name)}
|
||||
oprType={oprType}
|
||||
modalVisible={visible}
|
||||
onSave={handleSave}
|
||||
onCancel={() => {
|
||||
setVisible(false);
|
||||
}}
|
||||
initValue={initValue}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SqlParams;
|
||||
@@ -0,0 +1,195 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Modal, Form, Input, Select, Checkbox } from 'antd';
|
||||
import { isFunction } from 'lodash';
|
||||
import { objToArray } from '@/utils/utils';
|
||||
import type { ParamsItemProps, OprType } from '../data';
|
||||
import { IDataSource } from '../../data';
|
||||
import TextArea from 'antd/lib/input/TextArea';
|
||||
import ParamsSqlEditor from './SqlParamsSqlEditor';
|
||||
|
||||
const EnumSqlParamsType = {
|
||||
auth: '权限变量',
|
||||
query: '查询变量',
|
||||
};
|
||||
|
||||
const EnumSqlValueType = {
|
||||
string: '字符串',
|
||||
sql: 'SQL表达式',
|
||||
date: '日期',
|
||||
boolean: '布尔',
|
||||
number: '数字',
|
||||
};
|
||||
|
||||
const { Option } = Select;
|
||||
|
||||
const ParamsTextArea: FC<ParamsItemProps> = ({ value, onChange }) => {
|
||||
return (
|
||||
<TextArea
|
||||
value={value && value[0]}
|
||||
onChange={(e) => {
|
||||
if (onChange) {
|
||||
onChange([e.target.value]);
|
||||
}
|
||||
}}
|
||||
placeholder="请输入表达式"
|
||||
rows={3}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
type IProps = {
|
||||
oprType: OprType;
|
||||
modalVisible: boolean;
|
||||
onSave: (values: IDataSource.ISqlParamsItem) => Promise<any>;
|
||||
onCancel?: (oprType: OprType) => void;
|
||||
initValue?: IDataSource.ISqlParamsItem;
|
||||
nameList?: string[];
|
||||
};
|
||||
|
||||
const SqlParamsDetailModal: FC<IProps> = ({
|
||||
oprType = 'add',
|
||||
initValue = {} as IDataSource.ISqlParamsItem,
|
||||
modalVisible,
|
||||
onSave,
|
||||
onCancel,
|
||||
nameList,
|
||||
}) => {
|
||||
const [valueType, setValueType] = useState<IDataSource.ISqlParamsValueType>();
|
||||
const [udf, setUdf] = useState<boolean>();
|
||||
const [oldName, setOldName] = useState<string>();
|
||||
|
||||
const formLayout = {
|
||||
labelCol: { span: 7 },
|
||||
wrapperCol: { span: 13 },
|
||||
};
|
||||
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const submitSave = async () => {
|
||||
const fieldsValue = await form.validateFields();
|
||||
onSave({ ...fieldsValue, index: initValue.index, udf });
|
||||
};
|
||||
|
||||
const handleCancel = async () => {
|
||||
if (onCancel && isFunction(onCancel)) {
|
||||
setUdf(false);
|
||||
onCancel(oprType);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
form.setFieldsValue({
|
||||
...initValue,
|
||||
});
|
||||
setValueType(initValue.valueType);
|
||||
setUdf(initValue.udf);
|
||||
setOldName(initValue.name);
|
||||
}, [initValue]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
forceRender
|
||||
title={`${oprType === 'add' ? '新建' : '编辑'}sql参数`}
|
||||
open={modalVisible}
|
||||
onOk={submitSave}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Form
|
||||
{...formLayout}
|
||||
initialValues={{
|
||||
...initValue,
|
||||
}}
|
||||
form={form}
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="参数名称"
|
||||
rules={[
|
||||
{ required: true, message: '请输入参数名称' },
|
||||
{
|
||||
validator(_, value, confirm) {
|
||||
if (
|
||||
nameList?.some((item) => {
|
||||
return item === value && value !== oldName;
|
||||
})
|
||||
) {
|
||||
confirm('名称不能重复');
|
||||
} else {
|
||||
confirm();
|
||||
}
|
||||
},
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input placeholder="请输入参数名称" />
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="type"
|
||||
label="参数类型"
|
||||
rules={[{ required: true, message: '请选择参数类型' }]}
|
||||
>
|
||||
<Select placeholder="请选择" disabled={true}>
|
||||
{objToArray(EnumSqlParamsType).map((d) => (
|
||||
<Option key={d.value} value={d.value}>
|
||||
{d.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item
|
||||
name="valueType"
|
||||
label="值类型"
|
||||
rules={[{ required: true, message: '请选择值类型' }]}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择值类型"
|
||||
onChange={(e) => {
|
||||
setValueType(e as IDataSource.ISqlParamsValueType);
|
||||
if (e === 'sql') {
|
||||
form.setFieldsValue({
|
||||
defaultValues: undefined,
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
{objToArray(EnumSqlValueType).map((d) => (
|
||||
<Option key={d.value} value={d.value}>
|
||||
{d.label}
|
||||
</Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="alias" label="别名">
|
||||
<Input placeholder="请输入参数别名" />
|
||||
</Form.Item>
|
||||
{valueType !== 'sql' && (
|
||||
<Form.Item name="udf" label="是否使用表达式">
|
||||
<Checkbox
|
||||
checked={udf}
|
||||
onChange={(e) => {
|
||||
setUdf(e.target.checked);
|
||||
if (e.target.checked) {
|
||||
form.setFieldsValue({
|
||||
defaultValues: '',
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Form.Item>
|
||||
)}
|
||||
{valueType === 'sql' || udf ? (
|
||||
<Form.Item name="defaultValues" label="表达式">
|
||||
<ParamsTextArea />
|
||||
</Form.Item>
|
||||
) : (
|
||||
<Form.Item name="defaultValues" label="默认值">
|
||||
<ParamsSqlEditor />
|
||||
</Form.Item>
|
||||
)}
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default SqlParamsDetailModal;
|
||||
@@ -0,0 +1,132 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import type { FC } from 'react';
|
||||
import { Input, Tag, Tooltip } from 'antd';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
import styles from '../style.less';
|
||||
|
||||
type ParamsItemProps = {
|
||||
value?: string[];
|
||||
onChange?: (e: string[]) => void;
|
||||
};
|
||||
const ParamsSqlEditor: FC<ParamsItemProps> = ({ value, onChange }) => {
|
||||
const [editInputValue, setEditInputValue] = useState<string>();
|
||||
const [inputValue, setInputValue] = useState<string>();
|
||||
const [editInputIndex, setEditInputIndex] = useState<number>(-1);
|
||||
const [inputVisible, setInputVisible] = useState<boolean>(false);
|
||||
const editInput = useRef<typeof Input>(null);
|
||||
const inputRef = useRef<typeof Input>(null);
|
||||
|
||||
const handleEditInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
setEditInputValue(e.target.value);
|
||||
};
|
||||
const handleEditInputConfirm = () => {
|
||||
const newValues = value ? [...value] : [];
|
||||
newValues[editInputIndex] = editInputValue || '';
|
||||
if (onChange) {
|
||||
onChange(newValues);
|
||||
}
|
||||
setEditInputIndex(-1);
|
||||
setEditInputValue('');
|
||||
};
|
||||
const handleClose = (removedTag: string) => {
|
||||
const newValues = value ? value.filter((tag) => tag !== removedTag) : [];
|
||||
if (onChange) {
|
||||
onChange(newValues);
|
||||
}
|
||||
};
|
||||
const handleInputChange: React.ChangeEventHandler<HTMLInputElement> = (e) => {
|
||||
setInputValue(e.target.value);
|
||||
};
|
||||
|
||||
const handleInputConfirm = () => {
|
||||
const newValues = value ? [...value] : [];
|
||||
if (inputValue && !newValues.includes(inputValue)) {
|
||||
newValues.push(inputValue);
|
||||
}
|
||||
if (onChange) {
|
||||
onChange(newValues);
|
||||
}
|
||||
setInputVisible(false);
|
||||
setInputValue('');
|
||||
};
|
||||
|
||||
const showInput = () => {
|
||||
setInputVisible(true);
|
||||
setTimeout(() => {
|
||||
inputRef.current?.focus();
|
||||
}, 0);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{value &&
|
||||
value.map((tag: any, index: number) => {
|
||||
if (editInputIndex === index) {
|
||||
return (
|
||||
<Input
|
||||
ref={editInput}
|
||||
key={tag}
|
||||
size="small"
|
||||
className={styles.tagInput}
|
||||
value={editInputValue}
|
||||
onChange={handleEditInputChange}
|
||||
onBlur={handleEditInputConfirm}
|
||||
onPressEnter={handleEditInputConfirm}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const isLongTag = tag.length > 20;
|
||||
|
||||
const tagElem = (
|
||||
<Tag
|
||||
className={styles.editTag}
|
||||
key={tag}
|
||||
closable={true}
|
||||
onClose={() => handleClose(tag)}
|
||||
>
|
||||
<span
|
||||
onDoubleClick={(e) => {
|
||||
setEditInputIndex(index);
|
||||
setEditInputValue(tag);
|
||||
e.preventDefault();
|
||||
setTimeout(() => {
|
||||
editInput.current?.focus();
|
||||
}, 0);
|
||||
}}
|
||||
>
|
||||
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
|
||||
</span>
|
||||
</Tag>
|
||||
);
|
||||
return isLongTag ? (
|
||||
<Tooltip title={tag} key={tag}>
|
||||
{tagElem}
|
||||
</Tooltip>
|
||||
) : (
|
||||
tagElem
|
||||
);
|
||||
})}
|
||||
{inputVisible && (
|
||||
<Input
|
||||
ref={inputRef}
|
||||
type="text"
|
||||
size="small"
|
||||
className={styles.tagInput}
|
||||
value={inputValue}
|
||||
onChange={handleInputChange}
|
||||
onBlur={handleInputConfirm}
|
||||
onPressEnter={handleInputConfirm}
|
||||
/>
|
||||
)}
|
||||
{!inputVisible && (
|
||||
<Tag className={styles.siteTagPlus} onClick={showInput}>
|
||||
<PlusOutlined /> 增加默认值
|
||||
</Tag>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ParamsSqlEditor;
|
||||
Reference in New Issue
Block a user