From 0ac652c5d97cfceff5945b6aaec59aa923a0fecc Mon Sep 17 00:00:00 2001
From: tristanliu <37809633+sevenliu1896@users.noreply.github.com>
Date: Mon, 31 Jul 2023 11:23:37 +0800
Subject: [PATCH] semantic-fe  visual modeling  pr (#21)

* [improvement][semantic-fe] Added an editing component to set filtering rules for Q&A. Now, the SQL editor will be accompanied by a list for display and control, to resolve ambiguity when using comma-separated values.
[improvement][semantic-fe] Improved validation logic and prompt copywriting for data source/dimension/metric editing and creation.
[improvement][semantic-fe] Improved user experience for visual modeling. Now, when using the legend to control the display/hide of data sources and their associated metric dimensions, the canvas will be re-layout based on the activated data source in the legend.

* [improvement][semantic-fe] Submitted a new version of the visual modeling tool.
[improvement][semantic-fe] Fixed an issue with the initialization of YoY and MoM metrics in Q&A settings.
[improvement][semantic-fe] Added a version field to the database settings.
[improvement][semantic-fe] 1. Added the ability to set YoY and MoM metrics in Q&A settings.2. Moved dimension value editing from the dimension editing window to the dimension list.

---------

Co-authored-by: tristanliu <tristanliu@tencent.com>
---
 .../src/components/StandardFormRow/index.less |  96 +++
 .../src/components/StandardFormRow/index.tsx  |  43 +
 .../src/components/TagSelect/index.less       |  33 +
 .../src/components/TagSelect/index.tsx        | 191 +++++
 webapp/packages/supersonic-fe/src/global.less |  15 +-
 .../src/pages/SemanticModel/ChatSetting.tsx   |  10 +-
 .../Metric/components/MetricFilter.tsx        | 109 +++
 .../pages/SemanticModel/Metric/constants.ts   |   0
 .../src/pages/SemanticModel/Metric/data.d.ts  |   0
 .../src/pages/SemanticModel/Metric/index.tsx  | 196 +++++
 .../src/pages/SemanticModel/Metric/model.ts   |   0
 .../src/pages/SemanticModel/Metric/service.ts |   0
 .../src/pages/SemanticModel/Metric/style.less |  10 +
 .../src/pages/SemanticModel/Metric/utils.ts   |   0
 .../pages/SemanticModel/ProjectManager.tsx    |  12 +-
 .../XflowJsonSchemaFormDrawerForm.tsx         |   2 +-
 .../SemanticGraph/components/ContextMenu.tsx  |  22 +-
 .../components/DeleteConfirmModal.tsx         |  20 +-
 .../SemanticGraph/components/GraphToolBar.tsx |  66 ++
 .../components/NodeInfoDrawer.tsx             | 292 +++++++
 .../SemanticGraph/components/ToolBar.tsx      |  65 +-
 .../SemanticModel/SemanticGraph/index.tsx     | 214 ++++-
 .../SemanticModel/SemanticGraph/style.less    | 776 +-----------------
 .../SemanticModel/SemanticGraph/utils.ts      | 129 ++-
 .../SemanticModel/SemanticGraphCanvas.tsx     |  10 +-
 .../components/ClassDataSourceTable.tsx       |  88 +-
 .../components/ClassDataSourceTypeModal.tsx   | 107 ++-
 .../components/ClassDataSourceTypeModal1.tsx  | 101 +++
 .../components/ClassDimensionTable.tsx        |  42 +-
 .../components/ClassMetricTable.tsx           |  33 +-
 .../components/CommonEditList/TextArea.tsx    | 166 ++++
 .../components/CommonEditTable.tsx            |  15 +-
 .../Database/DatabaseCreateForm.tsx           |  23 +-
 .../components/DimensionInfoModal.tsx         | 115 ++-
 .../components/DimensionValueSettingModal.tsx | 156 ++++
 .../components/Entity/DefaultSettingForm.tsx  |  78 +-
 .../Entity/DimensionAndMetricVisibleModal.tsx |  29 +-
 .../Entity/DimensionMetricVisibleForm.tsx     |   6 +-
 .../Entity/DimensionValueSettingForm.tsx      |   3 +-
 .../components/Entity/EntityCreateForm.tsx    |  35 +-
 .../components/Entity/EntitySection.tsx       |  23 +-
 .../Entity/EntitySettingSection.tsx           |  69 ++
 .../Entity/RecommendedQuestionsSection.tsx    |  87 ++
 .../SemanticModel/components/Entity/utils.ts  |  11 +-
 .../components/MetricInfoCreateForm.tsx       |   8 +-
 .../SemanticModel/components/TransTypeTag.tsx |  10 +-
 .../pages/SemanticModel/components/style.less |  21 +-
 .../src/pages/SemanticModel/data.d.ts         |  19 +-
 .../src/pages/SemanticModel/enum.ts           |   5 +
 .../src/pages/SemanticModel/service.ts        |   2 +-
 50 files changed, 2375 insertions(+), 1188 deletions(-)
 create mode 100644 webapp/packages/supersonic-fe/src/components/StandardFormRow/index.less
 create mode 100644 webapp/packages/supersonic-fe/src/components/StandardFormRow/index.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/components/TagSelect/index.less
 create mode 100644 webapp/packages/supersonic-fe/src/components/TagSelect/index.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/constants.ts
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/data.d.ts
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/model.ts
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/service.ts
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/utils.ts
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphToolBar.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditList/TextArea.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionValueSettingModal.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySettingSection.tsx
 create mode 100644 webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx

diff --git a/webapp/packages/supersonic-fe/src/components/StandardFormRow/index.less b/webapp/packages/supersonic-fe/src/components/StandardFormRow/index.less
new file mode 100644
index 000000000..63d930c75
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/components/StandardFormRow/index.less
@@ -0,0 +1,96 @@
+@import '~antd/es/style/themes/default.less';
+
+.standardFormRow {
+  display: flex;
+  width: 100%;
+  margin-bottom: 12px;
+  // padding-bottom: 16px;
+  // border-bottom: 1px dashed @border-color-split;
+  :global {
+    .ant-form-item,
+    .ant-legacy-form-item {
+      margin-right: 24px;
+    }
+    .ant-form-item-label,
+    .ant-legacy-form-item-label {
+      label {
+        margin-right: 0;
+        color: @text-color;
+      }
+    }
+    .ant-form-item-label,
+    .ant-legacy-form-item-label,
+    .ant-form-item-control,
+    .ant-legacy-form-item-control {
+      padding: 0;
+      line-height: 32px;
+    }
+  }
+  .label {
+    flex: 0 0 auto;
+    margin-right: 24px;
+    color: @heading-color;
+    font-size: @font-size-base;
+    text-align: left;
+    & > span {
+      display: inline-block;
+      flex-shrink: 0;
+      width: 80px;
+      height: 32px;
+      // height: 20px;
+      color: #999;
+      font-weight: 500;
+      font-size: 14px;
+      font-family: PingFangSC-Medium, PingFang SC;
+      line-height: 32px;
+      // line-height: 20px;
+      // &::after {
+      //   content: '：';
+      // }
+    }
+  }
+  .content {
+    flex: 1 1 0;
+    :global {
+      .ant-form-item,
+      .ant-legacy-form-item {
+        &:last-child {
+          display: block;
+          margin-right: 0;
+        }
+      }
+    }
+  }
+}
+
+.standardFormRowLast {
+  margin-bottom: 0;
+  padding-bottom: 0;
+  border: none;
+}
+
+.standardFormRowBlock {
+  :global {
+    .ant-form-item,
+    .ant-legacy-form-item,
+    div.ant-form-item-control-wrapper,
+    div.ant-legacy-form-item-control-wrapper {
+      display: block;
+    }
+  }
+}
+
+.standardFormRowGrid {
+  :global {
+    .ant-form-item,
+    .ant-legacy-form-item,
+    div.ant-form-item-control-wrapper,
+    div.ant-legacy-form-item-control-wrapper {
+      display: block;
+    }
+    .ant-form-item-label,
+    .ant-legacy-form-item-label {
+      float: left;
+    }
+  }
+}
diff --git a/webapp/packages/supersonic-fe/src/components/StandardFormRow/index.tsx b/webapp/packages/supersonic-fe/src/components/StandardFormRow/index.tsx
new file mode 100644
index 000000000..57a0038ba
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/components/StandardFormRow/index.tsx
@@ -0,0 +1,43 @@
+import React from 'react';
+import classNames from 'classnames';
+import styles from './index.less';
+
+type StandardFormRowProps = {
+  title?: string;
+  last?: boolean;
+  block?: boolean;
+  grid?: boolean;
+  style?: React.CSSProperties;
+  titleClassName?: string;
+};
+
+const StandardFormRow: React.FC<StandardFormRowProps> = ({
+  title,
+  children,
+  last,
+  block,
+  grid,
+  titleClassName,
+  ...rest
+}) => {
+  const cls = classNames(styles.standardFormRow, {
+    [styles.standardFormRowBlock]: block,
+    [styles.standardFormRowLast]: last,
+    [styles.standardFormRowGrid]: grid,
+  });
+
+  const labelCls = classNames(styles.label, titleClassName);
+
+  return (
+    <div className={cls} {...rest}>
+      {title && (
+        <div className={labelCls}>
+          <span>{title}</span>
+        </div>
+      )}
+      <div className={styles.content}>{children}</div>
+    </div>
+  );
+};
+
+export default StandardFormRow;
diff --git a/webapp/packages/supersonic-fe/src/components/TagSelect/index.less b/webapp/packages/supersonic-fe/src/components/TagSelect/index.less
new file mode 100644
index 000000000..b174f042c
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/components/TagSelect/index.less
@@ -0,0 +1,33 @@
+@import '~antd/es/style/themes/default.less';
+
+.tagSelect {
+  position: relative;
+  max-height: 32px;
+  margin-left: -8px;
+  overflow: hidden;
+  line-height: 32px;
+  transition: all 0.3s;
+  user-select: none;
+  :global {
+    .ant-tag {
+      margin-right: 24px;
+      padding: 0 8px;
+      font-size: @font-size-base;
+    }
+  }
+  &.expanded {
+    max-height: 200px;
+    transition: all 0.3s;
+  }
+  .trigger {
+    position: absolute;
+    top: 0;
+    right: 0;
+    span.anticon {
+      font-size: 12px;
+    }
+  }
+  &.hasExpandTag {
+    padding-right: 50px;
+  }
+}
diff --git a/webapp/packages/supersonic-fe/src/components/TagSelect/index.tsx b/webapp/packages/supersonic-fe/src/components/TagSelect/index.tsx
new file mode 100644
index 000000000..0e0020bb9
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/components/TagSelect/index.tsx
@@ -0,0 +1,191 @@
+import { DownOutlined, UpOutlined } from '@ant-design/icons';
+import { useBoolean, useControllableValue } from 'ahooks';
+import { Tag } from 'antd';
+import classNames from 'classnames';
+import { FC, useEffect } from 'react';
+import React from 'react';
+import styles from './index.less';
+
+const { CheckableTag } = Tag;
+
+export interface TagSelectOptionProps {
+  value: string | number | undefined;
+  style?: React.CSSProperties;
+  checked?: boolean;
+  onChange?: (value: string | number, state: boolean) => void;
+}
+
+const TagSelectOption: React.FC<TagSelectOptionProps> & {
+  isTagSelectOption: boolean;
+} = ({ children, checked, onChange, value }) => (
+  <CheckableTag
+    checked={!!checked}
+    key={value}
+    onChange={(state) => onChange && onChange(value, state)}
+  >
+    {children}
+  </CheckableTag>
+);
+
+TagSelectOption.isTagSelectOption = true;
+
+type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
+export interface TagSelectProps {
+  onChange?: (value: (string | number)[]) => void;
+  expandable?: boolean;
+  value?: (string | number)[];
+  defaultValue?: (string | number)[];
+  style?: React.CSSProperties;
+  hideCheckAll?: boolean;
+  actionsText?: {
+    expandText?: React.ReactNode;
+    collapseText?: React.ReactNode;
+    selectAllText?: React.ReactNode;
+  };
+  className?: string;
+  Option?: TagSelectOptionProps;
+  children?: TagSelectOptionElement | TagSelectOptionElement[];
+  single?: boolean;
+  disableUnCheck?: boolean;
+  empty?: boolean;
+  isSelectAll?: boolean;
+  reverseCheckAll?: boolean;
+}
+
+const TagSelect: FC<TagSelectProps> & { Option: typeof TagSelectOption } = (props) => {
+  const {
+    children,
+    hideCheckAll = false,
+    className,
+    style,
+    expandable,
+    actionsText = {},
+    single = false,
+    disableUnCheck = false,
+    empty = false,
+    isSelectAll = false,
+    reverseCheckAll = false,
+  } = props;
+
+  const [expand, { toggle }] = useBoolean();
+
+  const [value, setValue] = useControllableValue<(string | number)[] | undefined>(props);
+
+  useEffect(() => {
+    if (empty) {
+      setValue([]);
+    }
+  }, [empty]);
+
+  const isTagSelectOption = (node: TagSelectOptionElement) =>
+    node &&
+    node.type &&
+    (node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption');
+
+  const getAllTags = () => {
+    const childrenArray = React.Children.toArray(children) as TagSelectOptionElement[];
+    const checkedTags = childrenArray
+      .filter((child) => isTagSelectOption(child))
+      .map((child) => child.props.value);
+    return checkedTags || [];
+  };
+
+  const onSelectAll = (checked: boolean) => {
+    let checkedTags: (string | number)[] = [];
+    if (reverseCheckAll) {
+      setValue(undefined);
+      return;
+    }
+    if (checked) {
+      checkedTags = getAllTags();
+    }
+    setValue(checkedTags);
+  };
+
+  useEffect(() => {
+    if (isSelectAll) {
+      onSelectAll(true);
+    }
+  }, []);
+
+  const handleTagChange = (tag: string | number, checked: boolean) => {
+    let checkedTags: (string | number)[] = [...(value || [])];
+    if (single && checkedTags.length > 0) {
+      checkedTags = [checkedTags.join('')];
+    }
+    const index = checkedTags.indexOf(tag);
+    if (checked && index === -1) {
+      if (single) {
+        checkedTags = [tag];
+      } else {
+        checkedTags.push(tag);
+      }
+    } else if (!checked && index > -1 && !disableUnCheck) {
+      checkedTags.splice(index, 1);
+    }
+    setValue(checkedTags.length === 0 ? undefined : checkedTags);
+  };
+
+  const checkedAll = getAllTags().length === value?.length;
+  const hasChecked = value === undefined ? false : value?.length > 0;
+
+  const {
+    expandText = '展开',
+    collapseText = '收起',
+    selectAllText = reverseCheckAll ? '不限' : '全部',
+  } = actionsText;
+
+  const cls = classNames(styles.tagSelect, className, {
+    [styles.hasExpandTag]: expandable,
+    [styles.expanded]: expand,
+  });
+
+  return (
+    <div className={cls} style={style}>
+      {hideCheckAll ? null : (
+        <CheckableTag
+          checked={reverseCheckAll ? !hasChecked : checkedAll}
+          key="tag-select-__all__"
+          onChange={onSelectAll}
+        >
+          {selectAllText}
+        </CheckableTag>
+      )}
+      {children &&
+        React.Children.map(children, (child: TagSelectOptionElement) => {
+          if (isTagSelectOption(child)) {
+            return React.cloneElement(child, {
+              key: `tag-select-${child.props.value}`,
+              value: child.props.value,
+              checked: value && value.indexOf(child.props.value) > -1,
+              onChange: handleTagChange,
+            });
+          }
+          return child;
+        })}
+      {expandable && (
+        <a
+          className={styles.trigger}
+          onClick={() => {
+            toggle();
+          }}
+        >
+          {expand ? (
+            <>
+              {collapseText} <UpOutlined />
+            </>
+          ) : (
+            <>
+              {expandText}
+              <DownOutlined />
+            </>
+          )}
+        </a>
+      )}
+    </div>
+  );
+};
+
+TagSelect.Option = TagSelectOption;
+
+export default TagSelect;
diff --git a/webapp/packages/supersonic-fe/src/global.less b/webapp/packages/supersonic-fe/src/global.less
index bbce2f2af..9cb4dfdaa 100644
--- a/webapp/packages/supersonic-fe/src/global.less
+++ b/webapp/packages/supersonic-fe/src/global.less
@@ -228,7 +228,7 @@ ol {
   li {
     cursor: pointer;
     list-style-type:none;
-    // list-style: none;
+    line-height: 25px;
     margin-left: 0;
     &:hover {
       color: #4E86F5;
@@ -242,4 +242,15 @@ ol {
   .ant-tag {
     transition: none;
   }
-} 
\ No newline at end of file
+} 
+
+.semantic-graph-toolbar {
+  position: absolute;
+  width: 200px;
+}
+
+.g6-component-tooltip {
+  p {
+    line-height: 25px;
+  }
+}
\ No newline at end of file
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx
index c1b84e90d..66386f7d7 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ChatSetting.tsx
@@ -6,6 +6,7 @@ import styles from './components/style.less';
 import type { StateType } from './model';
 import { DownOutlined } from '@ant-design/icons';
 import EntitySection from './components/Entity/EntitySection';
+import RecommendedQuestionsSection from './components/Entity/RecommendedQuestionsSection';
 import { ISemantic } from './data';
 import { getDomainList } from './service';
 import OverView from './components/OverView';
@@ -130,15 +131,20 @@ const ChatSetting: React.FC<Props> = ({ domainManger, dispatch }) => {
 
   const isModelItem = [
     {
-      label: '指标场景',
+      label: '指标模式',
       key: 'metric',
       children: <EntitySection chatConfigType={ChatConfigType.AGG} />,
     },
     {
-      label: '明细场景',
+      label: '实体模式',
       key: 'dimenstion',
       children: <EntitySection chatConfigType={ChatConfigType.DETAIL} />,
     },
+    {
+      label: '推荐问题',
+      key: 'recommendedQuestions',
+      children: <RecommendedQuestionsSection />,
+    },
   ];
 
   return (
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx
new file mode 100644
index 000000000..02328c9d3
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/components/MetricFilter.tsx
@@ -0,0 +1,109 @@
+import { Form, Select, Input } from 'antd';
+import StandardFormRow from '@/components/StandardFormRow';
+import TagSelect from '@/components/TagSelect';
+import React, { useEffect } from 'react';
+import { SENSITIVE_LEVEL_OPTIONS } from '../../constant';
+
+const FormItem = Form.Item;
+const { Option } = Select;
+
+type Props = {
+  filterValues?: any;
+  onFiltersChange: (_: any, values: any) => void;
+};
+
+const MetricFilter: React.FC<Props> = ({ filterValues = {}, onFiltersChange }) => {
+  const [form] = Form.useForm();
+
+  useEffect(() => {
+    form.setFieldsValue({
+      ...filterValues,
+    });
+  }, [form, filterValues]);
+
+  const handleValuesChange = (value: any, values: any) => {
+    onFiltersChange(value, values);
+  };
+
+  const onSearch = (value) => {
+    if (!value) {
+      return;
+    }
+    onFiltersChange(value, form.getFieldsValue());
+  };
+
+  const filterList = [
+    {
+      title: '指标类型',
+      key: 'type',
+      options: [
+        {
+          value: 'ATOMIC',
+          label: '原子指标',
+        },
+        { value: 'DERIVED', label: '衍生指标' },
+      ],
+    },
+    {
+      title: '敏感度',
+      key: 'sensitiveLevel',
+      options: SENSITIVE_LEVEL_OPTIONS,
+    },
+  ];
+
+  return (
+    <Form
+      layout="inline"
+      form={form}
+      colon={false}
+      onValuesChange={(value, values) => {
+        if (value.keywords || value.keywordsType) {
+          return;
+        }
+        handleValuesChange(value, values);
+      }}
+      initialValues={{
+        keywordsType: 'name',
+      }}
+    >
+      <StandardFormRow key="search" block>
+        <Input.Group compact>
+          <FormItem name={'keywordsType'} noStyle>
+            <Select>
+              <Option value="name">中文名</Option>
+              <Option value="bizName">英文名</Option>
+              <Option value="id">ID</Option>
+            </Select>
+          </FormItem>
+          <FormItem name={'keywords'} noStyle>
+            <Input.Search
+              placeholder="请输入需要查询的指标信息"
+              allowClear
+              onSearch={onSearch}
+              style={{ width: 300 }}
+              enterButton
+            />
+          </FormItem>
+        </Input.Group>
+      </StandardFormRow>
+      {filterList.map((item) => {
+        const { title, key, options } = item;
+        return (
+          <StandardFormRow key={key} title={title} block>
+            <FormItem name={key}>
+              <TagSelect reverseCheckAll single>
+                {options.map((item: any) => (
+                  <TagSelect.Option key={item.value} value={item.value}>
+                    {item.label}
+                  </TagSelect.Option>
+                ))}
+              </TagSelect>
+            </FormItem>
+          </StandardFormRow>
+        );
+      })}
+    </Form>
+  );
+};
+
+export default MetricFilter;
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/constants.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/constants.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/data.d.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/data.d.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx
new file mode 100644
index 000000000..a04839196
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/index.tsx
@@ -0,0 +1,196 @@
+import type { ActionType, ProColumns } from '@ant-design/pro-table';
+import ProTable from '@ant-design/pro-table';
+import { message, Space } from 'antd';
+import React, { useRef, useState, useEffect } from 'react';
+import type { Dispatch } from 'umi';
+import { connect } from 'umi';
+import type { StateType } from '../model';
+import { SENSITIVE_LEVEL_ENUM } from '../constant';
+import { queryMetric } from '../service';
+import MetricFilter from './components/MetricFilter';
+
+import moment from 'moment';
+import styles from './style.less';
+
+type Props = {
+  dispatch: Dispatch;
+  domainManger: StateType;
+};
+
+type QueryMetricListParams = {
+  id?: string;
+  name?: string;
+  bizName?: string;
+  sensitiveLevel?: string;
+  type?: string;
+};
+
+const ClassMetricTable: React.FC<Props> = () => {
+  const [pagination, setPagination] = useState({
+    current: 1,
+    pageSize: 20,
+    total: 0,
+  });
+
+  const [dataSource, setDataSource] = useState<any[]>([]);
+  const actionRef = useRef<ActionType>();
+
+  useEffect(() => {
+    queryMetricList();
+  }, []);
+
+  const queryMetricList = async (params: QueryMetricListParams = {}) => {
+    const { code, data, msg } = await queryMetric({
+      ...params,
+      ...pagination,
+    });
+    const { list, pageSize, current, total } = data;
+    let resData: any = {};
+    if (code === 200) {
+      setPagination({
+        pageSize: Math.min(pageSize, 100),
+        current,
+        total,
+      });
+      setDataSource(list);
+      resData = {
+        data: list || [],
+        success: true,
+      };
+    } else {
+      message.error(msg);
+      setDataSource([]);
+      resData = {
+        data: [],
+        total: 0,
+        success: false,
+      };
+    }
+    return resData;
+  };
+
+  const columns: ProColumns[] = [
+    {
+      dataIndex: 'id',
+      title: 'ID',
+    },
+    {
+      dataIndex: 'name',
+      title: '指标名称',
+    },
+    {
+      dataIndex: 'alias',
+      title: '别名',
+      search: false,
+    },
+    {
+      dataIndex: 'bizName',
+      title: '字段名称',
+    },
+    {
+      dataIndex: 'sensitiveLevel',
+      title: '敏感度',
+      valueEnum: SENSITIVE_LEVEL_ENUM,
+    },
+    {
+      dataIndex: 'createdBy',
+      title: '创建人',
+      search: false,
+    },
+    {
+      dataIndex: 'description',
+      title: '描述',
+      search: false,
+    },
+    {
+      dataIndex: 'type',
+      title: '指标类型',
+      // search: false,
+      valueEnum: {
+        ATOMIC: '原子指标',
+        DERIVED: '衍生指标',
+      },
+    },
+
+    {
+      dataIndex: 'updatedAt',
+      title: '更新时间',
+      search: false,
+      render: (value: any) => {
+        return value && value !== '-' ? moment(value).format('YYYY-MM-DD HH:mm:ss') : '-';
+      },
+    },
+  ];
+
+  const handleFilterChange = async (filterParams: {
+    keywordsType: string;
+    keywords: string;
+    sensitiveLevel: string;
+    type: string;
+  }) => {
+    const params: QueryMetricListParams = {};
+    const { keywordsType, keywords, sensitiveLevel, type } = filterParams;
+    if (keywordsType && keywords) {
+      params[keywordsType] = keywords;
+    }
+    const sensitiveLevelValue = sensitiveLevel?.[0];
+    const typeValue = type?.[0];
+    if (sensitiveLevelValue) {
+      params.sensitiveLevel = sensitiveLevelValue;
+    }
+    if (type) {
+      params.type = typeValue;
+    }
+
+    await queryMetricList(params);
+  };
+
+  return (
+    <>
+      <div className={styles.metricFilterWrapper}>
+        <MetricFilter
+          onFiltersChange={(_, values) => {
+            handleFilterChange(values);
+          }}
+        />
+      </div>
+      <ProTable
+        className={`${styles.metricTable}`}
+        actionRef={actionRef}
+        // headerTitle="指标列表"
+        rowKey="id"
+        search={false}
+        dataSource={dataSource}
+        columns={columns}
+        pagination={pagination}
+        tableAlertRender={() => {
+          return false;
+        }}
+        onChange={(data: any) => {
+          const { current, pageSize, total } = data;
+          setPagination({
+            current,
+            pageSize,
+            total,
+          });
+        }}
+        size="small"
+        options={{ reload: false, density: false, fullScreen: false }}
+        // toolBarRender={() => [
+        //   <Button
+        //     key="create"
+        //     type="primary"
+        //     onClick={() => {
+        //       setMetricItem(undefined);
+        //     }}
+        //   >
+        //     创建指标
+        //   </Button>,
+        // ]}
+      />
+    </>
+  );
+};
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(ClassMetricTable);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/model.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/model.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/service.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/service.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less
new file mode 100644
index 000000000..363e8c2ae
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/style.less
@@ -0,0 +1,10 @@
+.metricFilterWrapper {
+    margin: 20px;
+    padding: 20px;
+    border-radius: 10px;
+    background: #fff;
+}
+
+.metricTable {
+    margin: 20px;
+}
\ No newline at end of file
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/utils.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/Metric/utils.ts
new file mode 100644
index 000000000..e69de29bb
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx
index 7b7541382..69bb7a12a 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/ProjectManager.tsx
@@ -7,6 +7,7 @@ import ClassDimensionTable from './components/ClassDimensionTable';
 import ClassMetricTable from './components/ClassMetricTable';
 import PermissionSection from './components/Permission/PermissionSection';
 import DatabaseSection from './components/Database/DatabaseSection';
+import EntitySettingSection from './components/Entity/EntitySettingSection';
 import OverView from './components/OverView';
 import styles from './components/style.less';
 import type { StateType } from './model';
@@ -149,11 +150,10 @@ const DomainManger: React.FC<Props> = ({ domainManger, dispatch }) => {
 
   const isModelItem = [
     {
-      label: '可视化建模',
+      label: '画布',
       key: 'xflow',
       children: (
-        <div style={{ width: '100%', height: 'calc(100vh - 200px)' }}>
-          {/* <SemanticFlow /> */}
+        <div style={{ width: '100%', marginTop: -20 }}>
           <SemanticGraphCanvas />
         </div>
       ),
@@ -178,6 +178,12 @@ const DomainManger: React.FC<Props> = ({ domainManger, dispatch }) => {
       key: 'metric',
       children: <ClassMetricTable />,
     },
+    {
+      label: '实体',
+      key: 'entity',
+      children: <EntitySettingSection />,
+    },
+
     {
       label: '权限管理',
       key: 'permissonSetting',
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/components/XflowJsonSchemaFormDrawerForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/components/XflowJsonSchemaFormDrawerForm.tsx
index 0ba3965ff..7ac421e2e 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/components/XflowJsonSchemaFormDrawerForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticFlows/components/XflowJsonSchemaFormDrawerForm.tsx
@@ -7,7 +7,7 @@ import { connect } from 'umi';
 import { DATASOURCE_NODE_RENDER_ID } from '../constant';
 import DataSourceRelationFormDrawer from './DataSourceRelationFormDrawer';
 import DataSourceCreateForm from '../../Datasource/components/DataSourceCreateForm';
-import ClassDataSourceTypeModal from '../../components/ClassDataSourceTypeModal';
+import ClassDataSourceTypeModal from '../../components/ClassDataSourceTypeModal1';
 import { GraphApi } from '../service';
 import { SemanticNodeType } from '../../enum';
 import type { StateType } from '../../model';
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx
index 23ca7d24a..3619f129d 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ContextMenu.tsx
@@ -6,34 +6,36 @@ import { SemanticNodeType } from '../../enum';
 import { SEMANTIC_NODE_TYPE_CONFIG } from '../../constant';
 
 type InitContextMenuProps = {
-  graphShowType: string;
+  graphShowType?: string;
   onMenuClick?: (key: string, item: Item) => void;
 };
 
 export const getMenuConfig = (props?: InitContextMenuProps) => {
-  const { graphShowType, onMenuClick } = props || {};
+  const { onMenuClick } = props || {};
   return {
     getContent(evt) {
       const nodeData = evt?.item?._cfg?.model;
       const { name, nodeType } = nodeData as any;
       if (nodeData) {
         const nodeTypeConfig = SEMANTIC_NODE_TYPE_CONFIG[nodeType] || {};
-        let ulNode = `<ul>
+        let ulNode = `
         <li title='编辑' key='edit' >编辑</li>
         <li title='删除' key='delete' >删除</li>
-      </ul>`;
+     `;
         if (nodeType === SemanticNodeType.DATASOURCE) {
-          if (graphShowType) {
-            const typeString = graphShowType === SemanticNodeType.DIMENSION ? '维度' : '指标';
-            ulNode = `<ul>
-              <li title='新增${typeString}' key='create' >新增${typeString}</li>
-            </ul>`;
-          }
+          ulNode = `
+              <li title='新增维度' key='createDimension' >新增维度</li>
+              <li title='新增指标' key='createMetric' >新增指标</li>
+              <li title='编辑' key='editDatasource' >编辑</li>
+              <li title='删除' key='deleteDatasource' >删除</li>
+            `;
         }
         const header = `${name}`;
         return `<div class="g6ContextMenuContainer">
           <h3>${presetsTagDomString(nodeTypeConfig.label, nodeTypeConfig.color)}${header}</h3>
+          <ul>
         ${ulNode}
+        </ul>
         </div>`;
       }
       return `<div>当前节点信息获取失败</div>`;
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx
index 131132876..02628b306 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/DeleteConfirmModal.tsx
@@ -1,11 +1,10 @@
-import { Button, Modal, message } from 'antd';
+import { Modal, message } from 'antd';
 import React, { useState } from 'react';
 import { SemanticNodeType } from '../../enum';
-import { deleteDimension, deleteMetric } from '../../service';
+import { deleteDimension, deleteMetric, deleteDatasource } from '../../service';
 
 type Props = {
   nodeData: any;
-  nodeType: SemanticNodeType;
   onOkClick: () => void;
   onCancelClick: () => void;
   open: boolean;
@@ -13,7 +12,6 @@ type Props = {
 
 const DeleteConfirmModal: React.FC<Props> = ({
   nodeData,
-  nodeType,
   onOkClick,
   onCancelClick,
   open = false,
@@ -21,11 +19,21 @@ const DeleteConfirmModal: React.FC<Props> = ({
   const [confirmLoading, setConfirmLoading] = useState(false);
   const deleteNode = async () => {
     setConfirmLoading(true);
-    const { id } = nodeData;
-    let deleteQuery = deleteDimension;
+    const { id, nodeType } = nodeData;
+    let deleteQuery;
+    if (nodeType === SemanticNodeType.DIMENSION) {
+      deleteQuery = deleteDimension;
+    }
     if (nodeType === SemanticNodeType.METRIC) {
       deleteQuery = deleteMetric;
     }
+    if (nodeType === SemanticNodeType.DATASOURCE) {
+      deleteQuery = deleteDatasource;
+    }
+    if (!deleteQuery) {
+      message.error('当前节点类型不是维度，指标，数据源中的一种，请确认节点数据');
+      return;
+    }
     const { code, msg } = await deleteQuery(id);
     setConfirmLoading(false);
     if (code === 200) {
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphToolBar.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphToolBar.tsx
new file mode 100644
index 000000000..64601960e
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/GraphToolBar.tsx
@@ -0,0 +1,66 @@
+import { Button, Space } from 'antd';
+import { PlusOutlined } from '@ant-design/icons';
+import React from 'react';
+
+import { connect } from 'umi';
+import type { StateType } from '../../model';
+
+type Props = {
+  domainManger: StateType;
+  onClick: (params?: { eventName?: string }) => void;
+  [key: string]: any;
+};
+
+const GraphToolBar: React.FC<Props> = ({ onClick }) => {
+  return (
+    <div
+      style={{
+        padding: 0,
+        backgroundColor: '#fff',
+        display: 'flex',
+        justifyContent: 'end',
+        position: 'absolute',
+        top: 20,
+        right: 20,
+        zIndex: 1,
+      }}
+    >
+      <Space>
+        <Button
+          key="createDatabaseBtn"
+          icon={<PlusOutlined />}
+          size="small"
+          onClick={() => {
+            onClick?.({ eventName: 'createDatabase' });
+          }}
+        >
+          新建数据源
+        </Button>
+        <Button
+          key="createDimensionBtn"
+          icon={<PlusOutlined />}
+          size="small"
+          onClick={() => {
+            onClick?.({ eventName: 'createDimension' });
+          }}
+        >
+          新建维度
+        </Button>
+        <Button
+          key="createMetricBtn"
+          icon={<PlusOutlined />}
+          size="small"
+          onClick={() => {
+            onClick?.({ eventName: 'createMetric' });
+          }}
+        >
+          新建指标
+        </Button>
+      </Space>
+    </div>
+  );
+};
+
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(GraphToolBar);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx
new file mode 100644
index 000000000..fa5d78e1d
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/NodeInfoDrawer.tsx
@@ -0,0 +1,292 @@
+import {
+  Button,
+  Drawer,
+  message,
+  Row,
+  Col,
+  Divider,
+  Tag,
+  Space,
+  Typography,
+  Popconfirm,
+} from 'antd';
+import React, { useState, useEffect, ReactNode } from 'react';
+import { SemanticNodeType } from '../../enum';
+import { deleteDimension, deleteMetric, deleteDatasource } from '../../service';
+import { connect } from 'umi';
+import type { StateType } from '../../model';
+import moment from 'moment';
+import styles from '../style.less';
+import TransTypeTag from '../../components/TransTypeTag';
+import { MetricTypeWording } from '../../enum';
+import { SENSITIVE_LEVEL_ENUM } from '../../constant';
+
+const { Paragraph } = Typography;
+
+type Props = {
+  nodeData: any;
+  domainManger: StateType;
+  onNodeChange: (params?: { eventName?: string }) => void;
+  onEditBtnClick?: (nodeData: any) => void;
+  [key: string]: any;
+};
+
+interface DescriptionItemProps {
+  title: string;
+  content: React.ReactNode;
+}
+
+type InfoListItemChildrenItem = {
+  label: string;
+  value: string;
+  content?: ReactNode;
+  hideItem?: boolean;
+};
+
+type InfoListItem = {
+  title: string;
+  hideItem?: boolean;
+  children: InfoListItemChildrenItem[];
+};
+
+const DescriptionItem = ({ title, content }: DescriptionItemProps) => (
+  <div style={{ marginBottom: 7, fontSize: 14 }}>
+    <Space>
+      <span style={{ width: 'max-content' }}>{title}:</span>
+      {content}
+    </Space>
+  </div>
+);
+
+const NodeInfoDrawer: React.FC<Props> = ({
+  nodeData,
+  domainManger,
+  onNodeChange,
+  onEditBtnClick,
+  ...restProps
+}) => {
+  const [infoList, setInfoList] = useState<InfoListItem[]>([]);
+  const { selectDomainName } = domainManger;
+  useEffect(() => {
+    if (!nodeData) {
+      return;
+    }
+    const {
+      alias,
+      fullPath,
+      bizName,
+      createdBy,
+      createdAt,
+      updatedAt,
+      description,
+      domainName,
+      sensitiveLevel,
+      type,
+      nodeType,
+    } = nodeData;
+
+    const list = [
+      {
+        title: '基本信息',
+        children: [
+          {
+            label: '字段名称',
+            value: bizName,
+          },
+          {
+            label: '别名',
+            value: alias || '-',
+          },
+          {
+            label: '所属主题域',
+            value: domainName,
+            content: <Tag>{domainName || selectDomainName}</Tag>,
+          },
+
+          {
+            label: '描述',
+            value: description,
+          },
+        ],
+      },
+      {
+        title: '应用信息',
+        children: [
+          {
+            label: '全路径',
+            value: fullPath,
+            content: (
+              <Paragraph style={{ width: 275, margin: 0 }} ellipsis={{ tooltip: fullPath }}>
+                {fullPath}
+              </Paragraph>
+            ),
+          },
+          {
+            label: '敏感度',
+            value: SENSITIVE_LEVEL_ENUM[sensitiveLevel],
+          },
+          {
+            label: '指标类型',
+            value: MetricTypeWording[type],
+            hideItem: nodeType !== SemanticNodeType.METRIC,
+          },
+        ],
+      },
+      {
+        title: '创建信息',
+        children: [
+          {
+            label: '创建人',
+            value: createdBy,
+          },
+          {
+            label: '创建时间',
+            value: createdAt ? moment(createdAt).format('YYYY-MM-DD HH:mm:ss') : '',
+          },
+          {
+            label: '更新时间',
+            value: updatedAt ? moment(updatedAt).format('YYYY-MM-DD HH:mm:ss') : '',
+          },
+        ],
+      },
+    ];
+
+    const datasourceList = [
+      {
+        title: '基本信息',
+        children: [
+          {
+            label: '英文名称',
+            value: bizName,
+          },
+          {
+            label: '所属主题域',
+            value: domainName,
+            content: <Tag>{domainName || selectDomainName}</Tag>,
+          },
+          {
+            label: '描述',
+            value: description,
+          },
+        ],
+      },
+      {
+        title: '创建信息',
+        children: [
+          {
+            label: '创建人',
+            value: createdBy,
+          },
+          {
+            label: '创建时间',
+            value: createdAt ? moment(createdAt).format('YYYY-MM-DD HH:mm:ss') : '',
+          },
+          {
+            label: '更新时间',
+            value: updatedAt ? moment(updatedAt).format('YYYY-MM-DD HH:mm:ss') : '',
+          },
+        ],
+      },
+    ];
+
+    setInfoList(nodeType === SemanticNodeType.DATASOURCE ? datasourceList : list);
+  }, [nodeData]);
+
+  const handleDeleteConfirm = async () => {
+    let deleteQuery;
+    if (nodeData?.nodeType === SemanticNodeType.METRIC) {
+      deleteQuery = deleteMetric;
+    }
+    if (nodeData?.nodeType === SemanticNodeType.DIMENSION) {
+      deleteQuery = deleteDimension;
+    }
+    if (nodeData?.nodeType === SemanticNodeType.DATASOURCE) {
+      deleteQuery = deleteDatasource;
+    }
+    if (!deleteQuery) {
+      return;
+    }
+    const { code, msg } = await deleteQuery(nodeData?.uid);
+    if (code === 200) {
+      onNodeChange?.({ eventName: nodeData?.nodeType });
+    } else {
+      message.error(msg);
+    }
+  };
+
+  return (
+    <>
+      <Drawer
+        title={
+          <Space>
+            {nodeData?.name}
+            <TransTypeTag type={nodeData?.nodeType} />
+          </Space>
+        }
+        placement="right"
+        mask={false}
+        getContainer={false}
+        footer={
+          <div className="ant-drawer-extra">
+            <Space>
+              <Button
+                type="primary"
+                key="editBtn"
+                onClick={() => {
+                  onEditBtnClick?.(nodeData);
+                }}
+              >
+                编辑
+              </Button>
+
+              <Popconfirm
+                title="确认删除？"
+                okText="是"
+                cancelText="否"
+                onConfirm={() => {
+                  handleDeleteConfirm();
+                }}
+              >
+                <Button danger key="deleteBtn">
+                  删除
+                </Button>
+              </Popconfirm>
+            </Space>
+          </div>
+        }
+        {...restProps}
+      >
+        <div key={nodeData?.id} className={styles.nodeInfoDrawerContent}>
+          {infoList.map((item) => {
+            const { children, title } = item;
+            return (
+              <div key={title} style={{ display: item.hideItem ? 'none' : 'block' }}>
+                <p className={styles.title}>{title}</p>
+                {children.map((childrenItem) => {
+                  return (
+                    <Row
+                      key={`${childrenItem.label}-${childrenItem.value}`}
+                      style={{ marginBottom: 10, display: childrenItem.hideItem ? 'none' : 'flex' }}
+                    >
+                      <Col span={24}>
+                        <DescriptionItem
+                          title={childrenItem.label}
+                          content={childrenItem.content || childrenItem.value}
+                        />
+                      </Col>
+                    </Row>
+                  );
+                })}
+                <Divider />
+              </div>
+            );
+          })}
+        </div>
+      </Drawer>
+    </>
+  );
+};
+
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(NodeInfoDrawer);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx
index c7c92eb89..44d6bc508 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/components/ToolBar.tsx
@@ -1,6 +1,6 @@
 import G6, { Graph } from '@antv/g6';
 import { createDom } from '@antv/dom-util';
-import { RefreshGraphData } from '../../data';
+import { ToolBarSearchCallBack } from '../../data';
 const searchIconSvgPath = `<path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479 712 412c0-80.2-31.3-155.4-87.9-212.1-56.6-56.7-132-87.9-212.1-87.9s-155.5 31.3-212.1 87.9C143.2 256.5 112 331.8 112 412c0 80.1 31.3 155.5 87.9 212.1C256.5 680.8 331.8 712 412 712c67 0 130.6-21.8 182.7-62l259.7 259.6a8.2 8.2 0 0011.6 0l43.6-43.5a8.2 8.2 0 000-11.6zM570.4 570.4C528 612.7 471.8 636 412 636s-116-23.3-158.4-65.6C211.3 528 188 471.8 188 412s23.3-116.1 65.6-158.4C296 211.3 352.2 188 412 188s116.1 23.2 158.4 65.6S636 352.2 636 412s-23.3 116.1-65.6 158.4z" />`;
 
 // const searchNode = (graph) => {
@@ -27,64 +27,25 @@ const searchIconSvgPath = `<path d="M909.6 854.5L649.9 594.8C690.2 542.7 712 479
 //   }
 // };
 
-interface Node {
-  label: string;
-  children?: Node[];
-}
-
-function findNodesByLabel(query: string, nodes: Node[]): Node[] {
-  const result: Node[] = [];
-
-  for (const node of nodes) {
-    let match = false;
-    let children: Node[] = [];
-
-    // 如果节点的label包含查询字符串，我们将其标记为匹配
-    if (node.label.includes(query)) {
-      match = true;
-    }
-
-    // 我们还需要在子节点中进行搜索
-    if (node.children) {
-      children = findNodesByLabel(query, node.children);
-      if (children.length > 0) {
-        match = true;
-      }
-    }
-
-    // 如果节点匹配或者其子节点匹配，我们将其添加到结果中
-    if (match) {
-      result.push({ ...node, children });
-    }
-  }
-
-  return result;
-}
-
-const searchNode = (graph: Graph, refreshGraphData?: RefreshGraphData) => {
+const searchNode = (graph: Graph, onSearch?: ToolBarSearchCallBack) => {
   const toolBarSearchInput = document.getElementById('toolBarSearchInput') as HTMLInputElement;
   const searchText = toolBarSearchInput.value.trim();
-  const graphData = graph.get('initGraphData');
-  const filterChildrenData = findNodesByLabel(searchText, graphData.children);
-  refreshGraphData?.({
-    ...graphData,
-    children: filterChildrenData,
-  });
+  onSearch?.(searchText);
 };
 
-const generatorSearchInputDom = (graph: Graph, refreshGraphData: RefreshGraphData) => {
+const generatorSearchInputDom = (graph: Graph, onSearch: ToolBarSearchCallBack) => {
   const domString =
     '<input placeholder="请输入指标/维度名称" class="ant-input" id="toolBarSearchInput" type="text" value="" />';
   const searchInputDom = createDom(domString);
   searchInputDom.addEventListener('keydown', (event) => {
     if (event.key === 'Enter') {
-      searchNode(graph, refreshGraphData);
+      searchNode(graph, onSearch);
     }
   });
   return searchInputDom;
 };
 
-const generatorSearchBtnDom = (graph: Graph) => {
+const generatorSearchBtnDom = (graph: Graph, onSearch: ToolBarSearchCallBack) => {
   const domString = `<button
   id="toolBarSearchBtn"
   type="button"
@@ -106,14 +67,14 @@ const generatorSearchBtnDom = (graph: Graph) => {
 </button>`;
   const searchBtnDom = createDom(domString);
   searchBtnDom.addEventListener('click', () => {
-    searchNode(graph);
+    searchNode(graph, onSearch);
   });
   return searchBtnDom;
 };
 
-const searchInputDOM = (graph: Graph, refreshGraphData: RefreshGraphData) => {
-  const searchInputDom = generatorSearchInputDom(graph, refreshGraphData);
-  const searchBtnDom = generatorSearchBtnDom(graph);
+const searchInputDOM = (graph: Graph, onSearch: ToolBarSearchCallBack) => {
+  const searchInputDom = generatorSearchInputDom(graph, onSearch);
+  const searchBtnDom = generatorSearchBtnDom(graph, onSearch);
   const searchInput = `
   <div id="searchInputContent" class="g6-component-toolbar-search-input" style="position: absolute;top: 38px;width: 190px;left: 0;">
     <span class="ant-input-group-wrapper ant-input-search" >
@@ -129,7 +90,7 @@ const searchInputDOM = (graph: Graph, refreshGraphData: RefreshGraphData) => {
   return searchDom;
 };
 
-const initToolBar = ({ refreshGraphData }: { refreshGraphData: RefreshGraphData }) => {
+const initToolBar = ({ onSearch }: { onSearch: ToolBarSearchCallBack }) => {
   const toolBarInstance = new G6.ToolBar();
   const config = toolBarInstance._cfgs;
   const defaultContentDomString = config.getContent();
@@ -155,10 +116,10 @@ const initToolBar = ({ refreshGraphData }: { refreshGraphData: RefreshGraphData
   defaultContentDom.insertAdjacentHTML('afterbegin', searchBtnDom);
   let searchInputContentVisible = true;
   const toolbar = new G6.ToolBar({
-    position: { x: 10, y: 10 },
+    position: { x: 20, y: 20 },
     className: 'semantic-graph-toolbar',
     getContent: (graph) => {
-      const searchInput = searchInputDOM(graph as Graph, refreshGraphData);
+      const searchInput = searchInputDOM(graph as Graph, onSearch);
       const content = `<div class="g6-component-toolbar-content">${defaultContentDom.outerHTML}</div>`;
       const contentDom = createDom(content);
       contentDom.appendChild(searchInput);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx
index f57c09ffa..cd4176cdc 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/index.tsx
@@ -20,15 +20,17 @@ import initLegend from './components/Legend';
 import { SemanticNodeType } from '../enum';
 import G6 from '@antv/g6';
 import { ISemantic, IDataSource } from '../data';
-
+import NodeInfoDrawer from './components/NodeInfoDrawer';
 import DimensionInfoModal from '../components/DimensionInfoModal';
 import MetricInfoCreateForm from '../components/MetricInfoCreateForm';
 import DeleteConfirmModal from './components/DeleteConfirmModal';
+import ClassDataSourceTypeModal from '../components/ClassDataSourceTypeModal';
+import GraphToolBar from './components/GraphToolBar';
 import { cloneDeep } from 'lodash';
 
 type Props = {
   domainId: number;
-  graphShowType: SemanticNodeType;
+  graphShowType?: SemanticNodeType;
   domainManger: StateType;
   dispatch: Dispatch;
 };
@@ -36,13 +38,18 @@ type Props = {
 const DomainManger: React.FC<Props> = ({
   domainManger,
   domainId,
-  graphShowType = SemanticNodeType.DIMENSION,
+  // graphShowType = SemanticNodeType.DIMENSION,
+  graphShowType,
   dispatch,
 }) => {
   const ref = useRef(null);
+  const dataSourceRef = useRef<ISemantic.IDomainSchemaRelaList>([]);
   const [graphData, setGraphData] = useState<TreeGraphData>();
   const [createDimensionModalVisible, setCreateDimensionModalVisible] = useState<boolean>(false);
   const [createMetricModalVisible, setCreateMetricModalVisible] = useState<boolean>(false);
+  const [infoDrawerVisible, setInfoDrawerVisible] = useState<boolean>(false);
+
+  const [currentNodeData, setCurrentNodeData] = useState<any>();
 
   const legendDataRef = useRef<any[]>([]);
   const graphRef = useRef<any>(null);
@@ -53,12 +60,15 @@ const DomainManger: React.FC<Props> = ({
 
   const [nodeDataSource, setNodeDataSource] = useState<any>();
 
+  const [dataSourceInfoList, setDataSourceInfoList] = useState<IDataSource.IDataSourceItem[]>([]);
+
   const { dimensionList, metricList } = domainManger;
 
   const dimensionListRef = useRef<ISemantic.IDimensionItem[]>([]);
   const metricListRef = useRef<ISemantic.IMetricItem[]>([]);
 
   const [confirmModalOpenState, setConfirmModalOpenState] = useState<boolean>(false);
+  const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
 
   // const toggleNodeVisibility = (graph: Graph, node: Item, visible: boolean) => {
   //   if (visible) {
@@ -84,8 +94,34 @@ const DomainManger: React.FC<Props> = ({
   //   }
   // };
 
-  const changeGraphData = (data: IDataSource.IDataSourceItem[], type: SemanticNodeType) => {
-    const relationData = formatterRelationData(data, type);
+  const handleSeachNode = (text: string) => {
+    const filterData = dataSourceRef.current.reduce(
+      (data: ISemantic.IDomainSchemaRelaList, item: ISemantic.IDomainSchemaRelaItem) => {
+        const { dimensions, metrics } = item;
+        const dimensionsList = dimensions.filter((dimension) => {
+          return dimension.name.includes(text);
+        });
+        const metricsList = metrics.filter((metric) => {
+          return metric.name.includes(text);
+        });
+        data.push({
+          ...item,
+          dimensions: dimensionsList,
+          metrics: metricsList,
+        });
+        return data;
+      },
+      [],
+    );
+    const rootGraphData = changeGraphData(filterData);
+    refreshGraphData(rootGraphData);
+  };
+
+  const changeGraphData = (
+    dataSourceList: ISemantic.IDomainSchemaRelaList,
+    type?: SemanticNodeType,
+  ): TreeGraphData => {
+    const relationData = formatterRelationData({ dataSourceList, type, limit: 20 });
     const legendList = relationData.map((item: any) => {
       const { id, name } = item;
       return {
@@ -101,7 +137,6 @@ const DomainManger: React.FC<Props> = ({
       name: domainManger.selectDomainName,
       children: relationData,
     };
-    //
     return graphRootData;
   };
 
@@ -112,8 +147,14 @@ const DomainManger: React.FC<Props> = ({
     const { code, data } = await getDomainSchemaRela(params.domainId);
     if (code === 200) {
       if (data) {
-        const graphRootData = changeGraphData(data, params.graphShowType || graphShowType);
+        setDataSourceInfoList(
+          data.map((item: ISemantic.IDomainSchemaRelaItem) => {
+            return item.datasource;
+          }),
+        );
+        const graphRootData = changeGraphData(data);
         setGraphData(graphRootData);
+        dataSourceRef.current = data;
         return graphRootData;
       }
       return false;
@@ -164,14 +205,17 @@ const DomainManger: React.FC<Props> = ({
     if (!targetData) {
       return;
     }
-
     const datasource = loopNodeFindDataSource(item);
     if (datasource) {
       setNodeDataSource({
+        ...datasource,
         id: datasource.uid,
-        name: datasource.name,
       });
     }
+    if (targetData.nodeType === SemanticNodeType.DATASOURCE) {
+      setCreateDataSourceModalOpen(true);
+      return;
+    }
     if (targetData.nodeType === SemanticNodeType.DIMENSION) {
       const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid);
       if (targetItem) {
@@ -180,6 +224,7 @@ const DomainManger: React.FC<Props> = ({
       } else {
         message.error('获取维度初始化数据失败');
       }
+      return;
     }
     if (targetData.nodeType === SemanticNodeType.METRIC) {
       const targetItem = metricListRef.current.find((item) => item.id === targetData.uid);
@@ -189,22 +234,23 @@ const DomainManger: React.FC<Props> = ({
       } else {
         message.error('获取指标初始化数据失败');
       }
+      return;
     }
   };
 
-  const handleContextMenuClickCreate = (item: IItemBaseConfig) => {
+  const handleContextMenuClickCreate = (item: IItemBaseConfig, key: string) => {
     const datasource = item.model;
     if (!datasource) {
       return;
     }
     setNodeDataSource({
+      ...datasource,
       id: datasource.uid,
-      name: datasource.name,
     });
-    if (graphShowType === SemanticNodeType.DIMENSION) {
+    if (key === 'createDimension') {
       setCreateDimensionModalVisible(true);
     }
-    if (graphShowType === SemanticNodeType.METRIC) {
+    if (key === 'createMetric') {
       setCreateMetricModalVisible(true);
     }
     setDimensionItem(undefined);
@@ -216,10 +262,19 @@ const DomainManger: React.FC<Props> = ({
     if (!targetData) {
       return;
     }
+    if (targetData.nodeType === SemanticNodeType.DATASOURCE) {
+      setCurrentNodeData({
+        ...targetData,
+        id: targetData.uid,
+      });
+      setConfirmModalOpenState(true);
+      return;
+    }
     if (targetData.nodeType === SemanticNodeType.DIMENSION) {
       const targetItem = dimensionListRef.current.find((item) => item.id === targetData.uid);
       if (targetItem) {
-        setDimensionItem({ ...targetItem });
+        // setDimensionItem({ ...targetItem });
+        setCurrentNodeData(targetItem);
         setConfirmModalOpenState(true);
       } else {
         message.error('获取维度初始化数据失败');
@@ -228,7 +283,8 @@ const DomainManger: React.FC<Props> = ({
     if (targetData.nodeType === SemanticNodeType.METRIC) {
       const targetItem = metricListRef.current.find((item) => item.id === targetData.uid);
       if (targetItem) {
-        setMetricItem({ ...targetItem });
+        // setMetricItem({ ...targetItem });
+        setCurrentNodeData(targetItem);
         setConfirmModalOpenState(true);
       } else {
         message.error('获取指标初始化数据失败');
@@ -242,19 +298,27 @@ const DomainManger: React.FC<Props> = ({
     }
     switch (key) {
       case 'edit':
+      case 'editDatasource':
         handleContextMenuClickEdit(item._cfg);
         break;
       case 'delete':
+      case 'deleteDatasource':
         handleContextMenuClickDelete(item._cfg);
         break;
-      case 'create':
-        handleContextMenuClickCreate(item._cfg);
+      case 'createDimension':
+      case 'createMetric':
+        handleContextMenuClickCreate(item._cfg, key);
         break;
       default:
         break;
     }
   };
 
+  const handleNodeTypeClick = (nodeData: any) => {
+    setCurrentNodeData(nodeData);
+    setInfoDrawerVisible(true);
+  };
+
   const graphConfigMap = {
     dendrogram: {
       defaultEdge: {
@@ -308,7 +372,7 @@ const DomainManger: React.FC<Props> = ({
       const graphConfigKey = graphNodeList.length > 20 ? 'dendrogram' : 'mindmap';
 
       getLegendDataFilterFunctions();
-      const toolbar = initToolBar({ refreshGraphData });
+      const toolbar = initToolBar({ onSearch: handleSeachNode });
       const tooltip = initTooltips();
       const contextMenu = initContextMenu({
         graphShowType,
@@ -325,14 +389,14 @@ const DomainManger: React.FC<Props> = ({
         height,
         modes: {
           default: [
-            {
-              type: 'collapse-expand',
-              onChange: function onChange(item, collapsed) {
-                const data = item!.get('model');
-                data.collapsed = collapsed;
-                return true;
-              },
-            },
+            // {
+            //   type: 'collapse-expand',
+            //   onChange: function onChange(item, collapsed) {
+            //     const data = item!.get('model');
+            //     data.collapsed = collapsed;
+            //     return true;
+            //   },
+            // },
             'drag-node',
             'drag-canvas',
             // 'activate-relations',
@@ -368,6 +432,8 @@ const DomainManger: React.FC<Props> = ({
         plugins: [legend, tooltip, toolbar, contextMenu],
       });
       graphRef.current.set('initGraphData', graphData);
+      graphRef.current.set('initDataSource', dataSourceRef.current);
+
       const legendCanvas = legend._cfgs.legendCanvas;
 
       // legend模式事件方法bindEvents会有点击图例空白清空选中的逻辑，在注册click事件前，先将click事件队列清空；
@@ -408,13 +474,34 @@ const DomainManger: React.FC<Props> = ({
           label: node.name,
         });
       });
-
       graphRef.current.data(graphData);
       graphRef.current.render();
       graphRef.current.fitView([80, 80]);
 
       setAllActiveLegend(legend);
 
+      graphRef.current.on('node:click', (evt: any) => {
+        const item = evt.item; // 被操作的节点 item
+        const itemData = item?._cfg?.model;
+        if (itemData) {
+          const { nodeType } = itemData;
+          if (
+            [
+              SemanticNodeType.DIMENSION,
+              SemanticNodeType.METRIC,
+              SemanticNodeType.DATASOURCE,
+            ].includes(nodeType)
+          ) {
+            handleNodeTypeClick(itemData);
+            return;
+          }
+        }
+      });
+
+      graphRef.current.on('canvas:click', () => {
+        setInfoDrawerVisible(false);
+      });
+
       const rootNode = graphRef.current.findById('root');
       graphRef.current.hideItem(rootNode);
       if (typeof window !== 'undefined')
@@ -442,18 +529,69 @@ const DomainManger: React.FC<Props> = ({
 
   return (
     <>
+      <GraphToolBar
+        onClick={({ eventName }: { eventName: string }) => {
+          setNodeDataSource(undefined);
+          if (eventName === 'createDatabase') {
+            setCreateDataSourceModalOpen(true);
+          }
+          if (eventName === 'createDimension') {
+            setCreateDimensionModalVisible(true);
+            setDimensionItem(undefined);
+          }
+          if (eventName === 'createMetric') {
+            setCreateMetricModalVisible(true);
+            setMetricItem(undefined);
+          }
+        }}
+      />
       <div
         ref={ref}
         key={`${domainId}-${graphShowType}`}
         id="semanticGraph"
-        style={{ width: '100%', height: '100%' }}
+        style={{ width: '100%', height: 'calc(100vh - 175px)', position: 'relative' }}
       />
+      <NodeInfoDrawer
+        nodeData={currentNodeData}
+        placement="right"
+        onClose={() => {
+          setInfoDrawerVisible(false);
+        }}
+        open={infoDrawerVisible}
+        mask={false}
+        getContainer={false}
+        onEditBtnClick={(nodeData: any) => {
+          handleContextMenuClickEdit({ model: nodeData });
+          setInfoDrawerVisible(false);
+        }}
+        onNodeChange={({ eventName }: { eventName: string }) => {
+          updateGraphData();
+          setInfoDrawerVisible(false);
+          if (eventName === SemanticNodeType.METRIC) {
+            dispatch({
+              type: 'domainManger/queryMetricList',
+              payload: {
+                domainId,
+              },
+            });
+          }
+          if (eventName === SemanticNodeType.DIMENSION) {
+            dispatch({
+              type: 'domainManger/queryDimensionList',
+              payload: {
+                domainId,
+              },
+            });
+          }
+        }}
+      />
+
       {createDimensionModalVisible && (
         <DimensionInfoModal
           domainId={domainId}
           bindModalVisible={createDimensionModalVisible}
           dimensionItem={dimensionItem}
-          dataSourceList={nodeDataSource ? [nodeDataSource] : []}
+          dataSourceList={nodeDataSource ? [nodeDataSource] : dataSourceInfoList}
           onSubmit={() => {
             setCreateDimensionModalVisible(false);
             updateGraphData();
@@ -473,7 +611,7 @@ const DomainManger: React.FC<Props> = ({
         <MetricInfoCreateForm
           domainId={domainId}
           key={metricItem?.id}
-          datasourceId={nodeDataSource.id}
+          datasourceId={nodeDataSource?.id}
           createModalVisible={createMetricModalVisible}
           metricItem={metricItem}
           onSubmit={() => {
@@ -491,6 +629,19 @@ const DomainManger: React.FC<Props> = ({
           }}
         />
       )}
+      {
+        <ClassDataSourceTypeModal
+          open={createDataSourceModalOpen}
+          onCancel={() => {
+            setNodeDataSource(undefined);
+            setCreateDataSourceModalOpen(false);
+          }}
+          dataSourceItem={nodeDataSource}
+          onSubmit={() => {
+            updateGraphData();
+          }}
+        />
+      }
       {
         <DeleteConfirmModal
           open={confirmModalOpenState}
@@ -514,8 +665,7 @@ const DomainManger: React.FC<Props> = ({
           onCancelClick={() => {
             setConfirmModalOpenState(false);
           }}
-          nodeType={graphShowType}
-          nodeData={graphShowType === SemanticNodeType.DIMENSION ? dimensionItem : metricItem}
+          nodeData={currentNodeData}
         />
       }
     </>
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less
index 3d0257062..02383ba3b 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/style.less
@@ -1,760 +1,22 @@
-@borderColor: #eee;
-@activeColor: #a0c5e8;
-@hoverColor: #dee4e9;
-
-.pageContainer {
-  position: absolute;
-  top: 55px;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  // margin: -24px;
-  background: #fff;
-
-  &.externalPageContainer {
-    margin: 0 !important;
-  }
-}
-
-.searchBar {
-  :global {
-    .ant-form-item-label {
-      width: 70px;
-    }
-  }
-}
-
-.main {
-  position: absolute;
-  top: 0;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  display: flex;
-  :global {
-    .ant-tabs {
-      height: 100% !important;
-      .ant-tabs-content {
-        height: 100% !important;
-        .ant-tabs-tabpane {
-          height: 100%;
-        }
-      }
-    }
-  }
-}
-
-.rightSide {
-  position: relative;
-  z-index: 1;
-  min-width: 250px;
-  height: 100%;
-  margin-left: 4px;
-  padding: 10px;
-  overflow: hidden;
-  :global {
-    .ant-form-item {
-      margin-bottom: 6px;
-
-      .ant-form-item-label {
-        width: 70px;
-      }
-
-      .ant-form-item-control {
-        min-width: 100px;
-      }
-    }
-  }
-}
-
-.rightListSide {
-  position: relative;
-  z-index: 2;
-  flex: 1;
-  height: 100%;
-  // padding: 10px 10px 0;
-  background-color: #fff;
-  // 去掉标签间距
-  :global {
-    .ant-tabs-card.ant-tabs-top > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab,
-    .ant-tabs-card.ant-tabs-bottom > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab,
-    .ant-tabs-card.ant-tabs-top > div > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab,
-    .ant-tabs-card.ant-tabs-bottom > div > .ant-tabs-nav .ant-tabs-tab + .ant-tabs-tab {
-      margin-left: 0;
-    }
-    .ant-tabs > .ant-tabs-nav .ant-tabs-nav-add,
-    .ant-tabs > div > .ant-tabs-nav .ant-tabs-nav-add {
-      margin-left: 0;
-    }
-  }
-}
-
-.leftListSide {
-  position: relative;
-  z-index: 2;
-  flex: 1;
-  height: 100%;
-  // padding: 10px 10px 0;
-  background-color: #fff;
-}
-
-.tableTotal {
-  margin: 0 2px;
-  color: #296df3;
-  font-weight: bold;
-}
-
-.tableDetaildrawer {
-  :global {
-    .ant-drawer-header {
-      padding: 10px 45px 10px 10px;
-    }
-
-    .ant-drawer-close {
-      padding: 10px;
-    }
-
-    .ant-drawer-body {
-      padding: 0 10px 10px;
-    }
-
-    .ant-tabs-top > .ant-tabs-nav {
-      margin-bottom: 8px;
-    }
-  }
-}
-
-.tableDetailTable {
-  :global {
-    .ant-table-cell,
-    .resultTableRow > td {
-      padding: 8px;
-      font-size: 12px;
-    }
-  }
-}
-
-.sqlEditor {
-  min-width: 0;
-  height: 100%;
-  border: solid 1px @borderColor;
-
-  :global {
-    .ace_editor {
-      font-family: 'Menlo', 'Monaco', 'Ubuntu Mono', 'Consolas', 'source-code-pro' !important;
-    }
-  }
-}
-
-.sqlOprBar {
-  margin-top: -10px;
-  padding: 5px;
-  display: flex;
-  .sqlOprBarLeftBox {
-    flex: 1 1 200px;
-  }
-  .sqlOprBarRightBox {
-    flex: 0 1 210px;
-  }
-  :global {
-    .ant-btn-round.ant-btn-sm {
-      font-size: 12px;
-    }
-    .ant-btn-primary {
-      color: #fff;
-      background: #02a7f0;
-      border-color: #02a7f0;
-    }
-    .ant-segmented-item-selected {
-      color: #fff;
-      background: #02a7f0;
-      border-color: #02a7f0;
-    }
-  }
-}
-
-.sqlOprIcon {
-  margin-right: 30px;
-  color: #02a7f0;
-  font-size: 22px;
-
-  &:hover {
-    cursor: pointer;
-    opacity: 0.8;
-  }
-
-  &:active {
-    opacity: 0.7;
-  }
-}
-
-.sqlOprBtn {
-  margin-right: 30px;
-  vertical-align: super !important;
-
-  &:hover {
-    cursor: pointer;
-    opacity: 0.8;
-  }
-
-  &:active {
-    opacity: 0.7;
-  }
-}
-
-.sqlOprSwitch {
-  // vertical-align: super !important;
-  float: right;
-  margin-right: 10px !important;
-}
-
-:global {
-  .is-sql-full-select {
-    background-color: #02a7f0;
-  }
-  .cjjWdp:hover {
-    z-index: 10;
-  }
-}
-
-.sqlMain {
-  display: flex;
-  flex-direction: row;
-  height: 100%;
-
-  .sqlEditorWrapper {
-    flex: 1;
-    height: 100%;
-    overflow: hidden;
-  }
-
-  .sqlParams {
-    width: 20%;
-    height: 100% !important;
-    overflow: auto;
-  }
-  .hideSqlParams {
-    width: 0;
-    height: 100% !important;
-    overflow: auto;
-  }
-}
-
-.sqlParamsBody {
-  .header {
-    display: flex;
-    padding: 10px;
+.nodeInfoDrawerContent {
+  .title {
+    margin-bottom: 12px;
+    font-size: 16px;
+    color: #0e73ff;
     font-weight: bold;
-
-    .title {
-      flex: 1;
-    }
-
-    .icon {
-      display: flex;
-      align-items: center;
-      margin-right: 10px !important;
-      cursor: pointer;
+    position: relative;
+    &::before {
+      display: block;
+      position: absolute;
+      content: "";
+      left: -10px;
+      top: 7px;
+      height: 14px;
+      width: 3px;
+      font-size: 0;
+      background: #0e73ff;
+      border-radius: 2px;
+      border: 1px solid #0e73ff;
     }
   }
-
-  .paramsList {
-    .paramsItem {
-      display: flex;
-      padding: 10px;
-      :global {
-        .ant-list-item-action {
-          margin-left: 5px;
-        }
-      }
-
-      .name {
-        flex: 1;
-        width: 80%;
-        overflow: hidden;
-        font-size: 12px;
-        text-overflow: ellipsis;
-        &:hover {
-          cursor: pointer;
-        }
-      }
-
-      .icon {
-        // display: none;
-        margin-left: 10px;
-      }
-    }
-  }
-
-  // .paramsItem:hover {
-  //   .icon {
-  //     display: inline-block;
-  //     margin-left: 8px;
-  //     cursor: pointer;
-  //   }
-  // }
-}
-
-.disableIcon {
-  vertical-align: super !important;
-  // color: rgba(0, 10, 36, 0.25);
-  background: #7d7f80 !important;
-  border-color: #7d7f80 !important;
-  :global {
-    .anticon .anticon-play-circle {
-      color: #fff;
-    }
-  }
-
-  &:hover {
-    cursor: not-allowed;
-    opacity: 1;
-  }
-}
-
-.sqlTaskListWrap {
-  position: relative;
-  width: 262px;
-  border-top: 0 !important;
-  border-radius: 0;
-
-  :global {
-    .ant-card-head {
-      min-height: 20px;
-    }
-
-    .ant-card-head-title {
-      padding: 8px 0;
-    }
-  }
-}
-
-.sqlTaskList {
-  position: absolute !important;
-  top: 42px;
-  right: 0;
-  bottom: 0;
-  left: 0;
-  overflow: auto;
-}
-
-.sqlBottmWrap {
-  // position: absolute;
-  // top: 484px;
-  // right: 0;
-  // bottom: 0;
-  // left: 0;
-  display: flex;
-  height: 100%;
-  // padding: 0 10px;
-
-  &:global(.small) {
-    top: 334px;
-  }
-
-  &:global(.middle) {
-    top: 384px;
-  }
-}
-
-.sqlResultWrap {
-  position: relative;
-  display: flex;
-  flex: 1;
-  flex-direction: column;
-  overflow: auto;
-  border: solid 1px @borderColor;
-  border-top: 0;
-  border-left: 0;
-}
-
-.sqlToolBar {
-  display: flex;
-  flex-direction: row-reverse;
-  align-items: center;
-  height: 41px;
-  padding: 5px 0;
-  text-align: right;
-}
-
-.sqlResultPane {
-  flex: 1;
-  border-top: solid 1px @borderColor;
-}
-
-.sqlToolBtn {
-  margin-right: 15px;
-}
-.runScriptBtn {
-  margin-right: 15px;
-  background-color: #e87954;
-  border-color: #e87954;
-  &:hover{
-    border-color: #f89878;
-    background: #f89878;
-  }
-  &:focus {
-    border-color: #f89878;
-    background: #f89878;
-  }
-}
-
-.taskFailed {
-  padding: 20px 20px 0 20px;
-}
-
-.sqlResultContent {
-  position: absolute;
-  top: 50%;
-  width: 100%;
-  color: rgba(0, 0, 0, 0.25);
-  font-size: 16px;
-  text-align: center;
-}
-
-.sqlResultLog {
-  padding: 20px;
-  word-wrap: break-word;
-}
-
-.tableList {
-  position: absolute !important;
-  top: 160px;
-  right: 0;
-  bottom: 26px;
-  left: 0;
-  overflow-x: hidden;
-  overflow-y: auto;
-  border-bottom: solid 1px @borderColor;
-}
-
-.tablePage {
-  position: absolute !important;
-  bottom: 0;
-  left: 0;
-  z-index: 1;
-  width: 100%;
-  min-width: 250px;
-  overflow: hidden;
-}
-
-.tableListItem {
-  width: 88%;
-  overflow: hidden;
-  font-size: 12px;
-  text-overflow: ellipsis;
-
-  &:hover {
-    cursor: pointer;
-  }
-}
-
-.tableItem {
-  &:global(.ant-list-item) {
-    padding: 6px 0 6px 6px;
-  }
-
-  :global(.ant-list-item-action) {
-    margin-left: 12px !important;
-  }
-
-  &:hover {
-    background: @hoverColor;
-    border-bottom: 1px solid #f0f0f0;
-  }
-
-  &:global(.active) {
-    background: @activeColor;
-  }
-}
-
-
-.taskIcon {
-  margin-right: 10px;
-  color: #1890ff;
-  font-size: 14px;
-}
-
-.taskSuccessIcon {
-  .taskIcon();
-
-  color: #67c23a;
-}
-
-.taskFailIcon {
-  .taskIcon();
-
-  color: #f56c6c;
-}
-
-.resultFailIcon {
-  margin-right: 8px;
-  color: #f56c6c;
-}
-
-.taskItem {
-  padding: 10px 8px !important;
-  font-size: 12px;
-  cursor: pointer;
-
-  &:global(.ant-list-item) {
-    justify-content: flex-start;
-  }
-
-  &:hover {
-    background: @hoverColor;
-  }
-}
-
-.activeTask {
-  background: @activeColor;
-}
-
-.resultTable {
-  width: 100%;
-
-  :global {
-    .ant-table-body {
-      width: 100%;
-      // max-height: none !important;
-      overflow: auto !important;
-    }
-
-    .ant-table-cell,
-    .resultTableRow > td {
-      padding: 8px;
-      font-size: 12px;
-    }
-  }
-}
-
-.taskLogWrap {
-  word-wrap: break-word;
-}
-
-.siteTagPlus {
-  background: #fff;
-  border-style: dashed;
-}
-
-.editTag {
-  margin-bottom: 5px;
-  user-select: none;
-}
-
-.tagInput {
-  width: 78px;
-  margin-right: 8px;
-  vertical-align: top;
-}
-.outside {
-  position: relative;
-  height: 100%;
-}
-.collapseRightBtn {
-  position: absolute;
-  top: calc(50% + 50px);
-  right: 0;
-  z-index: 100;
-  display: flex;
-  align-items: center;
-  height: 70px;
-  color: #fff;
-  font-size: 12px;
-  background-color: rgba(40, 46, 54, 0.2);
-  border-radius: 24px 0 0 24px;
-  cursor: pointer;
-  transition: all 0.3s ease;
-}
-
-.collapseLeftBtn {
-  position: absolute;
-  top: calc(50% + 45px);
-  left: 0;
-  z-index: 100;
-  display: flex;
-  align-items: center;
-  height: 70px;
-  color: #fff;
-  font-size: 12px;
-  background-color: rgba(40, 46, 54, 0.2);
-  border-radius: 0 24px 24px 0;
-  cursor: pointer;
-  transition: all 0.3s ease;
-}
-
-.detail {
-  .titleCollapse {
-    float: right;
-    padding-right: 18px;
-    color: #1890ff;
-    line-height: 35px;
-    text-align: right;
-    cursor: pointer;
-  }
-
-  .tableTitle {
-    display: inline-block;
-    width: 85%;
-    margin-left: 15px;
-    overflow: hidden;
-    line-height: 35px;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-    cursor: pointer;
-  }
-
-  :global {
-    .ant-divider-horizontal {
-      margin: 0;
-    }
-  }
-}
-
-.search {
-  margin-left: 10px;
-}
-
-.middleArea {
-  :global {
-    .ant-tabs-nav .ant-tabs-tab {
-      border: none;
-      // background: #d9d9d96e;
-      border-right: 1px solid #f0f0f0;
-      border-radius: 0 !important;
-    }
-    .ant-tabs-nav-add {
-      border-radius: 0 !important;
-    }
-
-    .ant-tabs-tab {
-      .ant-tabs-tab-remove {
-        .closeTab {
-          opacity: 0;
-        }
-        .dot {
-          opacity: 1;
-        }
-      }
-    }
-    .ant-tabs-tab:hover {
-      .ant-tabs-tab-remove {
-        .closeTab {
-          opacity: 1 !important;
-        }
-        .dot {
-          opacity: 0;
-        }
-      }
-    }
-  }
-}
-
-.menu {
-  position: relative;
-  z-index: 1;
-  height: 100%;
-  padding: 5px;
-  overflow: hidden;
-  overflow-x: hidden;
-  overflow-y: auto;
-  :global {
-    .ant-form {
-      margin: -2px;
-    }
-  }
-}
-
-.menuList {
-  position: absolute !important;
-  top: 95px;
-  right: 0;
-  bottom: 26px;
-  left: 0;
-  overflow-x: hidden;
-  overflow-y: auto;
-  border-bottom: solid 1px @borderColor;
-  .menuItem {
-    &:global(.ant-list-item) {
-      padding: 6px 0 6px 14px;
-    }
-
-    :global(.ant-list-item-action) {
-      margin-left: 12px !important;
-    }
-
-    &:hover {
-      background: @hoverColor;
-      border-bottom: 1px solid #f0f0f0;
-      .icon {
-        display: block;
-      }
-    }
-
-    &:global(.active) {
-      background: @activeColor;
-    }
-    .menuListItem {
-      width: 90%;
-      overflow: hidden;
-      font-size: 12px;
-      white-space: nowrap;
-      text-overflow: ellipsis;
-      &:hover {
-        cursor: pointer;
-      }
-    }
-    .icon {
-      display: none;
-      margin-right: 15px !important;
-      cursor: pointer;
-    }
-    .menuIcon {
-      display: flex;
-    }
-  }
-}
-
-.scriptFile {
-  width: 100%;
-  margin: 10px;
-  overflow: hidden;
-  font-size: 14px;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-  .icon {
-    margin-right: 10px;
-  }
-}
-
-.sqlScriptName {
-  width: 93% !important;
-  margin: 14px 0 0 14px !important;
-}
-
-.fileIcon {
-  width: 20px !important;
-  height: 20px !important;
-  padding-top: 2px !important;
-  padding-right: 5px !important;
-  vertical-align: middle;
-}
-
-.itemName {
-  vertical-align: middle;
-}
-
-.paneName {
-  width: 100px;
-  overflow: hidden;
-  font-size: 12px !important;
-  white-space: nowrap;
-  text-overflow: ellipsis;
-}
-
-.titleIcon {
-  width: 16px !important;
-  height: 16px !important;
-  margin: 0 3px 4px;
-}
-
+}
\ No newline at end of file
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts
index d92942fb2..1b77d9b10 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraph/utils.ts
@@ -1,5 +1,6 @@
-import { ISemantic, IDataSource } from '../data';
+import { ISemantic } from '../data';
 import { SemanticNodeType } from '../enum';
+import { TreeGraphData } from '@antv/g6-core';
 
 export const typeConfigs = {
   datasource: {
@@ -11,6 +12,7 @@ export const typeConfigs = {
 export const getDimensionChildren = (
   dimensions: ISemantic.IDimensionItem[],
   dataSourceNodeId: string,
+  limit: number = 999,
 ) => {
   const dimensionChildrenList = dimensions.reduce(
     (dimensionChildren: any[], dimension: ISemantic.IDimensionItem) => {
@@ -19,7 +21,7 @@ export const getDimensionChildren = (
         ...dimension,
         nodeType: SemanticNodeType.DIMENSION,
         legendType: dataSourceNodeId,
-        id: `${SemanticNodeType.DIMENSION}-${id}`,
+        id: `${dataSourceNodeId}-${SemanticNodeType.DIMENSION}-${id}`,
         uid: id,
         style: {
           lineWidth: 2,
@@ -31,10 +33,14 @@ export const getDimensionChildren = (
     },
     [],
   );
-  return dimensionChildrenList;
+  return dimensionChildrenList.slice(0, limit);
 };
 
-export const getMetricChildren = (metrics: ISemantic.IMetricItem[], dataSourceNodeId: string) => {
+export const getMetricChildren = (
+  metrics: ISemantic.IMetricItem[],
+  dataSourceNodeId: string,
+  limit: number = 999,
+) => {
   const metricsChildrenList = metrics.reduce(
     (metricsChildren: any[], metric: ISemantic.IMetricItem) => {
       const { id } = metric;
@@ -42,7 +48,7 @@ export const getMetricChildren = (metrics: ISemantic.IMetricItem[], dataSourceNo
         ...metric,
         nodeType: SemanticNodeType.METRIC,
         legendType: dataSourceNodeId,
-        id: `${SemanticNodeType.METRIC}-${id}`,
+        id: `${dataSourceNodeId}-${SemanticNodeType.METRIC}-${id}`,
         uid: id,
         style: {
           lineWidth: 2,
@@ -54,40 +60,50 @@ export const getMetricChildren = (metrics: ISemantic.IMetricItem[], dataSourceNo
     },
     [],
   );
-  return metricsChildrenList;
+  return metricsChildrenList.slice(0, limit);
 };
 
-export const formatterRelationData = (
-  dataSourceList: IDataSource.IDataSourceItem[],
-  type: SemanticNodeType = SemanticNodeType.DIMENSION,
-) => {
-  const relationData = dataSourceList.reduce((relationList: any[], item: any) => {
-    const { datasource, dimensions, metrics } = item;
-    const { id } = datasource;
-    const dataSourceNodeId = `${SemanticNodeType.DATASOURCE}-${id}`;
-    let childrenList = [];
-    if (type === SemanticNodeType.METRIC) {
-      childrenList = getMetricChildren(metrics, dataSourceNodeId);
-    }
-    if (type === SemanticNodeType.DIMENSION) {
-      childrenList = getDimensionChildren(dimensions, dataSourceNodeId);
-    }
-    relationList.push({
-      ...datasource,
-      legendType: dataSourceNodeId,
-      id: dataSourceNodeId,
-      uid: id,
-      nodeType: SemanticNodeType.DATASOURCE,
-      size: 40,
-      children: [...childrenList],
-      style: {
-        lineWidth: 2,
-        fill: '#BDEFDB',
-        stroke: '#5AD8A6',
-      },
-    });
-    return relationList;
-  }, []);
+export const formatterRelationData = (params: {
+  dataSourceList: ISemantic.IDomainSchemaRelaList;
+  limit?: number;
+  type?: SemanticNodeType;
+}): TreeGraphData[] => {
+  const { type, dataSourceList, limit } = params;
+  const relationData = dataSourceList.reduce(
+    (relationList: TreeGraphData[], item: ISemantic.IDomainSchemaRelaItem) => {
+      const { datasource, dimensions, metrics } = item;
+      const { id } = datasource;
+      const dataSourceNodeId = `${SemanticNodeType.DATASOURCE}-${id}`;
+      let childrenList = [];
+      if (type === SemanticNodeType.METRIC) {
+        childrenList = getMetricChildren(metrics, dataSourceNodeId, limit);
+      }
+      if (type === SemanticNodeType.DIMENSION) {
+        childrenList = getDimensionChildren(dimensions, dataSourceNodeId, limit);
+      }
+      if (!type) {
+        const dimensionList = getDimensionChildren(dimensions, dataSourceNodeId, limit);
+        const metricList = getMetricChildren(metrics, dataSourceNodeId, limit);
+        childrenList = [...dimensionList, ...metricList];
+      }
+      relationList.push({
+        ...datasource,
+        legendType: dataSourceNodeId,
+        id: dataSourceNodeId,
+        uid: id,
+        nodeType: SemanticNodeType.DATASOURCE,
+        size: 40,
+        children: [...childrenList],
+        style: {
+          lineWidth: 2,
+          fill: '#BDEFDB',
+          stroke: '#5AD8A6',
+        },
+      });
+      return relationList;
+    },
+    [],
+  );
   return relationData;
 };
 
@@ -121,6 +137,11 @@ export const getNodeConfigByType = (nodeData: any, defaultConfig = {}) => {
     case SemanticNodeType.METRIC:
       return {
         ...defaultConfig,
+        style: {
+          lineWidth: 2,
+          fill: '#fffbe6',
+          stroke: '#ffe58f',
+        },
         labelCfg: { position: 'right', ...labelCfg },
       };
     default:
@@ -137,3 +158,37 @@ export const flatGraphDataNode = (graphData: any[]) => {
     return nodeList;
   }, []);
 };
+
+interface Node {
+  label: string;
+  children?: Node[];
+}
+
+export const findNodesByLabel = (query: string, nodes: Node[]): Node[] => {
+  const result: Node[] = [];
+
+  for (const node of nodes) {
+    let match = false;
+    let children: Node[] = [];
+
+    // 如果节点的label包含查询字符串，我们将其标记为匹配
+    if (node.label.includes(query)) {
+      match = true;
+    }
+
+    // 我们还需要在子节点中进行搜索
+    if (node.children) {
+      children = findNodesByLabel(query, node.children);
+      if (children.length > 0) {
+        match = true;
+      }
+    }
+
+    // 如果节点匹配或者其子节点匹配，我们将其添加到结果中
+    if (match) {
+      result.push({ ...node, children });
+    }
+  }
+
+  return result;
+};
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx
index 86ecdd9df..40a7a73e9 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/SemanticGraphCanvas.tsx
@@ -12,11 +12,11 @@ type Props = {
 };
 
 const SemanticGraphCanvas: React.FC<Props> = ({ domainManger }) => {
-  const [graphShowType, setGraphShowType] = useState<SemanticNodeType>(SemanticNodeType.DATASOURCE);
+  const [graphShowType, setGraphShowType] = useState<SemanticNodeType>(SemanticNodeType.DIMENSION);
   const { selectDomainId } = domainManger;
   return (
     <div className={styles.semanticGraphCanvas}>
-      <div className={styles.toolbar}>
+      {/* <div className={styles.toolbar}>
         <Radio.Group
           buttonStyle="solid"
           value={graphShowType}
@@ -29,7 +29,7 @@ const SemanticGraphCanvas: React.FC<Props> = ({ domainManger }) => {
           <Radio.Button value={SemanticNodeType.DIMENSION}>维度</Radio.Button>
           <Radio.Button value={SemanticNodeType.METRIC}>指标</Radio.Button>
         </Radio.Group>
-      </div>
+      </div> */}
 
       <div className={styles.canvasContainer}>
         {graphShowType === SemanticNodeType.DATASOURCE ? (
@@ -37,8 +37,8 @@ const SemanticGraphCanvas: React.FC<Props> = ({ domainManger }) => {
             <SemanticFlow />
           </div>
         ) : (
-          <div style={{ width: '100%', height: 'calc(100vh - 220px)' }}>
-            <SemanticGraph domainId={selectDomainId} graphShowType={graphShowType} />
+          <div style={{ width: '100%' }}>
+            <SemanticGraph domainId={selectDomainId} />
           </div>
         )}
       </div>
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx
index 5882d2774..e12aac195 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTable.tsx
@@ -1,31 +1,23 @@
 import type { ActionType, ProColumns } from '@ant-design/pro-table';
 import ProTable from '@ant-design/pro-table';
-import { message, Button, Drawer, Space, Popconfirm, Modal, Card, Row, Col } from 'antd';
-import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
-import React, { useRef, useState, useEffect } from 'react';
+import { message, Button, Space, Popconfirm } from 'antd';
+import React, { useRef, useState } from 'react';
 import type { Dispatch } from 'umi';
 import { connect } from 'umi';
-import DataSourceCreateForm from '../Datasource/components/DataSourceCreateForm';
 import ClassDataSourceTypeModal from './ClassDataSourceTypeModal';
 import type { StateType } from '../model';
 import { getDatasourceList, deleteDatasource } from '../service';
-import DataSource from '../Datasource';
 import moment from 'moment';
 
-const { Meta } = Card;
 type Props = {
   dispatch: Dispatch;
   domainManger: StateType;
 };
 
 const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
-  const { selectDomainId, dataBaseResultColsMap, dataBaseConfig } = domainManger;
-  const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
+  const { selectDomainId } = domainManger;
   const [dataSourceItem, setDataSourceItem] = useState<any>();
   const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
-  const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
-  const [fastModeSql, setFastModeSql] = useState<string>('');
-  const [fastModeTableName, setFastModeTableName] = useState<string>('');
 
   const actionRef = useRef<ActionType>();
 
@@ -70,11 +62,7 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
               key="classEditBtn"
               onClick={() => {
                 setDataSourceItem(record);
-                if (record.datasourceDetail.queryType === 'table_query') {
-                  setDataSourceModalVisible(true);
-                  return;
-                }
-                setCreateModalVisible(true);
+                setCreateDataSourceModalOpen(true);
               }}
             >
               编辑
@@ -133,25 +121,10 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
     return resData;
   };
 
-  const queryDataBaseExcuteSql = (tableName: string) => {
-    const sql = `select * from ${tableName}`;
-    setFastModeSql(sql);
-    setFastModeTableName(tableName);
-    dispatch({
-      type: 'domainManger/queryDataBaseExcuteSql',
-      payload: {
-        sql,
-        domainId: selectDomainId,
-        tableName,
-      },
-    });
-  };
-
   return (
     <>
       <ProTable
         actionRef={actionRef}
-        headerTitle="数据源列表"
         rowKey="id"
         columns={columns}
         params={{ domainId: selectDomainId }}
@@ -179,59 +152,12 @@ const ClassDataSourceTable: React.FC<Props> = ({ dispatch, domainManger }) => {
           onCancel={() => {
             setCreateDataSourceModalOpen(false);
           }}
-          onTypeChange={(type) => {
-            if (type === 'fast') {
-              setDataSourceModalVisible(true);
-            } else {
-              setCreateModalVisible(true);
-            }
-            setCreateDataSourceModalOpen(false);
+          dataSourceItem={dataSourceItem}
+          onSubmit={() => {
+            actionRef.current?.reload();
           }}
         />
       }
-      {dataSourceModalVisible && (
-        <DataSourceCreateForm
-          sql={fastModeSql}
-          basicInfoFormMode="fast"
-          domainId={Number(selectDomainId)}
-          dataSourceItem={dataSourceItem}
-          onCancel={() => {
-            setDataSourceModalVisible(false);
-          }}
-          onDataBaseTableChange={(tableName: string) => {
-            queryDataBaseExcuteSql(tableName);
-          }}
-          onSubmit={() => {
-            setDataSourceModalVisible(false);
-            setDataSourceItem(undefined);
-            actionRef.current?.reload();
-          }}
-          createModalVisible={dataSourceModalVisible}
-        />
-      )}
-      {createModalVisible && (
-        <Drawer
-          width={'100%'}
-          destroyOnClose
-          title="数据源编辑"
-          open={true}
-          onClose={() => {
-            setCreateModalVisible(false);
-            setDataSourceItem(undefined);
-          }}
-          footer={null}
-        >
-          <DataSource
-            initialValues={dataSourceItem}
-            domainId={Number(selectDomainId)}
-            onSubmitSuccess={() => {
-              setCreateModalVisible(false);
-              setDataSourceItem(undefined);
-              actionRef.current?.reload();
-            }}
-          />
-        </Drawer>
-      )}
     </>
   );
 };
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx
index 7cb3dcdba..597382cad 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal.tsx
@@ -1,28 +1,69 @@
-import { Modal, Card, Row, Col, Result, Button } from 'antd';
+import { Button, Drawer, Result, Modal, Card, Row, Col } from 'antd';
 import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
 import React, { useState, useEffect } from 'react';
-import { history, connect } from 'umi';
 import type { Dispatch } from 'umi';
+import { history, connect } from 'umi';
+import DataSourceCreateForm from '../Datasource/components/DataSourceCreateForm';
 import type { StateType } from '../model';
+import DataSource from '../Datasource';
+import { IDataSource } from '../data';
+
 const { Meta } = Card;
 type Props = {
   open: boolean;
-  domainManger: StateType;
-  onTypeChange: (type: 'fast' | 'normal') => void;
+  dataSourceItem: IDataSource.IDataSourceItem;
+  onTypeChange?: (type: 'fast' | 'normal') => void;
+  onSubmit?: () => void;
   onCancel?: () => void;
+  dispatch: Dispatch;
+  domainManger: StateType;
 };
 
 const ClassDataSourceTypeModal: React.FC<Props> = ({
   open,
   onTypeChange,
+  onSubmit,
+  dataSourceItem,
   domainManger,
   onCancel,
+  dispatch,
 }) => {
   const { selectDomainId, dataBaseConfig } = domainManger;
+  const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
+
+  const [dataSourceModalVisible, setDataSourceModalVisible] = useState(false);
+  const [fastModeSql, setFastModeSql] = useState<string>('');
+
   const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
+
   useEffect(() => {
-    setCreateDataSourceModalOpen(open);
-  }, [open]);
+    if (!dataSourceItem || !open) {
+      setCreateDataSourceModalOpen(open);
+      return;
+    }
+    if (dataSourceItem?.datasourceDetail?.queryType === 'table_query') {
+      setDataSourceModalVisible(true);
+    } else {
+      setCreateModalVisible(true);
+    }
+  }, [dataSourceItem, open]);
+
+  const queryDataBaseExcuteSql = (tableName: string) => {
+    const sql = `select * from ${tableName}`;
+    setFastModeSql(sql);
+
+    dispatch({
+      type: 'domainManger/queryDataBaseExcuteSql',
+      payload: {
+        sql,
+        domainId: selectDomainId,
+        tableName,
+      },
+    });
+  };
+  const handleCancel = () => {
+    onCancel?.();
+  };
 
   return (
     <>
@@ -30,7 +71,7 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
         open={createDataSourceModalOpen}
         onCancel={() => {
           setCreateDataSourceModalOpen(false);
-          onCancel?.();
+          handleCancel();
         }}
         footer={null}
         centered
@@ -43,8 +84,10 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
                 hoverable
                 style={{ height: 220 }}
                 onClick={() => {
-                  onTypeChange('fast');
+                  onTypeChange?.('fast');
                   setCreateDataSourceModalOpen(false);
+
+                  setDataSourceModalVisible(true);
                 }}
                 cover={
                   <CoffeeOutlined
@@ -59,8 +102,10 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
             <Col span={12}>
               <Card
                 onClick={() => {
-                  onTypeChange('normal');
+                  onTypeChange?.('normal');
                   setCreateDataSourceModalOpen(false);
+
+                  setCreateModalVisible(true);
                 }}
                 hoverable
                 style={{ height: 220 }}
@@ -93,10 +138,52 @@ const ClassDataSourceTypeModal: React.FC<Props> = ({
           />
         )}
       </Modal>
+
+      {dataSourceModalVisible && (
+        <DataSourceCreateForm
+          sql={fastModeSql}
+          basicInfoFormMode="fast"
+          domainId={Number(selectDomainId)}
+          dataSourceItem={dataSourceItem}
+          onCancel={() => {
+            setDataSourceModalVisible(false);
+            handleCancel();
+          }}
+          onDataBaseTableChange={(tableName: string) => {
+            queryDataBaseExcuteSql(tableName);
+          }}
+          onSubmit={() => {
+            setDataSourceModalVisible(false);
+            onSubmit?.();
+          }}
+          createModalVisible={dataSourceModalVisible}
+        />
+      )}
+      {createModalVisible && (
+        <Drawer
+          width={'100%'}
+          destroyOnClose
+          title="数据源编辑"
+          open={true}
+          onClose={() => {
+            setCreateModalVisible(false);
+            handleCancel();
+          }}
+          footer={null}
+        >
+          <DataSource
+            initialValues={dataSourceItem}
+            domainId={Number(selectDomainId)}
+            onSubmitSuccess={() => {
+              setCreateModalVisible(false);
+              onSubmit?.();
+            }}
+          />
+        </Drawer>
+      )}
     </>
   );
 };
-
 export default connect(({ domainManger }: { domainManger: StateType }) => ({
   domainManger,
 }))(ClassDataSourceTypeModal);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx
new file mode 100644
index 000000000..7632e1cff
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDataSourceTypeModal1.tsx
@@ -0,0 +1,101 @@
+import { Modal, Card, Row, Col, Result, Button } from 'antd';
+import { ConsoleSqlOutlined, CoffeeOutlined } from '@ant-design/icons';
+import React, { useState, useEffect } from 'react';
+import { history, connect } from 'umi';
+import type { StateType } from '../model';
+const { Meta } = Card;
+type Props = {
+  open: boolean;
+  domainManger: StateType;
+  onTypeChange: (type: 'fast' | 'normal') => void;
+  onCancel?: () => void;
+};
+
+const ClassDataSourceTypeModal: React.FC<Props> = ({
+  open,
+  onTypeChange,
+  domainManger,
+  onCancel,
+}) => {
+  const { selectDomainId, dataBaseConfig } = domainManger;
+  const [createDataSourceModalOpen, setCreateDataSourceModalOpen] = useState(false);
+  useEffect(() => {
+    setCreateDataSourceModalOpen(open);
+  }, [open]);
+
+  return (
+    <>
+      <Modal
+        open={createDataSourceModalOpen}
+        onCancel={() => {
+          setCreateDataSourceModalOpen(false);
+          onCancel?.();
+        }}
+        footer={null}
+        centered
+        closable={false}
+      >
+        {dataBaseConfig && dataBaseConfig.id ? (
+          <Row gutter={16} style={{ marginTop: '0px' }}>
+            <Col span={12}>
+              <Card
+                hoverable
+                style={{ height: 220 }}
+                onClick={() => {
+                  onTypeChange('fast');
+                  setCreateDataSourceModalOpen(false);
+                }}
+                cover={
+                  <CoffeeOutlined
+                    width={240}
+                    style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
+                  />
+                }
+              >
+                <Meta title="快速创建" description="自动进行数据源可视化创建" />
+              </Card>
+            </Col>
+            <Col span={12}>
+              <Card
+                onClick={() => {
+                  onTypeChange('normal');
+                  setCreateDataSourceModalOpen(false);
+                }}
+                hoverable
+                style={{ height: 220 }}
+                cover={
+                  <ConsoleSqlOutlined
+                    style={{ paddingTop: '45px', height: 120, fontSize: '48px', color: '#1890ff' }}
+                  />
+                }
+              >
+                <Meta title="SQL脚本" description="自定义SQL脚本创建数据源" />
+              </Card>
+            </Col>
+          </Row>
+        ) : (
+          <Result
+            status="warning"
+            subTitle="创建数据源需要先完成数据库设置"
+            extra={
+              <Button
+                type="primary"
+                key="console"
+                onClick={() => {
+                  history.replace(`/semanticModel/${selectDomainId}/dataBase`);
+                  onCancel?.();
+                }}
+              >
+                去设置
+              </Button>
+            }
+          />
+        )}
+      </Modal>
+    </>
+  );
+};
+
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(ClassDataSourceTypeModal);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx
index 0cd609945..9e0a04102 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassDimensionTable.tsx
@@ -8,6 +8,7 @@ import type { StateType } from '../model';
 import { SENSITIVE_LEVEL_ENUM } from '../constant';
 import { getDatasourceList, getDimensionList, deleteDimension } from '../service';
 import DimensionInfoModal from './DimensionInfoModal';
+import DimensionValueSettingModal from './DimensionValueSettingModal';
 import { ISemantic } from '../data';
 import moment from 'moment';
 import styles from './style.less';
@@ -22,6 +23,11 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
   const [createModalVisible, setCreateModalVisible] = useState<boolean>(false);
   const [dimensionItem, setDimensionItem] = useState<ISemantic.IDimensionItem>();
   const [dataSourceList, setDataSourceList] = useState<any[]>([]);
+  const [dimensionValueSettingList, setDimensionValueSettingList] = useState<
+    ISemantic.IDimensionValueSettingItem[]
+  >([]);
+  const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
+    useState<boolean>(false);
   const [pagination, setPagination] = useState({
     current: 1,
     pageSize: 20,
@@ -141,6 +147,20 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
             >
               编辑
             </a>
+            <a
+              key="classEditBtn"
+              onClick={() => {
+                setDimensionItem(record);
+                setDimensionValueSettingModalVisible(true);
+                if (Array.isArray(record.dimValueMaps)) {
+                  setDimensionValueSettingList(record.dimValueMaps);
+                } else {
+                  setDimensionValueSettingList([]);
+                }
+              }}
+            >
+              维度值设置
+            </a>
             <Popconfirm
               title="确认删除？"
               okText="是"
@@ -175,7 +195,7 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
       <ProTable
         className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
         actionRef={actionRef}
-        headerTitle="维度列表"
+        // headerTitle="维度列表"
         rowKey="id"
         columns={columns}
         request={queryDimensionList}
@@ -236,6 +256,26 @@ const ClassDimensionTable: React.FC<Props> = ({ domainManger, dispatch }) => {
           }}
         />
       )}
+      {dimensionValueSettingModalVisible && (
+        <DimensionValueSettingModal
+          dimensionValueSettingList={dimensionValueSettingList}
+          open={dimensionValueSettingModalVisible}
+          dimensionItem={dimensionItem}
+          onCancel={() => {
+            setDimensionValueSettingModalVisible(false);
+          }}
+          onSubmit={() => {
+            actionRef?.current?.reload();
+            dispatch({
+              type: 'domainManger/queryDimensionList',
+              payload: {
+                domainId: selectDomainId,
+              },
+            });
+            setDimensionValueSettingModalVisible(false);
+          }}
+        />
+      )}
     </>
   );
 };
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx
index 1a87709d3..468507627 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/ClassMetricTable.tsx
@@ -166,43 +166,12 @@ const ClassMetricTable: React.FC<Props> = ({ domainManger, dispatch }) => {
     },
   ];
 
-  // const saveMetric = async (fieldsValue: any, reloadState: boolean = true) => {
-  //   const queryParams = {
-  //     domainId: selectDomainId,
-  //     ...fieldsValue,
-  //   };
-  //   if (queryParams.typeParams && !queryParams.typeParams.expr) {
-  //     message.error('度量表达式不能为空');
-  //     return;
-  //   }
-  //   let saveMetricQuery = creatExprMetric;
-  //   if (queryParams.id) {
-  //     saveMetricQuery = updateExprMetric;
-  //   }
-  //   const { code, msg } = await saveMetricQuery(queryParams);
-  //   if (code === 200) {
-  //     message.success('编辑指标成功');
-  //     setCreateModalVisible(false);
-  //     if (reloadState) {
-  //       actionRef?.current?.reload();
-  //     }
-  //     dispatch({
-  //       type: 'domainManger/queryMetricList',
-  //       payload: {
-  //         domainId: selectDomainId,
-  //       },
-  //     });
-  //     return;
-  //   }
-  //   message.error(msg);
-  // };
-
   return (
     <>
       <ProTable
         className={`${styles.classTable} ${styles.classTableSelectColumnAlignLeft}`}
         actionRef={actionRef}
-        headerTitle="指标列表"
+        // headerTitle="指标列表"
         rowKey="id"
         search={{
           span: 4,
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditList/TextArea.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditList/TextArea.tsx
new file mode 100644
index 000000000..886070ca5
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditList/TextArea.tsx
@@ -0,0 +1,166 @@
+import React, { useState, useEffect } from 'react';
+import { List, Collapse, Button, Input } from 'antd';
+import { uuid } from '@/utils/utils';
+
+import styles from './style.less';
+
+const { Panel } = Collapse;
+const { TextArea } = Input;
+
+type Props = {
+  title?: string;
+  defaultCollapse?: boolean;
+  value?: string[];
+  onChange?: (list: string[]) => void;
+};
+
+type ListItem = {
+  id: string;
+  sql: string;
+};
+
+type List = ListItem[];
+
+const CommonEditList: React.FC<Props> = ({ title, defaultCollapse = false, value, onChange }) => {
+  const [listDataSource, setListDataSource] = useState<List>([]);
+  const [currentSql, setCurrentSql] = useState<string>('');
+  const [activeKey, setActiveKey] = useState<string>();
+  const [currentRecord, setCurrentRecord] = useState<ListItem>();
+
+  useEffect(() => {
+    if (Array.isArray(value)) {
+      const list = value.map((sql: string) => {
+        return {
+          id: uuid(),
+          sql,
+        };
+      });
+      setListDataSource(list);
+    }
+  }, [value]);
+
+  const handleListChange = (listDataSource: List) => {
+    const sqlList = listDataSource.map((item) => {
+      return item.sql;
+    });
+    onChange?.(sqlList);
+  };
+
+  return (
+    <div className={styles.commonEditList}>
+      <Collapse
+        activeKey={activeKey}
+        defaultActiveKey={defaultCollapse ? ['editor'] : undefined}
+        onChange={() => {}}
+        ghost
+      >
+        <Panel
+          header={title}
+          key="editor"
+          extra={
+            activeKey ? (
+              <Button
+                key="saveBtn"
+                type="primary"
+                onClick={() => {
+                  if (!currentRecord && !currentSql) {
+                    setActiveKey(undefined);
+                    return;
+                  }
+                  if (currentRecord) {
+                    const list = [...listDataSource].map((item) => {
+                      if (item.id === currentRecord.id) {
+                        return {
+                          ...item,
+                          sql: currentSql,
+                        };
+                      }
+                      return item;
+                    });
+                    setListDataSource(list);
+                    handleListChange(list);
+                  } else {
+                    const list = [
+                      ...listDataSource,
+                      {
+                        id: uuid(),
+                        sql: currentSql,
+                      },
+                    ];
+                    setListDataSource(list);
+                    handleListChange(list);
+                  }
+
+                  setActiveKey(undefined);
+                }}
+              >
+                确认
+              </Button>
+            ) : (
+              <Button
+                type="primary"
+                key="createBtn"
+                onClick={() => {
+                  setCurrentRecord(undefined);
+                  setCurrentSql('');
+                  setActiveKey('editor');
+                }}
+              >
+                新增
+              </Button>
+            )
+          }
+          showArrow={false}
+        >
+          <div>
+            <TextArea
+              placeholder="请输入推荐问题"
+              value={currentSql}
+              style={{ height: 150 }}
+              minLength={5}
+              onChange={(e) => {
+                setCurrentSql(e.target.value);
+              }}
+            />
+          </div>
+        </Panel>
+      </Collapse>
+      <List
+        itemLayout="horizontal"
+        dataSource={listDataSource || []}
+        renderItem={(item) => (
+          <List.Item
+            actions={[
+              <a
+                key="list-loadmore-edit"
+                onClick={() => {
+                  setCurrentSql(item.sql);
+                  setCurrentRecord(item);
+                  setActiveKey('editor');
+                }}
+              >
+                编辑
+              </a>,
+              <a
+                key="list-loadmore-more"
+                onClick={() => {
+                  const list = [...listDataSource].filter(({ id }) => {
+                    return item.id !== id;
+                  });
+                  handleListChange(list);
+                  setListDataSource(list);
+                }}
+              >
+                删除
+              </a>,
+            ]}
+          >
+            <List.Item.Meta title={item.sql} />
+          </List.Item>
+        )}
+      />
+    </div>
+  );
+};
+
+export default CommonEditList;
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditTable.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditTable.tsx
index 02bf2783f..5139a9752 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditTable.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/CommonEditTable.tsx
@@ -9,7 +9,7 @@ type Props = {
   title?: string;
   tableDataSource: any[];
   columnList: any[];
-  rowKey: string;
+  rowKey?: string;
   editableProTableProps?: any;
   onDataSourceChange?: (dataSource: any) => void;
   extenderCtrlColumn?: (text, record, _, action) => ReactNode[];
@@ -35,6 +35,7 @@ const CommonEditTable: React.FC<Props> = forwardRef(
     }: Props,
     ref: Ref<any>,
   ) => {
+    const defaultRowKey = rowKey || 'editRowId';
     const [dataSource, setDataSource] = useState<any[]>(tableDataSource);
     const actionRef = useRef<ActionType>();
 
@@ -50,7 +51,7 @@ const CommonEditTable: React.FC<Props> = forwardRef(
         tableDataSource.map((item: any) => {
           return {
             ...item,
-            editRowId: item[rowKey] || (Math.random() * 1000000).toFixed(0),
+            editRowId: item[defaultRowKey] || (Math.random() * 1000000).toFixed(0),
           };
         }),
       );
@@ -82,7 +83,9 @@ const CommonEditTable: React.FC<Props> = forwardRef(
               <a
                 key="deleteBtn"
                 onClick={() => {
-                  const data = [...dataSource].filter((item) => item[rowKey] !== record[rowKey]);
+                  const data = [...dataSource].filter(
+                    (item) => item[defaultRowKey] !== record[defaultRowKey],
+                  );
                   setDataSource(data);
                   handleDataSourceChange(data);
                 }}
@@ -111,7 +114,7 @@ const CommonEditTable: React.FC<Props> = forwardRef(
           key={title}
           actionRef={actionRef}
           headerTitle={title}
-          rowKey={'editRowId'}
+          rowKey={defaultRowKey}
           columns={columns}
           value={dataSource}
           tableAlertRender={() => {
@@ -133,9 +136,9 @@ const CommonEditTable: React.FC<Props> = forwardRef(
           }}
           editable={{
             onSave: (_, row) => {
-              const rowKeyValue = row[rowKey];
+              const rowKeyValue = row[defaultRowKey];
               const isSame = dataSource.filter((item: any, index: number) => {
-                return index !== row.index && item[rowKey] === rowKeyValue;
+                return index !== row.index && item[defaultRowKey] === rowKeyValue;
               });
               if (isSame[0]) {
                 message.error('存在重复值');
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx
index 402e53404..803b947fe 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Database/DatabaseCreateForm.tsx
@@ -20,15 +20,8 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
 ) => {
   const [form] = Form.useForm();
   const [selectedDbType, setSelectedDbType] = useState<string>('h2');
-  // const queryDatabaseConfig = async () => {
-  //   const { code, data } = await getDatabaseByDomainId(domainId);
-  //   if (code === 200) {
-  //     form.setFieldsValue({ ...data });
-  //     setSelectedDbType(data?.type);
-  //     return;
-  //   }
-  //   message.error('数据库配置获取错误');
-  // };
+
+  const [testLoading, setTestLoading] = useState<boolean>(false);
 
   useEffect(() => {
     form.resetFields();
@@ -36,11 +29,6 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
     setSelectedDbType(dataBaseConfig?.type);
   }, [dataBaseConfig]);
 
-  // useEffect(() => {
-  //   form.resetFields();
-  //   // queryDatabaseConfig();
-  // }, [domainId]);
-
   const getFormValidateFields = async () => {
     return await form.validateFields();
   };
@@ -65,10 +53,12 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
   };
   const testDatabaseConnection = async () => {
     const values = await form.validateFields();
+    setTestLoading(true);
     const { code, data } = await testDatabaseConnect({
       ...values,
       domainId,
     });
+    setTestLoading(false);
     if (code === 200 && data) {
       message.success('连接测试通过');
       return;
@@ -138,7 +128,9 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
         <FormItem name="database" label="数据库名称">
           <Input placeholder="请输入数据库名称" />
         </FormItem>
-
+        <FormItem name="version" label="数据库版本">
+          <Input placeholder="请输入数据库版本" />
+        </FormItem>
         <FormItem name="description" label="描述">
           <TextArea placeholder="请输入数据库描述" style={{ height: 100 }} />
         </FormItem>
@@ -146,6 +138,7 @@ const DatabaseCreateForm: ForwardRefRenderFunction<any, Props> = (
           <Space>
             <Button
               type="primary"
+              loading={testLoading}
               onClick={() => {
                 testDatabaseConnection();
               }}
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx
index efddaee79..9e95fc1f6 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionInfoModal.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect } from 'react';
+import React, { useEffect, useState } from 'react';
 import { Button, Form, Input, Modal, Select } from 'antd';
 import { SENSITIVE_LEVEL_OPTIONS } from '../constant';
 import { formLayout } from '@/components/FormHelper/utils';
@@ -6,6 +6,7 @@ import SqlEditor from '@/components/SqlEditor';
 import InfoTagList from './InfoTagList';
 import { ISemantic } from '../data';
 import { createDimension, updateDimension } from '../service';
+// import DimensionValueSettingModal from './DimensionValueSettingModal';
 import { message } from 'antd';
 
 export type CreateFormProps = {
@@ -31,16 +32,28 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
   onSubmit: handleUpdate,
 }) => {
   const isEdit = !!dimensionItem?.id;
-
+  const [dimensionValueSettingList, setDimensionValueSettingList] = useState<
+    ISemantic.IDimensionValueSettingItem[]
+  >([]);
   const [form] = Form.useForm();
   const { setFieldsValue, resetFields } = form;
-
-  const handleSubmit = async () => {
+  // const [dimensionValueSettingModalVisible, setDimensionValueSettingModalVisible] =
+  //   useState<boolean>(false);
+  const handleSubmit = async (
+    isSilenceSubmit = false,
+    dimValueMaps?: ISemantic.IDimensionValueSettingItem[],
+  ) => {
     const fieldsValue = await form.validateFields();
-    await saveDimension(fieldsValue);
+    await saveDimension(
+      {
+        ...fieldsValue,
+        dimValueMaps: dimValueMaps || dimensionValueSettingList,
+      },
+      isSilenceSubmit,
+    );
   };
 
-  const saveDimension = async (fieldsValue: any) => {
+  const saveDimension = async (fieldsValue: any, isSilenceSubmit = false) => {
     const queryParams = {
       domainId,
       type: 'categorical',
@@ -52,8 +65,10 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
     }
     const { code, msg } = await saveDimensionQuery(queryParams);
     if (code === 200) {
-      message.success('编辑维度成功');
-      handleUpdate(fieldsValue);
+      if (!isSilenceSubmit) {
+        message.success('编辑维度成功');
+        handleUpdate(fieldsValue);
+      }
       return;
     }
     message.error(msg);
@@ -66,6 +81,11 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
   useEffect(() => {
     if (dimensionItem) {
       setFormVal();
+      if (Array.isArray(dimensionItem.dimValueMaps)) {
+        setDimensionValueSettingList(dimensionItem.dimValueMaps);
+      } else {
+        setDimensionValueSettingList([]);
+      }
     } else {
       resetFields();
     }
@@ -78,7 +98,12 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
     return (
       <>
         <Button onClick={onCancel}>取消</Button>
-        <Button type="primary" onClick={handleSubmit}>
+        <Button
+          type="primary"
+          onClick={() => {
+            handleSubmit();
+          }}
+        >
           完成
         </Button>
       </>
@@ -93,20 +118,22 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
         </FormItem>
         <FormItem
           name="name"
-          label="维度中文名"
-          rules={[{ required: true, message: '请输入维度中文名' }]}
+          label="维度名称"
+          rules={[{ required: true, message: '请输入维度名称' }]}
         >
           <Input placeholder="名称不可重复" />
         </FormItem>
         <FormItem
+          hidden={isEdit}
           name="bizName"
-          label="维度英文名"
-          rules={[{ required: true, message: '请输入维度英文名' }]}
+          label="字段名称"
+          rules={[{ required: true, message: '请输入字段名称' }]}
         >
           <Input placeholder="名称不可重复" disabled={isEdit} />
         </FormItem>
 
         <FormItem
+          hidden={isEdit}
           name="datasourceId"
           label="所属数据源"
           rules={[{ required: true, message: '请选择所属数据源' }]}
@@ -158,6 +185,16 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
         >
           <TextArea placeholder="请输入维度描述" />
         </FormItem>
+        {/* <FormItem name="dimValueMaps" label="维度值设置">
+          <Button
+            type="primary"
+            onClick={() => {
+              setDimensionValueSettingModalVisible(true);
+            }}
+          >
+            设置
+          </Button>
+        </FormItem> */}
         <FormItem
           name="expr"
           label="表达式"
@@ -171,28 +208,38 @@ const DimensionInfoModal: React.FC<CreateFormProps> = ({
   };
 
   return (
-    <Modal
-      width={800}
-      destroyOnClose
-      title="维度信息"
-      style={{ top: 48 }}
-      maskClosable={false}
-      open={bindModalVisible}
-      footer={renderFooter()}
-      onCancel={onCancel}
-    >
-      <Form
-        {...formLayout}
-        form={form}
-        initialValues={
-          {
-            // ...formVals,
-          }
-        }
+    <>
+      <Modal
+        width={800}
+        destroyOnClose
+        title="维度信息"
+        style={{ top: 48 }}
+        maskClosable={false}
+        open={bindModalVisible}
+        footer={renderFooter()}
+        onCancel={onCancel}
       >
-        {renderContent()}
-      </Form>
-    </Modal>
+        <Form {...formLayout} form={form}>
+          {renderContent()}
+        </Form>
+      </Modal>
+      {/* {dimensionValueSettingModalVisible && (
+        <DimensionValueSettingModal
+          dimensionValueSettingList={dimensionValueSettingList}
+          open={dimensionValueSettingModalVisible}
+          onCancel={() => {
+            setDimensionValueSettingModalVisible(false);
+          }}
+          onSubmit={(dimValueMaps) => {
+            if (isEdit) {
+              handleSubmit(true, dimValueMaps);
+            }
+            setDimensionValueSettingList(dimValueMaps);
+            setDimensionValueSettingModalVisible(false);
+          }}
+        />
+      )} */}
+    </>
   );
 };
 
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionValueSettingModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionValueSettingModal.tsx
new file mode 100644
index 000000000..21e0412e7
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/DimensionValueSettingModal.tsx
@@ -0,0 +1,156 @@
+import React, { useEffect, useState } from 'react';
+import { Button, Modal, message } from 'antd';
+import { ISemantic } from '../data';
+import CommonEditTable from './CommonEditTable';
+import { createDimension, updateDimension } from '../service';
+import { connect } from 'umi';
+import type { StateType } from '../model';
+
+export type CreateFormProps = {
+  dimensionValueSettingList: ISemantic.IDimensionValueSettingItem[];
+  onCancel: () => void;
+  dimensionItem?: ISemantic.IDimensionItem;
+  open: boolean;
+  onSubmit: (values?: any) => void;
+  domainManger: StateType;
+};
+
+type TableDataSource = { techName: string; bizName: string; alias: string };
+
+const DimensionInfoModal: React.FC<CreateFormProps> = ({
+  onCancel,
+  open,
+  dimensionItem,
+  dimensionValueSettingList,
+  domainManger,
+  onSubmit,
+}) => {
+  const [tableDataSource, setTableDataSource] = useState<TableDataSource[]>([]);
+  const { selectDomainId } = domainManger;
+  const [dimValueMaps, setDimValueMaps] = useState<ISemantic.IDimensionValueSettingItem[]>([]);
+
+  useEffect(() => {
+    const dataSource = dimensionValueSettingList.map((item) => {
+      const { alias } = item;
+      return {
+        ...item,
+        alias: Array.isArray(alias) ? alias.join(',') : '',
+      };
+    });
+    setTableDataSource(dataSource);
+    setDimValueMaps(dimensionValueSettingList);
+  }, [dimensionValueSettingList]);
+
+  const handleSubmit = async () => {
+    await saveDimension({ dimValueMaps });
+    onSubmit?.(dimValueMaps);
+  };
+
+  const saveDimension = async (fieldsValue: any) => {
+    if (!dimensionItem?.id) {
+      return;
+    }
+    const queryParams = {
+      domainId: selectDomainId,
+      id: dimensionItem.id,
+      ...fieldsValue,
+    };
+    const { code, msg } = await updateDimension(queryParams);
+    if (code === 200) {
+      return;
+    }
+    message.error(msg);
+  };
+
+  const renderFooter = () => {
+    return (
+      <>
+        <Button onClick={onCancel}>取消</Button>
+        <Button
+          type="primary"
+          onClick={() => {
+            handleSubmit();
+          }}
+        >
+          完成
+        </Button>
+      </>
+    );
+  };
+
+  const columns = [
+    {
+      title: '技术名称',
+      dataIndex: 'techName',
+      width: 200,
+      formItemProps: {
+        fieldProps: {
+          placeholder: '请填写技术名称',
+        },
+        rules: [
+          {
+            required: true,
+            whitespace: true,
+            message: '此项是必填项',
+          },
+        ],
+      },
+    },
+    {
+      title: '业务名称',
+      dataIndex: 'bizName',
+      width: 200,
+      fieldProps: {
+        placeholder: '请填写业务名称',
+      },
+      formItemProps: {
+        rules: [
+          {
+            required: true,
+            whitespace: true,
+            message: '此项是必填项',
+          },
+        ],
+      },
+    },
+    {
+      title: '别名',
+      dataIndex: 'alias',
+      fieldProps: {
+        placeholder: '多个别名用英文逗号隔开',
+      },
+    },
+  ];
+
+  return (
+    <Modal
+      width={1000}
+      destroyOnClose
+      title="维度值设置"
+      style={{ top: 48 }}
+      maskClosable={false}
+      open={open}
+      footer={renderFooter()}
+      onCancel={onCancel}
+    >
+      <CommonEditTable
+        tableDataSource={tableDataSource}
+        columnList={columns}
+        onDataSourceChange={(tableData) => {
+          const dimValueMaps = tableData.map((item: TableDataSource) => {
+            return {
+              ...item,
+              alias: item.alias ? `${item.alias}`.split(',') : [],
+            };
+          });
+
+          setDimValueMaps(dimValueMaps);
+        }}
+      />
+    </Modal>
+  );
+};
+
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(DimensionInfoModal);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx
index 57a497f75..633b8227e 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DefaultSettingForm.tsx
@@ -11,7 +11,7 @@ import {
 } from './utils';
 import styles from '../style.less';
 import { ISemantic } from '../../data';
-import { ChatConfigType, TransType } from '../../enum';
+import { ChatConfigType, TransType, SemanticNodeType } from '../../enum';
 import TransTypeTag from '../TransTypeTag';
 
 type Props = {
@@ -99,7 +99,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
           name,
           label: (
             <>
-              <TransTypeTag type={TransType.DIMENSION} />
+              <TransTypeTag type={SemanticNodeType.DIMENSION} />
               {name}
             </>
           ),
@@ -115,7 +115,7 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
           name,
           label: (
             <>
-              <TransTypeTag type={TransType.METRIC} />
+              <TransTypeTag type={SemanticNodeType.METRIC} />
               {name}
             </>
           ),
@@ -198,30 +198,56 @@ const DefaultSettingForm: ForwardRefRenderFunction<any, Props> = (
           </FormItem>
         )}
         {chatConfigType === ChatConfigType.AGG && (
-          <FormItem
-            name="metricIds"
-            label={
-              <FormItemTitle
-                title={'指标'}
-                subTitle={'问答搜索结果选择中，如果没有指定指标，将会采用默认指标进行展示'}
+          <>
+            {/* <FormItem
+              name="metricIds"
+              label={
+                <FormItemTitle
+                  title={'指标'}
+                  subTitle={'问答搜索结果选择中，如果没有指定指标，将会采用默认指标进行展示'}
+                />
+              }
+            >
+              <Select
+                mode="multiple"
+                allowClear
+                style={{ width: '100%' }}
+                filterOption={(inputValue: string, item: any) => {
+                  const { label } = item;
+                  if (label.includes(inputValue)) {
+                    return true;
+                  }
+                  return false;
+                }}
+                placeholder="请选择展示指标信息"
+                options={metricListOptions}
               />
-            }
-          >
-            <Select
-              mode="multiple"
-              allowClear
-              style={{ width: '100%' }}
-              filterOption={(inputValue: string, item: any) => {
-                const { label } = item;
-                if (label.includes(inputValue)) {
-                  return true;
-                }
-                return false;
-              }}
-              placeholder="请选择展示指标信息"
-              options={metricListOptions}
-            />
-          </FormItem>
+            </FormItem>
+            <FormItem
+              name="ratioMetricIds"
+              label={
+                <FormItemTitle
+                  title={'同环比指标'}
+                  subTitle={'问答搜索含有指定的指标，将会同时计算该指标最后一天的同环比'}
+                />
+              }
+            >
+              <Select
+                mode="multiple"
+                allowClear
+                style={{ width: '100%' }}
+                filterOption={(inputValue: string, item: any) => {
+                  const { label } = item;
+                  if (label.includes(inputValue)) {
+                    return true;
+                  }
+                  return false;
+                }}
+                placeholder="请选择同环比指标"
+                options={metricListOptions}
+              />
+            </FormItem> */}
+          </>
         )}
 
         <FormItem
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx
index 8c65cc0e5..166ead06b 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionAndMetricVisibleModal.tsx
@@ -15,7 +15,7 @@ type Props = {
   settingSourceList: any[];
   onCancel: () => void;
   visible: boolean;
-  onSubmit: (params?: any) => void;
+  onSubmit: (params?: { isSilenceSubmit?: boolean }) => void;
 };
 
 const dimensionConfig = {
@@ -72,7 +72,8 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
     }
   }, [entityData, settingSourceList]);
 
-  const saveEntity = async () => {
+  const saveEntity = async (submitData: any, isSilenceSubmit = false) => {
+    const { selectedKeyList, knowledgeInfosMap } = submitData;
     const globalKnowledgeConfigFormFields = await formRef?.current?.getFormValidateFields?.();
     let globalKnowledgeConfig = entityData.globalKnowledgeConfig;
     if (globalKnowledgeConfigFormFields) {
@@ -127,8 +128,10 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
       id,
     });
     if (code === 200) {
-      onSubmit?.();
-      message.success('保存成功');
+      if (!isSilenceSubmit) {
+        message.success('保存成功');
+      }
+      onSubmit?.({ isSilenceSubmit });
       return;
     }
     message.error(msg);
@@ -145,7 +148,7 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
         <Button
           type="primary"
           onClick={() => {
-            saveEntity();
+            saveEntity({ selectedKeyList, knowledgeInfosMap });
           }}
         >
           完成
@@ -160,8 +163,9 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
       key: 'visibleSetting',
       children: (
         <DimensionMetricVisibleTransfer
-          onKnowledgeInfosMapChange={(knowledgeInfosMap) => {
-            setKnowledgeInfosMap(knowledgeInfosMap);
+          onKnowledgeInfosMapChange={(knowledgeInfos) => {
+            setKnowledgeInfosMap(knowledgeInfos);
+            saveEntity({ selectedKeyList, knowledgeInfosMap: knowledgeInfos }, true);
           }}
           knowledgeInfosMap={knowledgeInfosMap}
           titles={settingTypeConfig.titles}
@@ -169,6 +173,7 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
           targetList={selectedKeyList}
           onChange={(newTargetKeys) => {
             handleTransferChange(newTargetKeys);
+            saveEntity({ selectedKeyList: newTargetKeys, knowledgeInfosMap }, true);
           }}
         />
       ),
@@ -177,10 +182,12 @@ const DimensionAndMetricVisibleModal: React.FC<Props> = ({
       label: '全局维度值过滤',
       key: 'dimensionValueFilter',
       children: (
-        <DimensionValueSettingForm
-          initialValues={globalKnowledgeConfigInitialValues}
-          ref={formRef}
-        />
+        <div style={{ margin: '0 auto', width: '975px' }}>
+          <DimensionValueSettingForm
+            initialValues={globalKnowledgeConfigInitialValues}
+            ref={formRef}
+          />
+        </div>
       ),
     },
   ];
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionMetricVisibleForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionMetricVisibleForm.tsx
index 616061f7f..ac8a92f7c 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionMetricVisibleForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionMetricVisibleForm.tsx
@@ -74,9 +74,11 @@ const DimensionMetricVisibleForm: ForwardRefRenderFunction<any, Props> = ({
           onCancel={() => {
             setDimensionModalVisible(false);
           }}
-          onSubmit={() => {
+          onSubmit={(params) => {
             onSubmit?.();
-            setDimensionModalVisible(false);
+            if (!params?.isSilenceSubmit) {
+              setDimensionModalVisible(false);
+            }
           }}
         />
       )}
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx
index b5107bf1e..adaf1a58a 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/DimensionValueSettingForm.tsx
@@ -5,8 +5,7 @@ import { Form, Input } from 'antd';
 import { formLayout } from '@/components/FormHelper/utils';
 import { isString } from 'lodash';
 import styles from '../style.less';
-import CommonEditList from '../../components/CommonEditList/index';
-import SqlEditor from '@/components/SqlEditor';
+import CommonEditList from '../../components/CommonEditList';
 type Props = {
   initialValues: any;
   onSubmit?: () => void;
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx
index 1a3a78f47..8ab1885dd 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntityCreateForm.tsx
@@ -1,14 +1,13 @@
 import { useEffect, useState, forwardRef, useImperativeHandle } from 'react';
 import type { ForwardRefRenderFunction } from 'react';
 import { message, Form, Input, Select, Button } from 'antd';
-import { addDomainExtend, editDomainExtend } from '../../service';
-import type { ISemantic, IChatConfig } from '../../data';
+import { updateDomain } from '../../service';
+import type { ISemantic } from '../../data';
 import { formLayout } from '@/components/FormHelper/utils';
-import { formatRichEntityDataListToIds } from './utils';
 import styles from '../style.less';
 
 type Props = {
-  entityData: IChatConfig.IChatRichConfig;
+  entityData?: { id: number; names: string[] };
   dimensionList: ISemantic.IDimensionList;
   domainId: number;
   onSubmit: () => void;
@@ -21,22 +20,20 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
   ref,
 ) => {
   const [form] = Form.useForm();
-  const formatEntityData = formatRichEntityDataListToIds(entityData);
   const [dimensionListOptions, setDimensionListOptions] = useState<any>([]);
-
   const getFormValidateFields = async () => {
     return await form.validateFields();
   };
 
   useEffect(() => {
     form.resetFields();
-    if (!entityData?.entity) {
+    if (!entityData) {
       return;
     }
 
     form.setFieldsValue({
-      ...formatEntityData.entity,
-      id: formatEntityData.id,
+      ...entityData,
+      name: entityData.names.join(','),
     });
   }, [entityData]);
 
@@ -56,20 +53,14 @@ const EntityCreateForm: ForwardRefRenderFunction<any, Props> = (
 
   const saveEntity = async () => {
     const values = await form.validateFields();
-    const { id, name } = values;
-    let saveDomainExtendQuery = addDomainExtend;
-    if (id) {
-      saveDomainExtendQuery = editDomainExtend;
-    }
-    const { code, msg, data } = await saveDomainExtendQuery({
-      chatDetailConfig: {
-        ...formatEntityData,
-        entity: {
-          ...values,
-          names: name.split(','),
-        },
+    const { name } = values;
+
+    const { code, msg, data } = await updateDomain({
+      entity: {
+        ...values,
+        names: name.split(','),
       },
-      id,
+      id: domainId,
       domainId,
     });
 
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySection.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySection.tsx
index ee00cf3f2..075ca2dde 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySection.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySection.tsx
@@ -5,7 +5,7 @@ import { connect } from 'umi';
 import type { StateType } from '../../model';
 import { getDomainExtendDetailConfig } from '../../service';
 import ProCard from '@ant-design/pro-card';
-import EntityCreateForm from './EntityCreateForm';
+
 import DefaultSettingForm from './DefaultSettingForm';
 import type { IChatConfig } from '../../data';
 import DimensionMetricVisibleForm from './DimensionMetricVisibleForm';
@@ -26,8 +26,6 @@ const EntitySection: React.FC<Props> = ({
 
   const [entityData, setentityData] = useState<IChatConfig.IChatRichConfig>();
 
-  const entityCreateRef = useRef<any>({});
-
   const queryThemeListData: any = async () => {
     const { code, data } = await getDomainExtendDetailConfig({
       domainId: selectDomainId,
@@ -58,25 +56,6 @@ const EntitySection: React.FC<Props> = ({
   return (
     <div style={{ width: 800, margin: '0 auto' }}>
       <Space direction="vertical" style={{ width: '100%' }} size={20}>
-        {chatConfigType === 'detail' && entityData && (
-          <ProCard title="实体" bordered>
-            <EntityCreateForm
-              ref={entityCreateRef}
-              domainId={Number(selectDomainId)}
-              entityData={entityData}
-              dimensionList={dimensionList.filter((item) => {
-                const blackDimensionList = entityData?.visibility?.blackDimIdList;
-                if (Array.isArray(blackDimensionList)) {
-                  return !blackDimensionList.includes(item.id);
-                }
-                return false;
-              })}
-              onSubmit={() => {
-                queryThemeListData();
-              }}
-            />
-          </ProCard>
-        )}
         <ProCard bordered title="问答可见">
           <DimensionMetricVisibleForm
             chatConfigKey={
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySettingSection.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySettingSection.tsx
new file mode 100644
index 000000000..74f2f2694
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/EntitySettingSection.tsx
@@ -0,0 +1,69 @@
+import { message, Space } from 'antd';
+import React, { useState, useEffect, useRef } from 'react';
+import type { Dispatch } from 'umi';
+import { connect } from 'umi';
+import type { StateType } from '../../model';
+import { getDomainDetail } from '../../service';
+import ProCard from '@ant-design/pro-card';
+import EntityCreateForm from './EntityCreateForm';
+import type { IChatConfig } from '../../data';
+
+type Props = {
+  dispatch: Dispatch;
+  domainManger: StateType;
+};
+
+const EntitySettingSection: React.FC<Props> = ({ domainManger }) => {
+  const { selectDomainId, dimensionList, metricList } = domainManger;
+
+  const [entityData, setEntityData] = useState<IChatConfig.IChatRichConfig>();
+
+  const entityCreateRef = useRef<any>({});
+
+  const queryDomainData: any = async () => {
+    const { code, data } = await getDomainDetail({
+      domainId: selectDomainId,
+    });
+
+    if (code === 200) {
+      const { entity } = data;
+
+      setEntityData(entity);
+
+      return;
+    }
+
+    message.error('获取问答设置信息失败');
+  };
+
+  const initPage = async () => {
+    queryDomainData();
+  };
+
+  useEffect(() => {
+    initPage();
+  }, [selectDomainId]);
+
+  return (
+    <div style={{ width: 800, margin: '0 auto' }}>
+      <Space direction="vertical" style={{ width: '100%' }} size={20}>
+        {
+          <ProCard title="实体" bordered>
+            <EntityCreateForm
+              ref={entityCreateRef}
+              domainId={Number(selectDomainId)}
+              entityData={entityData}
+              dimensionList={dimensionList}
+              onSubmit={() => {
+                queryDomainData();
+              }}
+            />
+          </ProCard>
+        }
+      </Space>
+    </div>
+  );
+};
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(EntitySettingSection);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx
new file mode 100644
index 000000000..2e122dbf9
--- /dev/null
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/RecommendedQuestionsSection.tsx
@@ -0,0 +1,87 @@
+import { message } from 'antd';
+import React, { useState, useEffect } from 'react';
+import { connect } from 'umi';
+import type { StateType } from '../../model';
+import { getDomainExtendConfig, addDomainExtend, editDomainExtend } from '../../service';
+import ProCard from '@ant-design/pro-card';
+
+import TextAreaCommonEditList from '../../components/CommonEditList/TextArea';
+
+type Props = {
+  domainManger: StateType;
+};
+
+const RecommendedQuestionsSection: React.FC<Props> = ({ domainManger }) => {
+  const { selectDomainId } = domainManger;
+
+  const [questionData, setQuestionData] = useState<string[]>([]);
+  const [currentRecordId, setCurrentRecordId] = useState<number>(0);
+
+  const queryThemeListData: any = async () => {
+    const { code, data } = await getDomainExtendConfig({
+      domainId: selectDomainId,
+    });
+
+    if (code === 200) {
+      const target = data?.[0] || {};
+      if (Array.isArray(target.recommendedQuestions)) {
+        setQuestionData(
+          target.recommendedQuestions.map((item: { question: string }) => {
+            return item.question;
+          }),
+        );
+        setCurrentRecordId(target.id || 0);
+      } else {
+        setQuestionData([]);
+        setCurrentRecordId(0);
+      }
+      return;
+    }
+
+    message.error('获取问答设置信息失败');
+  };
+
+  const saveEntity = async (list: string[]) => {
+    let saveDomainExtendQuery = addDomainExtend;
+    if (currentRecordId) {
+      saveDomainExtendQuery = editDomainExtend;
+    }
+    const { code, msg } = await saveDomainExtendQuery({
+      recommendedQuestions: list.map((question: string) => {
+        return { question };
+      }),
+      id: currentRecordId,
+      domainId: selectDomainId,
+    });
+
+    if (code === 200) {
+      return;
+    }
+    message.error(msg);
+  };
+
+  const initPage = async () => {
+    queryThemeListData();
+  };
+
+  useEffect(() => {
+    initPage();
+  }, [selectDomainId]);
+
+  return (
+    <div style={{ width: 800, margin: '0 auto' }}>
+      <ProCard bordered title="问题推荐列表">
+        <TextAreaCommonEditList
+          value={questionData}
+          onChange={(list) => {
+            saveEntity(list);
+            setQuestionData(list);
+          }}
+        />
+      </ProCard>
+    </div>
+  );
+};
+export default connect(({ domainManger }: { domainManger: StateType }) => ({
+  domainManger,
+}))(RecommendedQuestionsSection);
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/utils.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/utils.ts
index e4d3a7ccf..1c4518fbc 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/utils.ts
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/Entity/utils.ts
@@ -18,8 +18,9 @@ export const formatRichEntityDataListToIds = (
   const detailData: {
     dimensionIds: number[];
     metricIds: number[];
-  } = { dimensionIds: [], metricIds: [] };
-  const { dimensions, metrics } = chatDefaultConfig || {};
+    ratioMetricIds: number[];
+  } = { dimensionIds: [], metricIds: [], ratioMetricIds: [] };
+  const { dimensions, metrics, ratioMetrics } = chatDefaultConfig || {};
   if (Array.isArray(dimensions)) {
     detailData.dimensionIds = dimensions.map((item: ISemantic.IDimensionItem) => {
       return item.id;
@@ -30,6 +31,12 @@ export const formatRichEntityDataListToIds = (
       return item.id;
     });
   }
+  if (Array.isArray(ratioMetrics)) {
+    detailData.ratioMetricIds = ratioMetrics.map((item: ISemantic.IMetricItem) => {
+      return item.id;
+    });
+  }
+
   let entitySetting = {};
   if (entity) {
     const entityItem = entity.dimItem;
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx
index 63a9f857c..434341d97 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/MetricInfoCreateForm.tsx
@@ -199,15 +199,15 @@ const MetricInfoCreateForm: React.FC<CreateFormProps> = ({
         </FormItem>
         <FormItem
           name="name"
-          label="指标中文名"
-          rules={[{ required: true, message: '请输入指标中文名' }]}
+          label="指标名称"
+          rules={[{ required: true, message: '请输入指标名称' }]}
         >
           <Input placeholder="名称不可重复" />
         </FormItem>
         <FormItem
           name="bizName"
-          label="指标英文名"
-          rules={[{ required: true, message: '请输入指标英文名' }]}
+          label="字段名称"
+          rules={[{ required: true, message: '请输入字段名称' }]}
         >
           <Input placeholder="名称不可重复" disabled={isEdit} />
         </FormItem>
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx
index 8a1d056d9..ca3e41995 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/TransTypeTag.tsx
@@ -1,19 +1,21 @@
 import { Tag } from 'antd';
 import React from 'react';
 
-import { TransType } from '../enum';
+import { SemanticNodeType } from '../enum';
 
 type Props = {
-  type: TransType;
+  type: SemanticNodeType;
 };
 
 const TransTypeTag: React.FC<Props> = ({ type }) => {
   return (
     <>
-      {type === TransType.DIMENSION ? (
+      {type === SemanticNodeType.DIMENSION ? (
         <Tag color="blue">{'维度'}</Tag>
-      ) : type === 'metric' ? (
+      ) : type === SemanticNodeType.METRIC ? (
         <Tag color="orange">{'指标'}</Tag>
+      ) : type === SemanticNodeType.DATASOURCE ? (
+        <Tag color="green">{'数据源'}</Tag>
       ) : (
         <></>
       )}
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less
index 481c4922b..0b2640d1e 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/components/style.less
@@ -8,7 +8,8 @@
   .projectManger {
     width: 100%;
     min-height: calc(100vh - 48px);
-    background: #f8f9fb;
+    // background: #f8f9fb;
+    background-color: #fff;
     position: relative;
 
 
@@ -37,8 +38,17 @@
     }
 
     .tab {
-      padding: 0 20px;
-      line-height: 28px;
+      :global {
+        .ant-tabs-tab-btn {
+          font-size: 16px !important;
+        }
+        .ant-tabs-nav-wrap {
+          padding: 0 20px;
+        }
+        .ant-tabs-nav {
+          margin-bottom: 0;
+        }
+      }
     }
 
     .mainTip {
@@ -50,8 +60,9 @@
         padding: 0 !important;
       }
       .ant-tabs-content-holder {
-        overflow: scroll;
-        height: calc(100vh - 192px);
+        margin-top: 20px;
+        // overflow: scroll;
+        // height: calc(100vh - 175px);
       }
     }
 
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts
index 422931726..757edf084 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/data.d.ts
@@ -8,7 +8,9 @@ export type UserName = string;
 
 export type SensitiveLevel = 0 | 1 | 2 | null;
 
-export type RefreshGraphData = (graphRootData: TreeGraphData) => void;
+// export type RefreshGraphData = (graphRootData: TreeGraphData) => void;
+
+export type ToolBarSearchCallBack = (text: string) => void;
 
 export declare namespace IDataSource {
   interface IIdentifiersItem {
@@ -113,8 +115,14 @@ export declare namespace ISemantic {
     semanticType: string;
     alias: string;
     useCnt: number;
+    dimValueMaps: IDimensionValueSettingItem[];
   }
 
+  interface IDimensionValueSettingItem {
+    techName: string;
+    bizName: string;
+    alias?: string[];
+  }
   interface IMeasure {
     name: string;
     agg?: string;
@@ -156,6 +164,14 @@ export declare namespace ISemantic {
 
   type IDimensionList = IDimensionItem[];
   type IMetricList = IMetricItem[];
+
+  interface IDomainSchemaRelaItem {
+    domainId: number;
+    dimensions: IDimensionList;
+    metrics: IMetricList;
+    datasource: IDataSourceItem;
+  }
+  type IDomainSchemaRelaList = IDomainSchemaRelaItem[];
 }
 
 export declare namespace IChatConfig {
@@ -218,6 +234,7 @@ export declare namespace IChatConfig {
     chatDefaultConfig: {
       dimensions: ISemantic.IDimensionList;
       metrics: ISemantic.IMetricList;
+      ratioMetrics: ISemantic.IMetricList;
       unit: number;
       period: string;
     };
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts
index 9f624714c..01e71630e 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/enum.ts
@@ -13,3 +13,8 @@ export enum SemanticNodeType {
   DIMENSION = 'dimension',
   METRIC = 'metric',
 }
+
+export enum MetricTypeWording {
+  ATOMIC = '原子指标',
+  DERIVED = '衍生指标',
+}
diff --git a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts
index b8cbd599e..661cac9ee 100644
--- a/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts
+++ b/webapp/packages/supersonic-fe/src/pages/SemanticModel/service.ts
@@ -111,7 +111,7 @@ export function deleteDomain(id: any): Promise<any> {
   });
 }
 
-export function getGroupAuthInfo(id: string): Promise<any> {
+export function getGroupAuthInfo(id: number): Promise<any> {
   return request(`${process.env.AUTH_API_BASE_URL}queryGroup`, {
     method: 'GET',
     params: {
