diff --git a/app-v2/app.less b/app-v2/app.less index 7c352d38..951b4be0 100644 --- a/app-v2/app.less +++ b/app-v2/app.less @@ -25,6 +25,10 @@ src: url(~@appv2/static/fonts/Roboto-Regular.ttf); } +#app { + font-family: Roboto-Regular, sans-serif; +} + .center-layout { width: @containerWidth; margin: 0 auto; @@ -38,7 +42,7 @@ } .ant-radio-group.nebula-tab-group { - background: #e9edef; + background: @lightGray; border-radius: 20px; padding: 4px; min-width: 400px; @@ -81,8 +85,6 @@ .ant-btn.primary-btn { color: @blue; border-color: @blue; - display: inline-flex; - align-items: center; svg { width: 20px; @@ -137,3 +139,28 @@ } } +.studio-form-footer { + position: fixed; + left: 0; + bottom: 0; + z-index: 10; + width: 100%; + height: 98px; + display: flex; + align-items: center; + justify-content: center; + background: #fff; + box-shadow: 0 -4px 4px rgba(0, 0, 0, 0.1); + + button { + width: 236px; + + &:not(:last-child) { + margin-right: 50px; + } + } +} + +.ant-form-item-label label { + font-family: Roboto-Bold, sans-serif; +} diff --git a/app-v2/common.less b/app-v2/common.less index b167a3a7..2fd5a506 100644 --- a/app-v2/common.less +++ b/app-v2/common.less @@ -1,6 +1,8 @@ @gray: #D5DDEB; -@lightGray: #F8F8F8; +@lightWhite: #F8F8F8; @containerWidth: 1180px; @darkGray: #8697B0; @red: #EB5757; @blue: #2F80ED; +@lightGray: #E9EDEF; +@lightBlue: #F3F6F9; diff --git a/app-v2/components/CSVPreviewLink/index.less b/app-v2/components/CSVPreviewLink/index.less index 9e7d6844..f71d9a30 100644 --- a/app-v2/components/CSVPreviewLink/index.less +++ b/app-v2/components/CSVPreviewLink/index.less @@ -1,3 +1,4 @@ +@import '~@appv2/common.less'; .popover-preview { max-width: 80%; } @@ -12,7 +13,7 @@ } .ant-table-tbody { tr { - background-color: #F3F6F9; + background-color: @lightBlue; } } } diff --git a/app-v2/config/locale/en-US.json b/app-v2/config/locale/en-US.json index e6ff8677..2a3f3b7c 100644 --- a/app-v2/config/locale/en-US.json +++ b/app-v2/config/locale/en-US.json @@ -327,17 +327,7 @@ "vidTypeDescription": "Specifies the data type of vertex IDs (VIDs) in a graph space. ", "createSuccess": "Create Successfully", "defineFields": "Define Properties", - "setTTL": "Set TTL", "uniqProperty": "Property name cannot be duplicated", - "timestampFormat": "Supported data inserting methods:
1. call function now()
2. call function timestamp(), for example: timestamp('2021-07-05T06:18:43.984000')
3. Input the timestamp directly, namely the number of seconds from 1970-01-01 00:00:00", - "dateFormat": "Supported data inserting methods:
Call function date(), for example: date('2021-03-17')", - "timeFormat": "Supported data inserting methods:
Call function time(), for example: time('17:53:59')", - "datetimeFormat": "Supported data inserting methods:
Call function datetime(), for example: datetime('2021-03-17T17:53:59')", - "geographyFormat": "Supported data inserting methods:
Call function ST_GeogFromText(), for example:ST_GeogFromText('POINT(6 10)')", - "geography(point)Format": "Supported data inserting methods:
Call function ST_GeogFromText('POINT()'), for example:ST_GeogFromText('POINT(6 10)')", - "geography(linestring)Format": "Supported data inserting methods:
Call function ST_GeogFromText('LINESTRING()'), for example:ST_GeogFromText('LINESTRING(3 4,10 50,20 25)')", - "geography(polygon)Format": "Supported data inserting methods:
Call function ST_GeogFromText('POLYGON()'), for example:ST_GeogFromText('POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))')", - "durationFormat": "Supported data inserting methods:
Call function duration(), for example:duration({years: 1, seconds: 0})", "cancelOperation": "Do you want to close this panel", "cancelPropmt": "If you close the panel, the configuration will be deleted automatically. Are you sure that you want to close the panel?", "fieldDisabled": "A TTL configuration is set for this property, so it cannot be edited. If you want to edit this property, delete the TTL configuration.", @@ -386,6 +376,19 @@ "operations": "Operations", "useSpaceErrTip": "Space not found. Trying to use a newly created graph space may fail because the creation is implemented asynchronously. To make sure the follow-up operations work as expected, Wait for two heartbeat cycles, i.e., 20 seconds.", "spaceNameEnter": "Please enter the space name", - "propertyCount": "Property Num" + "propertyCount": "Property Num", + "configTypeList": "{space} {type} List", + "configTypeAction": "{action} {type}", + "defineFields": "Define Properties", + "timestampFormat": "Supported data inserting methods:
1. call function now()
2. call function timestamp(), for example: timestamp('2021-07-05T06:18:43.984000')
3. Input the timestamp directly, namely the number of seconds from 1970-01-01 00:00:00", + "dateFormat": "Supported data inserting methods:
Call function date(), for example: date('2021-03-17')", + "timeFormat": "Supported data inserting methods:
Call function time(), for example: time('17:53:59')", + "datetimeFormat": "Supported data inserting methods:
Call function datetime(), for example: datetime('2021-03-17T17:53:59')", + "geographyFormat": "Supported data inserting methods:
Call function ST_GeogFromText(), for example:ST_GeogFromText('POINT(6 10)')", + "geography(point)Format": "Supported data inserting methods:
Call function ST_GeogFromText('POINT()'), for example:ST_GeogFromText('POINT(6 10)')", + "geography(linestring)Format": "Supported data inserting methods:
Call function ST_GeogFromText('LINESTRING()'), for example:ST_GeogFromText('LINESTRING(3 4,10 50,20 25)')", + "geography(polygon)Format": "Supported data inserting methods:
Call function ST_GeogFromText('POLYGON()'), for example:ST_GeogFromText('POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))')", + "durationFormat": "Supported data inserting methods:
Call function duration(), for example:duration({years: 1, seconds: 0})", + "setTTL": "Set TTL (Time To Live)" } } diff --git a/app-v2/config/locale/zh-CN.json b/app-v2/config/locale/zh-CN.json index 94a6c781..f8c8363a 100644 --- a/app-v2/config/locale/zh-CN.json +++ b/app-v2/config/locale/zh-CN.json @@ -323,17 +323,7 @@ "vidTypeDescription": "vid type 指定图空间中点 ID(VID)的数据类型。", "createSuccess": "创建成功", "defineFields": "定义属性", - "setTTL": "设置TTL", "uniqProperty": "属性名称不允许重名", - "timestampFormat": "时间类型支持插入方式:
1. 调用函数 now()
2. 调用函数 timestamp(),例如:timestamp('2021-07-05T06:18:43.984000')
3. 直接输入时间戳,即从 1970-01-01 00:00:00 开始的秒数", - "dateFormat": "日期类型支持插入方式:
调用函数 date(),例如:date('2021-03-17')", - "timeFormat": "时间类型支持插入方式:
调用函数 time(),例如time('17:53:59')", - "datetimeFormat": "日期时间类型支持插入方式:
调用函数 datetime(),例如:datetime('2021-03-17T17:53:59')", - "geographyFormat": "geo 类型支持插入方式:
调用函数 ST_GeogFromText(),例如:ST_GeogFromText('POINT(6 10)')", - "geography(point)Format": "geo(point) 类型支持插入方式:
调用函数 ST_GeogFromText('POINT()'),例如:ST_GeogFromText('POINT(6 10)')", - "geography(linestring)Format": "geo(linestring) 类型支持插入方式:
调用函数 ST_GeogFromText('LINESTRING()'),例如:ST_GeogFromText('LINESTRING(3 4,10 50,20 25)')", - "geography(polygon)Format": "geo(polygon) 类型支持插入方式:
调用函数 ST_GeogFromText('POLYGON()'),例如:ST_GeogFromText('POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))')", - "durationFormat": "duration 类型支持插入方式:
调用函数 duration(),例如:duration({years: 1, seconds: 0})", "cancelOperation": "是否取消配置并关闭面板", "cancelPropmt": "关闭面板将删除所有属性,是否继续?", "fieldDisabled": "该属性被 ttl_col 引用,不支持更改操作,如要更改,请先更新 ttl", @@ -382,6 +372,19 @@ "operations": "操作", "useSpaceErrTip": "图空间未找到。立刻尝试使用刚创建的图空间可能会失败,因为创建是异步实现的。为确保数据同步,后续操作能顺利进行,请等待 2 个心跳周期(20 秒)。", "spaceNameEnter": "请输入图空间名称", - "propertyCount": "属性数量" + "propertyCount": "属性数量", + "configTypeList": "{space} {type}列表", + "configTypeAction": "{action}{type}", + "defineFields": "定义属性", + "timestampFormat": "时间类型支持插入方式:
1. 调用函数 now()
2. 调用函数 timestamp(),例如:timestamp('2021-07-05T06:18:43.984000')
3. 直接输入时间戳,即从 1970-01-01 00:00:00 开始的秒数", + "dateFormat": "日期类型支持插入方式:
调用函数 date(),例如:date('2021-03-17')", + "timeFormat": "时间类型支持插入方式:
调用函数 time(),例如time('17:53:59')", + "datetimeFormat": "日期时间类型支持插入方式:
调用函数 datetime(),例如:datetime('2021-03-17T17:53:59')", + "geographyFormat": "geo 类型支持插入方式:
调用函数 ST_GeogFromText(),例如:ST_GeogFromText('POINT(6 10)')", + "geography(point)Format": "geo(point) 类型支持插入方式:
调用函数 ST_GeogFromText('POINT()'),例如:ST_GeogFromText('POINT(6 10)')", + "geography(linestring)Format": "geo(linestring) 类型支持插入方式:
调用函数 ST_GeogFromText('LINESTRING()'),例如:ST_GeogFromText('LINESTRING(3 4,10 50,20 25)')", + "geography(polygon)Format": "geo(polygon) 类型支持插入方式:
调用函数 ST_GeogFromText('POLYGON()'),例如:ST_GeogFromText('POLYGON((1 1,5 1,5 5,1 5,1 1),(2 2,2 3,3 3,3 2,2 2))')", + "durationFormat": "duration 类型支持插入方式:
调用函数 duration(),例如:duration({years: 1, seconds: 0})", + "setTTL": "设置TTL(存活时间)" } } \ No newline at end of file diff --git a/app-v2/interfaces/schema.ts b/app-v2/interfaces/schema.ts index 7e415239..e44d59f8 100644 --- a/app-v2/interfaces/schema.ts +++ b/app-v2/interfaces/schema.ts @@ -44,9 +44,11 @@ export interface IProperty { allowNull?: boolean; fixedLength?: string; comment?: string; + showType?: string; } -export type IndexType = 'TAG' | 'EDGE'; +export type IndexType = 'tag' | 'edge'; +export type ISchemaType = 'tag' | 'edge'; export type AlterType = 'ADD' | 'DROP' | 'CHANGE' | 'TTL' | 'COMMENT'; export interface IAlterConfig { fields?: IProperty[]; @@ -55,4 +57,11 @@ export interface IAlterConfig { col?: string; duration?: string; }; -} \ No newline at end of file +} + +export interface IAlterForm { + type: ISchemaType; + name: string; + action: AlterType; + config: IAlterConfig; +} diff --git a/app-v2/pages/Import/FileUpload/index.tsx b/app-v2/pages/Import/FileUpload/index.tsx index 2b2c9924..46154610 100644 --- a/app-v2/pages/Import/FileUpload/index.tsx +++ b/app-v2/pages/Import/FileUpload/index.tsx @@ -63,21 +63,19 @@ const FileUpload = () => { if (file.content) { return (
-
- - - - asyncDeleteFile(index)} - title={intl.get('common.ask')} - okText={intl.get('common.ok')} - cancelText={intl.get('common.cancel')} - > - - -
+ + + + asyncDeleteFile(index)} + title={intl.get('common.ask')} + okText={intl.get('common.ok')} + cancelText={intl.get('common.cancel')} + > + +
); } diff --git a/app-v2/pages/Import/TaskCreate/SchemaConfig/index.less b/app-v2/pages/Import/TaskCreate/SchemaConfig/index.less index 29ff8b87..6139305e 100644 --- a/app-v2/pages/Import/TaskCreate/SchemaConfig/index.less +++ b/app-v2/pages/Import/TaskCreate/SchemaConfig/index.less @@ -1,6 +1,6 @@ @import '~@appv2/common.less'; .config-collapse { - background: #F3F6F9; + background: @lightBlue; border: 1px solid @gray; box-sizing: border-box; border-radius: 6px; diff --git a/app-v2/pages/Import/TaskCreate/SchemaConfig/index.tsx b/app-v2/pages/Import/TaskCreate/SchemaConfig/index.tsx index 02a88032..be34f6f1 100644 --- a/app-v2/pages/Import/TaskCreate/SchemaConfig/index.tsx +++ b/app-v2/pages/Import/TaskCreate/SchemaConfig/index.tsx @@ -73,7 +73,7 @@ const SchemaConfig = (props: IProps) => { {type === 'vertices' && data.tags.map((tag, tagIndex) => )} {type === 'edge' && } {type === 'vertices' &&
- diff --git a/app-v2/pages/Import/TaskCreate/index.less b/app-v2/pages/Import/TaskCreate/index.less index d88fad63..ab8eff8d 100644 --- a/app-v2/pages/Import/TaskCreate/index.less +++ b/app-v2/pages/Import/TaskCreate/index.less @@ -2,6 +2,9 @@ .nebula-import-create { .create-form { padding: 32px 0 100px; + label { + font-family: Roboto-Bold; + } .basic-config { border-bottom: 1px solid @gray; } @@ -22,23 +25,6 @@ margin-right: 20px; } } - .footer { - position: fixed; - bottom: 0; - width: 100%; - height: 98px; - display: flex; - align-items: center; - justify-content: center; - background: #FFFFFF; - box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.1); - button { - width: 236px; - &:not(:last-child) { - margin-right: 50px; - } - } - } .label::after { content: ':'; padding-right: 5px; diff --git a/app-v2/pages/Import/TaskCreate/index.tsx b/app-v2/pages/Import/TaskCreate/index.tsx index 0586c0bc..306b05b7 100644 --- a/app-v2/pages/Import/TaskCreate/index.tsx +++ b/app-v2/pages/Import/TaskCreate/index.tsx @@ -119,6 +119,11 @@ const TaskCreate = () => { edgesConfig: [] }); }; + const handleSpaceChange = (space: string) => { + clearConfig(); + updateSpaceInfo(space); + }; + useEffect(() => { asyncGetTaskDir(); getSpaces(); @@ -131,12 +136,12 @@ const TaskCreate = () => { return (
-
+
- handleSpaceChange(value)}> {spaces.map(space => (
-
+
+ + + + {intl.get('common.propertyName')} + {intl.get('common.dataType')} + {intl.get('common.allowNull')} + {intl.get('common.defaults')} + {intl.get('common.comment')} + + + {fields.map(({ key, name, ...restField }, index) => ( + + + + + + + + + + + + {fields && fields[index] && properties[index].type === 'fixed_string' && ( + + + + + + )} + + + + + + + + + {fields && + fields[index] && + EXPLAIN_DATA_TYPE.includes(properties[index].type) ? ( + + + + + + ) : ( + + + + )} + + + + + + + + + + + + + + + ))} + + ); + }} + +
+ ); + }} + +
+ + ); +}; + +export default observer(PropertiesForm); diff --git a/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/TTLForm.tsx b/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/TTLForm.tsx new file mode 100644 index 00000000..2f58d5d6 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/TTLForm.tsx @@ -0,0 +1,97 @@ +import { Checkbox, Col, Form, Input, Row, Select } from 'antd'; +import { FormInstance } from 'antd/lib/form'; +import React from 'react'; +import intl from 'react-intl-universal'; +import { observer } from 'mobx-react-lite'; + +import './index.less'; +const Option = Select.Option; + +const innerItemLayout = { + labelCol: { + span: 8, + }, + wrapperCol: { + span: 14, + }, +}; + +interface IProps { + formRef: FormInstance +} + +const formRef = ((props: IProps) => { + const { formRef } = props; + const handleClearTtl = e => { + if(!e.target.checked) { + formRef.resetFields(['ttl_col', '']); + formRef.resetFields(['ttl_duration', '']); + } + }; + return ( + +
+ + + {intl.get('_schema.setTTL')} + + + + {({ getFieldValue }) => { + const properties = getFieldValue('properties') || []; + const ttlRequired = getFieldValue('ttlRequired'); + const ttlOptions = properties.filter(i => + ['int', 'int64', 'timestamp'].includes(i.type), + ); + return ( +
+ + + + + + + + + + + + +
+ ); + }} +
+
+
+ ); +}); + +export default observer(formRef); diff --git a/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/index.less b/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/index.less new file mode 100644 index 00000000..79188b68 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/index.less @@ -0,0 +1,55 @@ +@import '~@appv2/common.less'; +.config-form-group { + padding-bottom: 100px; + + .form-item { + margin-bottom: 30px; + border-bottom: 1px solid @gray; + } + .box-container { + margin-bottom: 28px; + padding: 20px 30px; + background: #FFFFFF; + box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15); + border-radius: 3px; + .ant-form-item { + margin-bottom: 0; + } + .form-header { + margin-top: 20px; + background: @lightGray; + padding: 10px 24px; + .required-item::before { + content: '*'; + padding-right: 3px; + color: red; + } + } + .fields-item { + padding: 14px 20px; + background-color: @lightBlue; + .ant-col > .ant-form-item { + margin-bottom: 0; + } + } + .item-string-length { + position: absolute; + bottom: 0; + } + .input-string-length { + margin-left: 5px; + width: 60px; + } + } + .item-string-length .ant-form-item-with-help .ant-form-item-explain { + white-space: nowrap; + height: 0; + min-height: 0; + } + .label { + font-family: Roboto-Bold; + } + .inline-item label{ + font-family: Roboto-Regular; + } +} \ No newline at end of file diff --git a/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/index.tsx b/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/index.tsx new file mode 100644 index 00000000..43d82d83 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Create/CommonCreate/index.tsx @@ -0,0 +1,105 @@ +import { Button, Col, Form, Input, Row, message } from 'antd'; +import React, { useState } from 'react'; +import intl from 'react-intl-universal'; +import { observer } from 'mobx-react-lite'; +import { nameRulesFn } from '@appv2/config/rules'; +import { useHistory, useParams } from 'react-router-dom'; +import { uniqBy } from 'lodash'; +import GQLCodeMirror from '@appv2/components/GQLCodeMirror'; +import { getTagOrEdgeCreateGQL } from '@appv2/utils/gql'; +import PropertiesForm from './PropertiesForm'; +import TTLForm from './TTLForm'; +import './index.less'; +import { useStore } from '@appv2/stores'; +import { ISchemaType } from '@appv2/interfaces/schema'; + +const formItemLayout = { + labelCol: { + span: 6, + }, + wrapperCol: { + span: 11, + }, +}; + +interface IProps { + createType: ISchemaType +} +const ConfigCreate = (props: IProps) => { + const { createType } = props; + const history = useHistory(); + const { space } = useParams() as {space :string }; + const [loading, setLoading] = useState(false); + const { schema: { createTagOrEdge } } = useStore(); + const [gql, setGql] = useState(''); + const [basicForm] = Form.useForm(); + + const updateGql = () => { + const { name, properties, ttl_col, ttl_duration, comment } = basicForm.getFieldsValue(); + const currentGQL = name + ? getTagOrEdgeCreateGQL({ + type: createType, + name, + properties, + ttl_col, + ttl_duration, + comment, + }) + : ''; + setGql(currentGQL); + }; + const handleCreate = async(values) => { + const { name, properties } = values; + const uniqProperties = uniqBy(properties, 'name'); + if (properties && properties.length !== uniqProperties.length) { + return message.warning(intl.get('schema.uniqProperty')); + } + setLoading(true); + const res = await createTagOrEdge({ + gql, + type: createType + }); + setLoading(false); + if (res.code === 0) { + message.success(intl.get('schema.createSuccess')); + history.push(`/schema/${space}/${createType}/edit/${name}`); + } + }; + return ( +
+
+ + + + + + + + + + + + + + + + + + +
+ + +
+
+ +
+ ); +}; + +export default observer(ConfigCreate); diff --git a/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/PropertiesForm.tsx b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/PropertiesForm.tsx new file mode 100644 index 00000000..4f2af436 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/PropertiesForm.tsx @@ -0,0 +1,192 @@ +import { Button, Checkbox, Col, Form, Modal, Row, message } from 'antd'; +import React, { useEffect, useState } from 'react'; +import intl from 'react-intl-universal'; +import { observer } from 'mobx-react-lite'; +import Icon from '@appv2/components/Icon'; +import { DisplayRow, EditRow } from './PropertiesRow'; +import './index.less'; +import { AlterType, IAlterForm, IProperty, ISchemaType } from '@appv2/interfaces/schema'; +const confirm = Modal.confirm; + +interface IProps { + editType: ISchemaType + initialRequired: boolean; + data: any; + editDisabled: boolean; + onBeforeEdit: (index: number | null) => void; + onEdit: (config: IAlterForm) => void; +} + +interface IEditProperty extends IProperty { + alterType: AlterType +} +const itemLayout = { + wrapperCol: { + span: 20, + }, +}; + +const PropertiesForm = (props: IProps) => { + const { + editType, + initialRequired, + data: { name, properties, ttlConfig }, + editDisabled, + onEdit, + onBeforeEdit } = props; + const [list, setList] = useState(properties); + const [editField, setEditField] = useState(null); + const [editRow, setEditRow] = useState(null); + const [propertyRequired, setPropertyRequired] = useState(false); + const [form] = Form.useForm(); + useEffect(() => { + setList(properties); + setEditRow(null); + setEditField(null); + }, [properties]); + useEffect(() => { + setPropertyRequired(initialRequired); + }, [initialRequired]); + + useEffect(() => { + form.resetFields(); + }, [editField]); + const handleClearProperties = e => { + const clear = e.target.checked; + if(clear) { + handlePropertyAdd(); + } else { + confirm({ + title: intl.get('schema.cancelOperation'), + content: intl.get('schema.cancelPropmt'), + okText: intl.get('common.yes'), + cancelText: intl.get('common.no'), + onOk: () => { + if (properties.length > 0) { + handlePropertyDelete(properties); + } + }, + }); + } + setPropertyRequired(clear); + }; + + const handlePropertyAdd = () => { + const editField: IEditProperty = { + name: '', + type: '', + value: '', + comment: '', + allowNull: true, + fixedLength: '', + alterType: 'ADD', + }; + setList([...list, editField]); + setEditField(editField); + setEditRow(list.length); + onBeforeEdit(list.length); + }; + + + const handlePropertyDelete = async(fields: IProperty[]) => { + onEdit({ + type: editType, + name, + action: 'DROP', + config: { + fields, + }, + }); + }; + + const handleEditBefore = (data: IProperty, index: number) => { + if (ttlConfig && ttlConfig.col === data.name) { + return message.warning(intl.get('schema.fieldDisabled')); + } + const editField = { + ...data, + alterType: 'CHANGE' as AlterType, + }; + setEditField(editField); + setEditRow(index); + onBeforeEdit(index); + }; + + const handleEditCancel = () => { + setEditRow(null); + setEditField(null); + onBeforeEdit(null); + if (editField?.alterType === 'ADD') { + setList(list.slice(0, -1)); + } + }; + + const handlePropertyUpdate = (values) => { + const { name: propertyName, type, value, comment, allowNull, fixedLength } = values; + const { alterType } = editField!; + onEdit({ + type: editType, + name, + action: alterType, + config: { + fields: [{ + name: propertyName, + type, + value, + comment, + allowNull, + fixedLength + }], + }, + }); + }; + return ( +
+ + + {intl.get('_schema.defineFields')} + + + +
+ + + + + + {intl.get('common.propertyName')} + {intl.get('common.dataType')} + {intl.get('common.allowNull')} + {intl.get('common.defaults')} + {intl.get('common.comment')} + + + {list.map((item, index) => { + return editRow === index + ? + : handleEditBefore(item, index)} + />; + })} +
+
+
+ ); +}; + +export default observer(PropertiesForm); diff --git a/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/PropertiesRow.tsx b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/PropertiesRow.tsx new file mode 100644 index 00000000..1e8b0567 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/PropertiesRow.tsx @@ -0,0 +1,181 @@ +import { Button, Checkbox, Col, Form, Input, Popconfirm, Popover, Row, Select } from 'antd'; +import React from 'react'; +import intl from 'react-intl-universal'; +import { AlterType, IProperty } from '@appv2/interfaces/schema'; +import { nameRulesFn, numberRulesFn } from '@appv2/config/rules'; +import { DATA_TYPE, EXPLAIN_DATA_TYPE } from '@appv2/utils/constant'; + +import './index.less'; +const Option = Select.Option; + +interface IEditProperty extends IProperty { + alterType: AlterType +} + +interface IProps { + data: IProperty; + onEditBefore: (data: IProperty) => void; + onDelete: (data: IProperty[]) => void; + disabled: boolean +} +interface IEditProps { + data: IEditProperty | null; + onEditCancel: () => void; +} + +export const DisplayRow = (props: IProps) => { + const { data, onEditBefore, onDelete, disabled } = props; + return + + {data.name} + + + {data.showType} + + + + + + {data.value?.toString()} + + + {data.comment} + + + + { + onDelete([data]); + }} + title={intl.get('common.ask')} + okText={intl.get('common.ok')} + cancelText={intl.get('common.cancel')} + > + + + + ; +}; + +export const EditRow = (props: IEditProps) => { + const { data, onEditCancel } = props; + if(!data) { + return null; + } + const { alterType, name, type, fixedLength, allowNull, value, comment } = data; + return ( + + {({ getFieldValue }) => { + const currentType = getFieldValue('type'); + return ( + + + + + + + + + + + + {currentType === 'fixed_string' && + + } + + + + + + + + + + {EXPLAIN_DATA_TYPE.includes(type) ? ( + + + + + + ) : ( + + + + )} + + + + + + + + + + + + + + + ); + }} + + ); +}; \ No newline at end of file diff --git a/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/TTLForm.tsx b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/TTLForm.tsx new file mode 100644 index 00000000..90a9a358 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/TTLForm.tsx @@ -0,0 +1,258 @@ +import { Button, Checkbox, Col, Form, Input, Modal, Popconfirm, Row, Select, message } from 'antd'; +import React, { useEffect, useState } from 'react'; +import intl from 'react-intl-universal'; +import { observer } from 'mobx-react-lite'; +import { IAlterForm, IProperty, ISchemaType } from '@appv2/interfaces/schema'; + +import './index.less'; + +const confirm = Modal.confirm; +const Option = Select.Option; +const innerItemLayout = { + labelCol: { + span: 8, + }, + wrapperCol: { + span: 14, + }, +}; + +interface IProps { + editType: ISchemaType + initialRequired: boolean; + editDisabled: boolean; + onBeforeEdit: (type?: null) => void; + onEdit: (config: IAlterForm) => void; + checkIndex: () => Promise; + data: { + name: string, + properties: IProperty[], + ttlConfig: { + duration: string, + col: string + } + } +} + +const formRef = ((props: IProps) => { + const { + editType, + initialRequired, + editDisabled, + checkIndex, + data, + onEdit, + onBeforeEdit } = props; + const { name, properties, ttlConfig: { col, duration } } = data; + const [ttlRequired, setTtlRequired] = useState(false); + const [editConfig, setEditConfig] = useState(null); + const [isEdit, setIsEdit] = useState(false); + const [ttlOptions, setTtlOptions] = useState([]); + const [form] = Form.useForm(); + + useEffect(() => { + setTtlRequired(initialRequired); + }, [initialRequired]); + + useEffect(() => { + setIsEdit(false); + }, [data]); + + useEffect(() => { + if(isEdit) { + const list = properties.filter(i => + ['int', 'int64', 'timestamp'].includes(i.type), + ); + setTtlOptions(list); + } + }, [isEdit]); + useEffect(() => { + form.resetFields(); + }, [editConfig]); + const handleClearTtl = async e => { + const clear = e.target.checked; + if(clear) { + const hasIndex = await checkIndex(); + if (hasIndex) { + return message.warning(intl.get('schema.indexExist')); + } + setTtlRequired(true); + handleEditBefore(); + } else { + confirm({ + title: intl.get('schema.cancelOperation'), + content: intl.get('schema.cancelPropmt'), + okText: intl.get('common.yes'), + cancelText: intl.get('common.no'), + onOk: handleTtlDelete, + }); + } + }; + + const handleTtlDelete = () => { + onEdit({ + type: editType, + name, + action: 'TTL', + config: { + ttl: { + col: '', + }, + }, + }); + }; + const handleTtlUpdate = (values) => { + onEdit({ + type: editType, + name, + action: 'TTL', + config: { + ttl: values, + }, + }); + }; + + const handleEditBefore = () => { + setEditConfig({ + col, + duration + }); + setIsEdit(true); + onBeforeEdit(); + }; + + const handleEditCancel = () => { + onBeforeEdit(null); + setIsEdit(false); + if(!col) { + setTtlRequired(false); + } + }; + return ( +
+ + + {intl.get('_schema.setTTL')} + + + {!isEdit && +
+ + + + {col} + + + + + {duration === '0' && col === '' ? '' : duration } + + + {col !== '' && + + + + + + + } + +
+
} + + {isEdit && +
+ + + + + + + + + + + + + + + + + + +
+
} +
+ ); +}); + +export default observer(formRef); diff --git a/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/index.less b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/index.less new file mode 100644 index 00000000..c3fc99de --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/index.less @@ -0,0 +1,61 @@ +@import '~@appv2/common.less'; +.config-edit-group { + padding-bottom: 100px; + + .form-item { + margin-bottom: 30px; + border-bottom: 1px solid @gray; + } + .box-container { + margin-bottom: 28px; + padding: 20px 30px; + background: #FFFFFF; + box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.15); + border-radius: 3px; + .ant-form-item { + margin-bottom: 0; + } + .form-header { + margin-top: 20px; + background: @lightGray; + padding: 10px 24px; + .required-item::before { + content: '*'; + padding-right: 3px; + color: red; + } + } + .fields-item { + padding: 14px 20px; + background-color: @lightBlue; + display: flex; + align-items: center; + .ant-col > .ant-form-item { + margin-bottom: 0; + } + } + .item-string-length { + position: absolute; + bottom: 0; + } + .input-string-length { + margin-left: 5px; + width: 60px; + } + .operations { + display: flex; + align-items: end; + } + } + .item-string-length .ant-form-item-with-help .ant-form-item-explain { + white-space: nowrap; + height: 0; + min-height: 0; + } + .label { + font-family: Roboto-Bold; + } + .inline-item label{ + font-family: Roboto-Regular; + } +} \ No newline at end of file diff --git a/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/index.tsx b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/index.tsx new file mode 100644 index 00000000..f0a656b4 --- /dev/null +++ b/app-v2/pages/Schema/SchemaConfig/Edit/CommonEdit/index.tsx @@ -0,0 +1,213 @@ +import { Button, Col, Form, Input, Row, message } from 'antd'; +import React, { useEffect, useState } from 'react'; +import intl from 'react-intl-universal'; +import { observer } from 'mobx-react-lite'; +import { useParams } from 'react-router-dom'; +import PropertiesForm from './PropertiesForm'; +import TTLForm from './TTLForm'; +import './index.less'; +import { useStore } from '@appv2/stores'; +import { convertBigNumberToString } from '@appv2/utils/function'; +import { IAlterForm, IProperty, ISchemaType } from '@appv2/interfaces/schema'; +import { trackPageView } from '@appv2/utils/stat'; + +const formItemLayout = { + labelCol: { + span: 6, + }, + wrapperCol: { + span: 11, + }, +}; + +interface IPropertyItem { + Default: any; + Field: string; + Null: string; + Type: string; + Comment: string; +} + +interface IData { + name: string; + comment: string; + properties: IProperty[], + ttlConfig: { + duration: string, + col: string + } +} +interface IProps { + editType: ISchemaType +} +const ConfigEdit = (props: IProps) => { + const { editType } = props; + const { name: editName } = useParams() as {name: string }; + const [editKey, setEditKey] = useState(null); + const { schema: { getTagOrEdgeDetail, getTagOrEdgeInfo, alterField, getIndexTree } } = useStore(); + const [tempComment, setTempComment] = useState(''); + const [data, setData] = useState({ + name: '', + comment: '', + properties: [], + ttlConfig: { + col: '', + duration: '' + } + } as IData); + const [ttlRequired, setTtlRequired] = useState(false); + const [propertiesRequired, setPropertiesRequired] = useState(false); + useEffect(() => { + trackPageView(`/schema/config/${editType}/edit`); + getDetails(); + }, []); + const getDetails = async() => { + const { code, data } = await getTagOrEdgeDetail(editType, editName); + const { code: propCode, data: propData } = await getTagOrEdgeInfo( + editType, + editName, + ); + if (code === 0) { + const key = editType === 'tag' ? 'Create Tag' : 'Create Edge'; + const info = data.tables[0][key]; + const fieldInfo = propCode === 0 ? propData.tables : []; + handleData(info, fieldInfo); + } + }; + + const handleData = (data: string, fieldInfo: IPropertyItem[]) => { + const reg = /CREATE (?:TAG|EDGE)\s`\w+`\s\((.*)\)\s+(ttl_duration = \d+),\s+(ttl_col = "\w*")(, comment = ".*")?/gm; + const str = data.replace(/[\r\n]/g, ' '); + const infoList = reg.exec(str) || []; + const properties: IProperty[] = fieldInfo.map(i => ({ + name: i.Field, + showType: i.Type, + type: i.Type.startsWith('fixed_string') ? 'fixed_string' : i.Type, + allowNull: i.Null === 'YES', + comment: i.Comment === '_EMPTY_' ? '' : i.Comment, + value: i.Default === '_EMPTY_' ? '' : convertBigNumberToString(i.Default), + fixedLength: i.Type.startsWith('fixed_string') + ? i.Type.replace(/[fixed_string(|)]/g, '') + : '', + })) || []; + const duration = infoList[2]?.split(' = ')[1] || ''; + const col = infoList[3]?.split(' = ')[1].replace(/"/g, '') || ''; + const comment = infoList[4]?.split(' = ')[1].slice(1, -1) || ''; + const propertiesRequired = properties.length > 0; + const ttlRequired = col !== ''; + setPropertiesRequired(propertiesRequired); + setTtlRequired(ttlRequired); + setData({ + name: editName, + comment, + properties, + ttlConfig: { + col, + duration + } + }); + }; + + const handleCommentEditStart = () => { + setEditKey('comment'); + setTempComment(data.comment); + }; + + const handleAlter = async(config: IAlterForm) => { + const res = await alterField(config); + if (res.code === 0) { + message.success(intl.get('common.updateSuccess')); + await getDetails(); + setEditKey(null); + } + }; + const handleCommentUpdate = async() => { + handleAlter({ + type: editType, + name: editName, + action: 'COMMENT', + config: { + comment: tempComment, + }, + }); + }; + + const checkIndex = async() => { + const res = (await getIndexTree(editType)) || []; + const hasIndex = res.some(i => i.name === editName); + return hasIndex; + }; + return ( +
+
+ + + + + {editName} + + + + + {editKey !== 'comment' ? ( + <> + {data.comment} + + + ) : ( + <> + setTempComment(e.target.value)} + /> + + + + )} + + + + + +
+ setEditKey(index !== null ? `properties[${index}]` : null)} + initialRequired={propertiesRequired} + onEdit={handleAlter} + data={data} /> + setEditKey(type === null ? null : 'ttl')} + /> +
+ ); +}; + +export default observer(ConfigEdit); diff --git a/app-v2/pages/Schema/SchemaListLayout/index.less b/app-v2/pages/Schema/SchemaConfig/List/CommonLayout/index.less similarity index 100% rename from app-v2/pages/Schema/SchemaListLayout/index.less rename to app-v2/pages/Schema/SchemaConfig/List/CommonLayout/index.less diff --git a/app-v2/pages/Schema/SchemaListLayout/index.tsx b/app-v2/pages/Schema/SchemaConfig/List/CommonLayout/index.tsx similarity index 95% rename from app-v2/pages/Schema/SchemaListLayout/index.tsx rename to app-v2/pages/Schema/SchemaConfig/List/CommonLayout/index.tsx index 13c62714..c1459194 100644 --- a/app-v2/pages/Schema/SchemaListLayout/index.tsx +++ b/app-v2/pages/Schema/SchemaConfig/List/CommonLayout/index.tsx @@ -16,7 +16,7 @@ interface IProps { children?: any; type: string } -const SchemaListLayout = (props: IProps) => { +const CommonLayout = (props: IProps) => { const { onSearch, data, columns, loading, renderExpandInfo, children, type } = props; const { space } = useParams() as { space :string, module?: string }; const [expandKeys, setExpandKeys] = useState([]); @@ -57,4 +57,4 @@ const SchemaListLayout = (props: IProps) => { ); }; -export default observer(SchemaListLayout); +export default observer(CommonLayout); diff --git a/app-v2/pages/Schema/Edge/index.tsx b/app-v2/pages/Schema/SchemaConfig/List/Edge/index.tsx similarity index 96% rename from app-v2/pages/Schema/Edge/index.tsx rename to app-v2/pages/Schema/SchemaConfig/List/Edge/index.tsx index b83fee5e..91752953 100644 --- a/app-v2/pages/Schema/Edge/index.tsx +++ b/app-v2/pages/Schema/SchemaConfig/List/Edge/index.tsx @@ -7,7 +7,8 @@ import { observer } from 'mobx-react-lite'; import { useStore } from '@appv2/stores'; import { sortByFieldAndFilter } from '@appv2/utils/function'; import { IEdge } from '@appv2/interfaces/schema'; -import SchemaListLayout from '../SchemaListLayout'; +import Cookie from 'js-cookie'; +import CommonLayout from '../CommonLayout'; function renderEdgeInfo(edge: IEdge) { const fieldsColumn = [ @@ -110,7 +111,7 @@ const EdgeList = () => { } }, }, - ], [edgeList]); + ], [edgeList, Cookie.get('lang')]); useEffect(() => { getData(); }, [space]); @@ -121,7 +122,7 @@ const EdgeList = () => { list: edgeList, })); }, [edgeList, searchVal]); - return { } }, }, - ], [indexType]); + ], [indexType, Cookie.get('lang')]); const handleTabChange = e => { setIndexType(e.target.value); @@ -128,7 +129,7 @@ const IndexList = () => { list: indexList, })); }, [indexList, searchVal]); - return { {intl.get('common.edge')}
- ; + ; }; export default observer(IndexList); diff --git a/app-v2/pages/Schema/Search/index.less b/app-v2/pages/Schema/SchemaConfig/List/Search/index.less similarity index 100% rename from app-v2/pages/Schema/Search/index.less rename to app-v2/pages/Schema/SchemaConfig/List/Search/index.less diff --git a/app-v2/pages/Schema/Search/index.tsx b/app-v2/pages/Schema/SchemaConfig/List/Search/index.tsx similarity index 100% rename from app-v2/pages/Schema/Search/index.tsx rename to app-v2/pages/Schema/SchemaConfig/List/Search/index.tsx diff --git a/app-v2/pages/Schema/Tag/index.tsx b/app-v2/pages/Schema/SchemaConfig/List/Tag/index.tsx similarity index 96% rename from app-v2/pages/Schema/Tag/index.tsx rename to app-v2/pages/Schema/SchemaConfig/List/Tag/index.tsx index 24b64fee..f6365d56 100644 --- a/app-v2/pages/Schema/Tag/index.tsx +++ b/app-v2/pages/Schema/SchemaConfig/List/Tag/index.tsx @@ -7,7 +7,8 @@ import { observer } from 'mobx-react-lite'; import { useStore } from '@appv2/stores'; import { sortByFieldAndFilter } from '@appv2/utils/function'; import { ITag } from '@appv2/interfaces/schema'; -import SchemaListLayout from '../SchemaListLayout'; +import Cookie from 'js-cookie'; +import CommonLayout from '../CommonLayout'; function renderTagInfo(tag: ITag) { const fieldsColumn = [ @@ -112,7 +113,7 @@ const TagList = () => { ); }, }, - ], [tagList]); + ], [tagList, Cookie.get('lang')]); useEffect(() => { getData(); @@ -125,7 +126,7 @@ const TagList = () => { })); }, [tagList, searchVal]); - return { +const SchemaConfig = () => { const history = useHistory(); const [tab, setTab] = useState('tag'); - const { space, type } = useParams() as {space :string, type: string }; + const { space, type, action } = useParams() as {space :string, type: string, action: string }; const { schema } = useStore(); const { spaces, getSpaces, switchSpace } = schema; - const routes = useMemo(() => [ - { - path: '/schema', - breadcrumbName: intl.get('_schema.spaceList'), - }, - { - path: '#', - breadcrumbName: space, - }, - ], [space]); + const routes = useMemo(() => { + if(action === 'list') { + return [ + { + path: '/schema', + breadcrumbName: intl.get('_schema.spaceList'), + }, + { + path: '#', + breadcrumbName: space, + }, + ]; + } else { + return [ + { + path: '/schema', + breadcrumbName: intl.get('_schema.spaceList'), + }, + { + path: `/schema/${space}/${type}/list`, + breadcrumbName: intl.get('_schema.configTypeList', { space, type: intl.get(`common.${type}`) }), + }, + { + path: '#', + breadcrumbName: intl.get('_schema.configTypeAction', { + action: intl.get(`common.${action}`), + type: intl.get(`common.${type}`) }), + }, + ]; + } + }, [space, action, Cookie.get('lang')]); useEffect(() => { setTab(type); if(spaces.length === 0) { @@ -50,16 +74,16 @@ const SpaceConfig = () => {
{intl.get('common.currentSpace')} - handleUpdateSpace(value)}> {spaces.map(space => ( ))} - + : {space}}
} />
-
+ {action === 'list' &&
{ {intl.get('common.index')} {intl.get('common.statistics')} -
+
} <> { exact={true} component={IndexList} /> - {/* } /> ( - - )} + render={() => } /> - } /> ( - - )} + render={() => } /> - { ); }; -export default observer(SpaceConfig); +export default observer(SchemaConfig); diff --git a/app-v2/pages/Schema/SpaceCreate/index.less b/app-v2/pages/Schema/SpaceCreate/index.less index 7a1c596b..68787686 100644 --- a/app-v2/pages/Schema/SpaceCreate/index.less +++ b/app-v2/pages/Schema/SpaceCreate/index.less @@ -5,6 +5,12 @@ .space-form { border-bottom: 1px solid @gray; padding-bottom: 20px; + label { + font-family: Roboto-Bold; + } + .optional-item { + color: @gray; + } .item-string-length { position: absolute; bottom: 0; @@ -14,22 +20,4 @@ } } } - .footer { - position: fixed; - bottom: 0; - z-index: 10; - width: 100%; - height: 98px; - display: flex; - align-items: center; - justify-content: center; - background: #FFFFFF; - box-shadow: 0px -4px 4px rgba(0, 0, 0, 0.1); - button { - width: 236px; - &:not(:last-child) { - margin-right: 50px; - } - } - } } \ No newline at end of file diff --git a/app-v2/pages/Schema/SpaceCreate/index.tsx b/app-v2/pages/Schema/SpaceCreate/index.tsx index 4a3d0f94..e68bab56 100644 --- a/app-v2/pages/Schema/SpaceCreate/index.tsx +++ b/app-v2/pages/Schema/SpaceCreate/index.tsx @@ -12,6 +12,7 @@ import intl from 'react-intl-universal'; import './index.less'; import { useHistory } from 'react-router-dom'; import FormItem from 'antd/lib/form/FormItem'; +import Cookie from 'js-cookie'; const Option = Select.Option; function getVidType(type: string, length?: string) { @@ -49,7 +50,7 @@ const SpaceCreate = () => { path: '#', breadcrumbName: intl.get('_schema.createSpace'), }, - ], []); + ], [Cookie.get('lang')]); const handleCreate = () => { setLoading(true); @@ -173,7 +174,7 @@ const SpaceCreate = () => {
-
+
diff --git a/app-v2/static/fonts/iconfont.js b/app-v2/static/fonts/iconfont.js index 8e352b27..4fa36635 100644 --- a/app-v2/static/fonts/iconfont.js +++ b/app-v2/static/fonts/iconfont.js @@ -1 +1 @@ -!function(a){var o,h,t,l,e,c='',v=(v=document.getElementsByTagName("script"))[v.length-1].getAttribute("data-injectcss"),m=function(a,o){o.parentNode.insertBefore(a,o)};if(v&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function i(){e||(e=!0,t())}function n(){try{l.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}i()}o=function(){var a,o=document.createElement("div");o.innerHTML=c,c=null,(o=o.getElementsByTagName("svg")[0])&&(o.setAttribute("aria-hidden","true"),o.style.position="absolute",o.style.width=0,o.style.height=0,o.style.overflow="hidden",o=o,(a=document.body).firstChild?m(o,a.firstChild):a.appendChild(o))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(h=function(){document.removeEventListener("DOMContentLoaded",h,!1),o()},document.addEventListener("DOMContentLoaded",h,!1)):document.attachEvent&&(t=o,l=a.document,e=!1,n(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,i())})}(window); \ No newline at end of file +!function(a){var h,o,t,l,e,v='',c=(c=document.getElementsByTagName("script"))[c.length-1].getAttribute("data-injectcss"),m=function(a,h){h.parentNode.insertBefore(a,h)};if(c&&!a.__iconfont__svg__cssinject__){a.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}function i(){e||(e=!0,t())}function n(){try{l.documentElement.doScroll("left")}catch(a){return void setTimeout(n,50)}i()}h=function(){var a,h=document.createElement("div");h.innerHTML=v,v=null,(h=h.getElementsByTagName("svg")[0])&&(h.setAttribute("aria-hidden","true"),h.style.position="absolute",h.style.width=0,h.style.height=0,h.style.overflow="hidden",h=h,(a=document.body).firstChild?m(h,a.firstChild):a.appendChild(h))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(h,0):(o=function(){document.removeEventListener("DOMContentLoaded",o,!1),h()},document.addEventListener("DOMContentLoaded",o,!1)):document.attachEvent&&(t=h,l=a.document,e=!1,n(),l.onreadystatechange=function(){"complete"==l.readyState&&(l.onreadystatechange=null,i())})}(window); \ No newline at end of file diff --git a/app-v2/stores/import.ts b/app-v2/stores/import.ts index 724bee8b..6aecd8a5 100644 --- a/app-v2/stores/import.ts +++ b/app-v2/stores/import.ts @@ -98,10 +98,10 @@ export class ImportStore { configIndex: number; }) => { const { schema } = this.rootStore; - const { getTagInfo, getTagDetail } = schema; + const { getTagOrEdgeInfo, getTagOrEdgeDetail } = schema; const { tag, tagIndex, configIndex } = payload; - const { code, data } = await getTagInfo(tag); - const createTag = await getTagDetail(tag); + const { code, data } = await getTagOrEdgeInfo('tag', tag); + const createTag = await getTagOrEdgeDetail('tag', tag); const defaultValueFields: any[] = []; if (!!createTag) { const res = @@ -147,9 +147,9 @@ export class ImportStore { this.edgesConfig = this.edgesConfig.splice(index, 1); } else { const { schema } = this.rootStore; - const { getEdgeInfo, getEdgeDetail, spaceVidType } = schema; - const { code, data } = await getEdgeInfo(edgeType); - const createTag = await getEdgeDetail(edgeType); + const { getTagOrEdgeInfo, getTagOrEdgeDetail, spaceVidType } = schema; + const { code, data } = await getTagOrEdgeInfo('edge', edgeType); + const createTag = await getTagOrEdgeDetail('edge', edgeType); const defaultValueFields: any[] = []; if (!!createTag) { const res = diff --git a/app-v2/stores/schema.ts b/app-v2/stores/schema.ts index 104fc826..21787865 100644 --- a/app-v2/stores/schema.ts +++ b/app-v2/stores/schema.ts @@ -1,12 +1,11 @@ import { action, makeAutoObservable, observable } from 'mobx'; import service from '@appv2/config/service'; -import { AlterType, IAlterConfig, IEdge, IIndexList, IProperty, ISpace, ITag, ITree, IndexType } from '@appv2/interfaces/schema'; +import { IAlterForm, IEdge, IIndexList, ISchemaType, ISpace, ITag, ITree, IndexType } from '@appv2/interfaces/schema'; import { handleKeyword } from '@appv2/utils/function'; import { findIndex } from 'lodash'; import { getAlterGQL, getIndexCreateGQL, - getTagOrEdgeCreateGQL, } from '@appv2/utils/gql'; export class SchemaStore { @@ -183,28 +182,11 @@ export class SchemaStore { } } - getEdgeInfo = async(edge: string) => { - const { code, data } = (await service.execNGQL({ - gql: 'desc edge' + '`' + edge + '`;', - })) as any; - return { code, data }; - } - - getEdgeDetail = async(name: string) => { - const gql = `SHOW CREATE EDGE ${handleKeyword(name)}`; - const { code, data, message } = (await service.execNGQL({ - gql, - })) as any; - return { code, data, message }; - } - getEdgeTypesFields = async(payload: { edgeTypes: any[] }) => { const { edgeTypes } = payload; await Promise.all( edgeTypes.map(async item => { - const { code, data } = await this.getEdgeInfo( - item, - ); + const { code, data } = await this.getTagOrEdgeInfo('edge', item); if (code === 0) { const edgeFields = data.tables.map(item => item.Field); this.addEdgesName({ @@ -233,9 +215,7 @@ export class SchemaStore { name: item, fields: [], }; - const { code, data } = await this.getEdgeInfo( - item, - ); + const { code, data } = await this.getTagOrEdgeInfo('edge', item); if (code === 0) { edge.fields = data.tables; } @@ -263,30 +243,6 @@ export class SchemaStore { return { code, data, message }; } - createEdge = async(payload: { - name: string; - comment?: string; - fields?: IProperty[]; - ttlConfig?: { - ttl_col: string; - ttl_duration: number; - }; - }) => { - const gql = await getTagOrEdgeCreateGQL({ ...payload, type: 'EDGE' }); - const { code, data, message } = (await service.execNGQL( - { - gql, - }, - { - trackEventConfig: { - category: 'schema', - action: 'create_edge', - }, - }, - )) as any; - return { code, data, message }; - } - addEdgesName = async(payload: any) => { const { edgeType, edgeFields } = payload; const index = findIndex(this.edgesFields, edgeType); @@ -317,7 +273,7 @@ export class SchemaStore { const { tags } = payload; await Promise.all( tags.map(async item => { - const { code, data } = await this.getTagInfo(item); + const { code, data } = await this.getTagOrEdgeInfo('tag', item); if (code === 0) { const tagFields = data.tables.map(item => ({ field: item.Field, @@ -329,21 +285,6 @@ export class SchemaStore { ); }; - getTagInfo = async(tag: string) => { - const { code, data } = (await service.execNGQL({ - gql: 'desc tag ' + '`' + tag + '`;', - })) as any; - return { code, data }; - } - - getTagDetail = async(name: string) => { - const gql = `SHOW CREATE TAG ${handleKeyword(name)}`; - const { code, data, message } = (await service.execNGQL({ - gql, - })) as any; - return { code, data, message }; - } - getTagList = async() => { const tags = await this.getTags(); if (tags) { @@ -354,9 +295,7 @@ export class SchemaStore { name: item, fields: [], }; - const { code, data } = await this.getTagInfo( - item, - ); + const { code, data } = await this.getTagOrEdgeInfo('tag', item); if (code === 0) { tag.fields = data.tables; } @@ -384,16 +323,11 @@ export class SchemaStore { return { code, data, message }; } - createTag = async(payload: { - name: string; - comment?: string; - fields?: IProperty[]; - ttlConfig?: { - ttl_col: string; - ttl_duration: number; - }; + createTagOrEdge = async(payload: { + type: ISchemaType, + gql: string }) => { - const gql = await getTagOrEdgeCreateGQL({ ...payload, type: 'TAG' }); + const { type, gql } = payload; const { code, data, message } = (await service.execNGQL( { gql, @@ -401,19 +335,14 @@ export class SchemaStore { { trackEventConfig: { category: 'schema', - action: 'create_tag', + action: `create_${type.toLowerCase()}`, }, }, )) as any; return { code, data, message }; } - alterField = async(payload: { - type: IndexType; - name: string; - action: AlterType; - config: IAlterConfig; - }) => { + alterField = async(payload: IAlterForm) => { const gql = getAlterGQL(payload); const { code, data, message } = (await service.execNGQL( { @@ -429,6 +358,22 @@ export class SchemaStore { return { code, data, message }; } + getTagOrEdgeDetail = async(type: ISchemaType, name: string) => { + const gql = `show create ${type} ${handleKeyword(name)}`; + const { code, data, message } = (await service.execNGQL({ + gql, + })) as any; + return { code, data, message }; + } + + getTagOrEdgeInfo = async(type: ISchemaType, name: string) => { + const gql = `desc ${type} ` + '`' + name + '`;'; + const { code, data } = (await service.execNGQL({ + gql, + })) as any; + return { code, data }; + } + // indexes getIndexes = async(type: IndexType) => { const { code, data } = (await service.execNGQL({ @@ -458,7 +403,7 @@ export class SchemaStore { `, })) as any; if (code === 0) { - const _type = type === 'TAG' ? 'Tag' : 'Edge'; + const _type = type === 'tag' ? 'tag' : 'edge'; const res = data.tables[0]?.[`Create ${_type} Index`] || ''; const reg = /comment = "(.+)"/g; const result = reg.exec(res); @@ -520,7 +465,7 @@ export class SchemaStore { }] }] }] */ - const key = type === 'TAG' ? 'tagIndexTree' : 'edgeIndexTree'; + const key = type === 'tag' ? 'tagIndexTree' : 'edgeIndexTree'; this.update({ [key]: tree, }); diff --git a/app-v2/utils/gql.ts b/app-v2/utils/gql.ts index 43ecb914..12d4f112 100644 --- a/app-v2/utils/gql.ts +++ b/app-v2/utils/gql.ts @@ -1,6 +1,5 @@ import { handleKeyword, handleVidStringName } from '@appv2/utils/function'; -import { AlterType, IAlterConfig, IProperty, IndexType } from '@appv2/interfaces/schema'; - +import { IAlterForm, IProperty, ISchemaType, IndexType } from '@appv2/interfaces/schema'; export const getExploreMatchGQL = (params: { selectVertexes: any[]; @@ -108,18 +107,16 @@ export const getSpaceCreateGQL = (params: { }; export const getTagOrEdgeCreateGQL = (params: { - type: 'TAG' | 'EDGE'; + type: ISchemaType; name: string; comment?: string; - fields?: IProperty[]; - ttlConfig?: { - ttl_col: string; - ttl_duration: number; - }; + properties?: IProperty[]; + ttl_col?: string; + ttl_duration?: number; }) => { - const { type, name, fields, ttlConfig, comment } = params; - const fieldsStr = fields - ? fields + const { type, name, properties, ttl_col, ttl_duration, comment } = params; + const propertiesStr = properties + ? properties .map(item => { let valueStr = ''; if (item.value) { @@ -155,24 +152,19 @@ export const getTagOrEdgeCreateGQL = (params: { }) .join(', ') : ''; - const ttlStr = ttlConfig - ? `TTL_DURATION = ${ttlConfig.ttl_duration || - ''}, TTL_COL = "${ttlConfig.ttl_col || ''}"` + const ttlStr = ttl_col + ? `TTL_DURATION = ${ttl_duration || + ''}, TTL_COL = "${ttl_col || ''}"` : ''; const gql = `CREATE ${type} ${handleKeyword(name)} ${ - fieldsStr.length > 0 ? `(${fieldsStr})` : '()' + propertiesStr.length > 0 ? `(${propertiesStr})` : '()' } ${ttlStr} ${ comment ? `${ttlStr.length > 0 ? ', ' : ''}COMMENT = "${comment}"` : '' }`; return gql; }; -export const getAlterGQL = (params: { - type: IndexType; - name: string; - action: AlterType; - config: IAlterConfig; -}) => { +export const getAlterGQL = (params: IAlterForm) => { let content; const { type, name, action, config } = params; if (action === 'TTL' && config.ttl) { diff --git a/app-v2/utils/http.ts b/app-v2/utils/http.ts index ee499618..eb2eb60c 100644 --- a/app-v2/utils/http.ts +++ b/app-v2/utils/http.ts @@ -4,7 +4,7 @@ import JSONBigint from 'json-bigint'; import intl from 'react-intl-universal'; import { trackEvent } from './stat'; -import { store } from '#app/store'; +import { getRootStore } from '@appv2/stores'; const service = axios.create({ @@ -43,9 +43,7 @@ service.interceptors.response.use( errMsg.includes('an existing connection was forcibly closed')) ) { message.warning(intl.get('warning.connectError')); - store.dispatch({ - type: 'nebula/asyncClearConfigServer', - }); + getRootStore().global.logout(); } else if (code === -1 && errMsg) { message.warning(errMsg); } diff --git a/config/webpack.base.ts b/config/webpack.base.ts index 1065b389..59f582f9 100644 --- a/config/webpack.base.ts +++ b/config/webpack.base.ts @@ -33,7 +33,7 @@ const commonConfig: Configuration = { 'table-header-color': '#465B7A', 'table-header-cell-split-color': '#E9EDEF', 'layout-body-background': '#F8F8F8', - 'font-family': 'Robot, sans-serif', + 'font-family': 'Robot-Regular, sans-serif', 'height-base': '38px', }, },