This is an automated email from the ASF dual-hosted git repository.

liyang pushed a commit to branch kylin5
in repository https://gitbox.apache.org/repos/asf/kylin.git

commit 8fde6b4aaba28ad48fc7d3daf8e9c6c64c52c6cb
Author: ShengHuang <[email protected]>
AuthorDate: Mon Sep 2 14:40:30 2024 +0800

    KYLIN-5959 Support internal table for website
---
 .../DataSourceModal/SourceHive/SourceHive.vue      |  40 +-
 .../common/DataSourceModal/SourceHive/locales.js   |   4 +-
 .../components/common/DataSourceModal/handler.js   |   2 +
 .../components/common/DataSourceModal/index.vue    |   1 +
 .../src/components/common/DataSourceModal/store.js |   1 +
 .../common/DropdownFilter/DropdownFilter.vue       |   4 +-
 .../components/common/DropdownFilter/locales.js    |   4 +-
 .../common/EditableBlock/EditableBlock.vue         |   2 +-
 .../components/layout/layout_left_right_top.vue    |  11 +-
 .../src/components/monitor/batchJobs/handler.js    |   8 +-
 kystudio/src/components/monitor/batchJobs/jobs.vue |  14 +-
 .../src/components/monitor/batchJobs/locales.js    |   5 +-
 kystudio/src/components/query/query_result.vue     |  34 +-
 kystudio/src/components/query/query_tab.vue        |  23 +-
 .../setting/SettingAdvanced/SettingAdvanced.vue    |   4 +-
 .../SettingInternalTable/SettingInternalTable.vue  |  95 +++++
 .../setting/SettingInternalTable/locales.js        |  16 +
 kystudio/src/components/setting/handler.js         |   1 +
 kystudio/src/components/setting/locales.js         |   1 +
 kystudio/src/components/setting/setting.vue        |   8 +-
 .../DataManagement/DataManagement.vue              | 285 +++++++++++++++
 .../InternalTable/DataManagement/LoadData.vue      | 213 +++++++++++
 .../studio/InternalTable/DataManagement/locales.js |  36 ++
 .../components/studio/InternalTable/List/index.vue | 406 +++++++++++++++++++++
 .../studio/InternalTable/List/locales.js           |  48 +++
 .../InternalTable/Setting/InternalTableSetting.vue | 374 +++++++++++++++++++
 .../studio/InternalTable/Setting/locales.js        |  48 +++
 .../src/components/studio/InternalTable/const.js   |  10 +
 .../src/components/studio/StudioSource/index.vue   |  32 +-
 .../src/components/studio/StudioSource/locales.js  |   4 +-
 kystudio/src/config/index.js                       |   1 +
 kystudio/src/config/spec.js                        |  11 +-
 kystudio/src/locale/en.js                          |   1 +
 kystudio/src/router/index.js                       |   7 +-
 kystudio/src/service/api.js                        |   4 +-
 kystudio/src/service/internalTable.js              |  43 +++
 kystudio/src/service/project.js                    |   3 +
 kystudio/src/store/index.js                        |   2 +
 kystudio/src/store/internalTable.js                |  57 +++
 kystudio/src/store/project.js                      |   6 +
 kystudio/src/store/types.js                        |  12 +
 kystudio/src/util/UtilTable.js                     |   2 +
 42 files changed, 1832 insertions(+), 51 deletions(-)

diff --git 
a/kystudio/src/components/common/DataSourceModal/SourceHive/SourceHive.vue 
b/kystudio/src/components/common/DataSourceModal/SourceHive/SourceHive.vue
index d83cc4e605..e4ee7c3d78 100644
--- a/kystudio/src/components/common/DataSourceModal/SourceHive/SourceHive.vue
+++ b/kystudio/src/components/common/DataSourceModal/SourceHive/SourceHive.vue
@@ -2,10 +2,10 @@
   <div class="source-hive clearfix" :class="{'zh-lang': 
$store.state.system.lang !== 'en'}">
     <div class="list clearfix">
       <div class="ksd-ml-24 ksd-mt-24">
-        <el-input :placeholder="$t('filterTableName')" 
-                  v-model="filterText" 
-                  prefix-icon="el-icon-search" 
-                  @keyup.enter.native="handleFilter()" 
+        <el-input :placeholder="$t('filterTableName')"
+                  v-model="filterText"
+                  prefix-icon="el-icon-search"
+                  @keyup.enter.native="handleFilter()"
                   @clear="handleFilter()">
         </el-input>
       </div>
@@ -48,11 +48,11 @@
               :validateRegex="regex.validateDB"
               @validateFail="selectedDBValidateFail"
               @refreshData="refreshDBData"
-              splitChar="," 
+              splitChar=","
               :selectedlabels="selectDBNames"
               :allowcreate="true"
               :placeholder="$t('dbPlaceholder')"
-              @removeTag="removeSelectedDB" 
+              @removeTag="removeSelectedDB"
               :datamap="{label: 'label', value: 'value'}">
             </arealabel>
           </div>
@@ -68,11 +68,11 @@
               :validateRegex="regex.validateTable"
               @validateFail="selectedTableValidateFail"
               @refreshData="refreshTableData"
-              splitChar="," 
+              splitChar=","
               :selectedlabels="selectTablesNames"
               :allowcreate="true"
               :placeholder="$t('dbTablePlaceholder')"
-              @removeTag="removeSelectedTable" 
+              @removeTag="removeSelectedTable"
               :datamap="{label: 'label', value: 'value'}">
             </arealabel>
           </div>
@@ -90,22 +90,32 @@
         </div>
       </transition>
     </div>
-    <div :class="['sample-block', {'has-error': needSampling && errorMsg}]">
-      <span class="ksd-title-label-small 
ksd-mr-10">{{$t('samplingTitle')}}</span><el-switch
+    <div class="source-options">
+      <div :class="['sample-block', {'has-error': needSampling && errorMsg}]">
+        <span class="ksd-title-label-small 
ksd-mr-10">{{$t('samplingTitle')}}</span><el-switch
         @change="handleSampling"
         :value="needSampling"
         :active-text="$t('kylinLang.common.OFF')"
         :inactive-text="$t('kylinLang.common.ON')">
       </el-switch>
-      <div class="sample-desc ksd-mt-5">{{$t('sampleDesc')}}</div>
-      <div class="sample-desc">
-        {{$t('sampleDesc1')}}<el-input size="small" style="width: 110px;" 
class="ksd-mrl-5" v-number="samplingRows" :value="samplingRows" 
:disabled="!needSampling" :class="{'is-error': needSampling&&errorMsg}" 
@input="handleSamplingRows"></el-input>{{$t('sampleDesc2')}}
-        <div class="error-msg" v-if="needSampling&&errorMsg">{{errorMsg}}</div>
+        <div class="sample-desc ksd-mt-5">{{$t('sampleDesc')}}</div>
+        <div class="sample-desc">
+          {{$t('sampleDesc1')}}<el-input size="small" style="width: 110px;" 
class="ksd-mrl-5" v-number="samplingRows" :value="samplingRows" 
:disabled="!needSampling" :class="{'is-error': needSampling&&errorMsg}" 
@input="handleSamplingRows"></el-input>{{$t('sampleDesc2')}}
+          <div class="error-msg" 
v-if="needSampling&&errorMsg">{{errorMsg}}</div>
+        </div>
+      </div>
+      <div v-if="currentSelectedProjectInternalTableEnabled" 
:class="['sample-block', {'has-error': loadAsInternalTable && errorMsg}]">
+        <span class="ksd-title-label-small 
ksd-mr-10">{{$t('loadAsInternalTableOption')}}</span><el-switch
+        @change="handleInternalTableOption"
+        :value="loadAsInternalTable"
+        :active-text="$t('kylinLang.common.OFF')"
+        :inactive-text="$t('kylinLang.common.ON')">
+      </el-switch>
+        <div class="sample-desc 
ksd-mt-5">{{$t('loadAsInternalTableDesc')}}</div>
       </div>
     </div>
   </div>
 </template>
-
 <script>
 import Vue from 'vue'
 import { mapGetters, mapActions, mapState } from 'vuex'
diff --git 
a/kystudio/src/components/common/DataSourceModal/SourceHive/locales.js 
b/kystudio/src/components/common/DataSourceModal/SourceHive/locales.js
index a3bbcd679e..9894f30056 100644
--- a/kystudio/src/components/common/DataSourceModal/SourceHive/locales.js
+++ b/kystudio/src/components/common/DataSourceModal/SourceHive/locales.js
@@ -43,6 +43,8 @@ export default {
     loadTableTips2_3: ', and press the ENTER key to complete. Use a comma to 
separate multiple values.',
     loadTableTips3_1: 'Up to ',
     loadTableTips3_2: '1000 tables',
-    loadTableTips3_3: ' could be loaded per time.'
+    loadTableTips3_3: ' could be loaded per time.',
+    loadAsInternalTableOption: 'Load as native table',
+    loadAsInternalTableDesc: 'Default to load as non-partitioned table. You 
can set the partitioned column on the inner table management page'
   }
 }
diff --git a/kystudio/src/components/common/DataSourceModal/handler.js 
b/kystudio/src/components/common/DataSourceModal/handler.js
index 18c9e23008..c78e00da00 100644
--- a/kystudio/src/components/common/DataSourceModal/handler.js
+++ b/kystudio/src/components/common/DataSourceModal/handler.js
@@ -85,6 +85,8 @@ function _getLoadTableSubmitData (form) {
     tables: form.selectedTables,
     databases: form.selectedDatabases,
     need_sampling: form.needSampling,
+    load_as_internal: form.loadAsInternalTable,
+    storage_type: 'gluten',
     sampling_rows: form.samplingRows
   }
 }
diff --git a/kystudio/src/components/common/DataSourceModal/index.vue 
b/kystudio/src/components/common/DataSourceModal/index.vue
index 24232cf51b..ba4cf20f9c 100644
--- a/kystudio/src/components/common/DataSourceModal/index.vue
+++ b/kystudio/src/components/common/DataSourceModal/index.vue
@@ -29,6 +29,7 @@
         :selected-tables="form.selectedTables"
         :selected-databases="form.selectedDatabases"
         :need-sampling="form.needSampling"
+        :load-as-internal-table="form.loadAsInternalTable"
         :sampling-rows="form.samplingRows"
         @input="handleInputTableOrDatabase">
       </SourceHive>
diff --git a/kystudio/src/components/common/DataSourceModal/store.js 
b/kystudio/src/components/common/DataSourceModal/store.js
index 9f76fa1f12..467c921392 100644
--- a/kystudio/src/components/common/DataSourceModal/store.js
+++ b/kystudio/src/components/common/DataSourceModal/store.js
@@ -40,6 +40,7 @@ const initialState = JSON.stringify({
     selectedTables: [],
     selectedDatabases: [],
     needSampling: false,
+    loadAsInternalTable: false,
     samplingRows: 20000000,
     settings: {
       type: '',
diff --git a/kystudio/src/components/common/DropdownFilter/DropdownFilter.vue 
b/kystudio/src/components/common/DropdownFilter/DropdownFilter.vue
index 99fbb8528c..3fe176712f 100644
--- a/kystudio/src/components/common/DropdownFilter/DropdownFilter.vue
+++ b/kystudio/src/components/common/DropdownFilter/DropdownFilter.vue
@@ -326,8 +326,8 @@ export default class DropdownFilter extends Vue {
           type="datetimerange"
           align="left"
           ref="$datePicker"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
+          start-placeholder={this.$t('startDatePlaceholder')}
+          end-placeholder={this.$t('endDatePlaceholder')}
           onInput={this.handleInput}
           picker-options={pickerOptions}
           onBlur={() => this.handleSetDropdown(false)}
diff --git a/kystudio/src/components/common/DropdownFilter/locales.js 
b/kystudio/src/components/common/DropdownFilter/locales.js
index 71dab10437..af92455242 100644
--- a/kystudio/src/components/common/DropdownFilter/locales.js
+++ b/kystudio/src/components/common/DropdownFilter/locales.js
@@ -3,6 +3,8 @@ export default {
     clearSelectItems: 'Clear selected items',
     lastDay: 'Last 1 day',
     lastWeek: 'Last 7 days',
-    lastMonth: 'Last 30 days'
+    lastMonth: 'Last 30 days',
+    startDatePlaceholder: 'Start Date',
+    endDatePlaceholder: 'End Date'
   }
 }
diff --git a/kystudio/src/components/common/EditableBlock/EditableBlock.vue 
b/kystudio/src/components/common/EditableBlock/EditableBlock.vue
index 395bc7a8c5..88db619047 100644
--- a/kystudio/src/components/common/EditableBlock/EditableBlock.vue
+++ b/kystudio/src/components/common/EditableBlock/EditableBlock.vue
@@ -9,7 +9,7 @@
       </i>
     </div>
     <div class="block-body" :class="{'no-footer': !isEditing}">
-      <slot></slot>
+      <slot :handleSubmit="handleSubmit"></slot>
     </div>
     <div class="block-foot ksd-btn-group-minwidth" v-if="isEditing">
       <el-button size="small" v-if="isReset" :loading="isResetLoading" 
@click="handleCancel">{{cancelText}}</el-button><el-button
diff --git a/kystudio/src/components/layout/layout_left_right_top.vue 
b/kystudio/src/components/layout/layout_left_right_top.vue
index 9247310a00..5095186ba1 100644
--- a/kystudio/src/components/layout/layout_left_right_top.vue
+++ b/kystudio/src/components/layout/layout_left_right_top.vue
@@ -187,7 +187,8 @@ let MessageBox = ElementUI.MessageBox
       'availableMenus',
       'isOnlyQueryNode',
       'dashboardActions',
-      'isTestingSecurityProfile'
+      'isTestingSecurityProfile',
+      'currentSelectedProjectInternalTableEnabled'
     ]),
     canAddProject () {
       // 模型编辑页面的时候,新增项目的按钮不可点
@@ -332,11 +333,13 @@ export default class LayoutLeftRightTop extends Vue {
   showMenuByRole (menuName) {
     switch (menuName) {
       case 'snapshot':
-        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.$store.state.project.snapshot_manual_management_enabled
+        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.$store.state.project.snapshot_manual_management_enabled && 
!this.currentSelectedProjectInternalTableEnabled
+      case 'internalTable':
+        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.currentSelectedProjectInternalTableEnabled
       case 'ddl':
-        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.$store.state.system.ddlEnabled === 'true'
+        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.$store.state.system.ddlEnabled === 'true' && 
!this.currentSelectedProjectInternalTableEnabled
       case 'logicalView':
-        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.$store.state.system.logicalViewEnabled === 'true'
+        return this.availableMenus.includes(menuName.toLowerCase()) && 
this.$store.state.system.logicalViewEnabled === 'true' && 
!this.currentSelectedProjectInternalTableEnabled
       default:
         return this.availableMenus.includes(menuName.toLowerCase())
     }
diff --git a/kystudio/src/components/monitor/batchJobs/handler.js 
b/kystudio/src/components/monitor/batchJobs/handler.js
index fdd02e8e54..ca29d36547 100644
--- a/kystudio/src/components/monitor/batchJobs/handler.js
+++ b/kystudio/src/components/monitor/batchJobs/handler.js
@@ -11,7 +11,9 @@ export function getStepLineName (that, name) {
     'Build Snapshot': that.$t('buildSnapshot'),
     'Clean Up Intermediate Table': that.$t('clearUpIntermediateTable'),
     'Optimize layout data': that.$t('LAYOUT_DATA_OPTIMIZE'),
-    LAYOUT_DATA_OPTIMIZE: that.$t('LAYOUT_DATA_OPTIMIZE')
+    LAYOUT_DATA_OPTIMIZE: that.$t('LAYOUT_DATA_OPTIMIZE'),
+    'Load Internal Table': that.$t('loadInternalTableStep'),
+    'Load Gluten Cache': that.$t('loadGlutenCacheStep')
   }
   return stepMap[name]
 }
@@ -38,7 +40,9 @@ export function getSubTasksName (that, name) {
     'delete useless layout data': that.$t('deleteUselessLayoutData'),
     'Optimize layout data by repartition': 
that.$t('optimizeLayoutDataByRepartition'),
     'Optimize layout data by zorder': that.$t('optimizeLayoutDataByZorder'),
-    'Optimize layout data by compaction': 
that.$t('optimizeLayoutDataByCompaction')
+    'Optimize layout data by compaction': 
that.$t('optimizeLayoutDataByCompaction'),
+    'Load Internal Table': that.$t('loadInternalTableStep'),
+    'Load Gluten Cache': that.$t('loadGlutenCacheStep')
   }
   return subTaskNameMap[name]
 }
diff --git a/kystudio/src/components/monitor/batchJobs/jobs.vue 
b/kystudio/src/components/monitor/batchJobs/jobs.vue
index 83f139c583..633217b8d9 100644
--- a/kystudio/src/components/monitor/batchJobs/jobs.vue
+++ b/kystudio/src/components/monitor/batchJobs/jobs.vue
@@ -38,7 +38,7 @@
           ref="jobsTable"
           :data="jobsList"
           highlight-current-row
-          :default-sort = "{prop: 'create_time', order: 'descending'}"
+          :default-sort="{prop: 'create_time', order: 'descending'}"
           :empty-text="emptyText"
           @sort-change="sortJobList"
           @selection-change="handleSelectionChange"
@@ -85,8 +85,8 @@
                 <common-tip :content="$t('snapshotDisableTips')" 
v-if="['SNAPSHOT_BUILD', 'SNAPSHOT_REFRESH'].includes(scope.row.job_name) && 
!scope.row.target_subject_error && 
!$store.state.project.snapshot_manual_management_enabled">
                   <span class="is-disabled">{{scope.row.target_subject}}</span>
                 </common-tip>
-                <a class="link" v-if="['SNAPSHOT_BUILD', 
'SNAPSHOT_REFRESH'].includes(scope.row.job_name) && 
$store.state.project.snapshot_manual_management_enabled&&!scope.row.target_subject_error"
 @click="gotoSnapshotList(scope.row)">{{scope.row.target_subject}}</a>
-                <a class="link" 
v-if="!tableJobTypes.includes(scope.row.job_name)&&!scope.row.target_subject_error"
 @click="gotoModelList(scope.row)">{{scope.row.target_subject}}</a>
+                <a class="link" v-if="scope.row.job_name === null" 
@click="gotoInternalTableList(scope.row)">{{scope.row.target_subject}}</a>
+                <a class="link" v-if="scope.row.job_name !== null && 
!tableJobTypes.includes(scope.row.job_name) && !scope.row.target_subject_error" 
@click="gotoModelList(scope.row)">{{scope.row.target_subject}}</a>
               </p>
             </template>
           </el-table-column>
@@ -633,6 +633,14 @@ export default class JobsList extends Vue {
     }
     return true
   }
+
+  gotoInternalTableList (item) {
+    // 暂停轮询,清掉计时器
+    clearTimeout(this.stCycle)
+    this.isPausePolling = true
+    this.$router.push({ name: 'InternalTable', params: { } })
+  }
+
   gotoModelList (item) {
     // 暂停轮询,清掉计时器
     clearTimeout(this.stCycle)
diff --git a/kystudio/src/components/monitor/batchJobs/locales.js 
b/kystudio/src/components/monitor/batchJobs/locales.js
index 2d8fef1a79..a9c3a46226 100644
--- a/kystudio/src/components/monitor/batchJobs/locales.js
+++ b/kystudio/src/components/monitor/batchJobs/locales.js
@@ -76,6 +76,7 @@ export default {
     SUB_PARTITION_REFRESH: 'Refresh Sub-partitions Data',
     SUB_PARTITION_BUILD: 'Build Sub-partitions Data',
     SNAPSHOT_BUILD: 'Build Snapshot',
+    INTERNAL_TABLE_BUILD: 'Load internal table',
     LAYOUT_DATA_OPTIMIZE: 'Storage Optimization',
     clearUpIntermediateTable: 'Garbage CleanUp',
     project: 'Project',
@@ -145,6 +146,8 @@ export default {
     errorStepTips: 'Job interrupted, "{name}" step error',
     noErrorMsg: 'No error message',
     chRestartTips: 'Can\'t restart the current job where some data has been 
loaded.',
-    fullOptimization: 'Full Optimization'
+    fullOptimization: 'Full Optimization',
+    loadInternalTableStep: 'Load Internal Table',
+    loadGlutenCacheStep: 'Load Internal Table Cache'
   }
 }
diff --git a/kystudio/src/components/query/query_result.vue 
b/kystudio/src/components/query/query_result.vue
index b4446c8226..e6b4bde5f9 100644
--- a/kystudio/src/components/query/query_result.vue
+++ b/kystudio/src/components/query/query_result.vue
@@ -98,7 +98,18 @@
     </div>
     <div v-show="!isStop" class="result-block">
       <el-tabs v-model="activeResultType" class="ksd-mt-16" type="button" 
:class="{'en-model': $lang==='en'}" @tab-click="changeDataType">
-          <el-tab-pane :label="$t('dataBtn')" name="data">
+            <el-tab-pane v-if="extraoption.explain" :label="$t('explainTab')" 
name="explain">
+              <div>
+                <div 
class="explain-title">{{$t('explainCalcitePlanTitle')}}</div>
+                <div 
class="explain-content">{{extraoption.query_plan.calcite_plan}}</div>
+              </div>
+
+              <div>
+                <div 
class="explain-title">{{$t('explainSparkPlanTitle')}}</div>
+                <div 
class="explain-content">{{extraoption.query_plan.spark_plan}}</div>
+              </div>
+            </el-tab-pane>
+            <el-tab-pane v-if="!extraoption.explain" :label="$t('dataBtn')" 
name="data">
             <div class="grid-box narrowTable">
               <template v-if="!isStop">
                 <el-table
@@ -282,7 +293,10 @@ import IndexDetails from 
'../studio/StudioModel/ModelList/ModelAggregate/indexDe
       refreshLater: 'No results, please try again later',
       fetchError: 'Can\'t get the result as the record is missing',
       buildIndexAlertTip: 'No model index built. Build model index to speed up 
query.',
-      gotoBuild: 'Go to build'
+      gotoBuild: 'Go to build',
+      explainTab: 'Explain',
+      explainCalcitePlanTitle: 'Calcite Plan',
+      explainSparkPlanTitle: 'Spark Plan'
     }
   },
   filters: {
@@ -311,7 +325,7 @@ export default class queryResult extends Vue {
   pageSizeX = 30
   hasClickExportBtn = false
   isShowDetail = false
-  activeResultType = 'data'
+  activeResultType = this.extraoption.explain ? 'explain' : 'data'
   charts = {
     type: 'lineChart',
     dimension: '',
@@ -622,7 +636,7 @@ export default class queryResult extends Vue {
         this.tableData[i][m] = showNull(trans)
       }
     }
-    this.pagerTableData = Object.assign([], this.tableData)
+    this.pagerTableData = Object.assign([], this.tableData.map(array => ({ 
...array })))
     this.$nextTick(() => {
       this.$refs.tableLayout && this.$refs.tableLayout.doLayout()
     })
@@ -901,4 +915,16 @@ export default class queryResult extends Vue {
     color: @text-normal-color;
     font-family: Lato,"Noto Sans S Chinese",sans-serif;
   }
+  .explain-title {
+    font-weight: bold;
+    margin-top: 12px;
+  }
+  .explain-content {
+    white-space: pre;
+    overflow: auto;
+    border: 1px solid #e6ebf4;
+    border-radius: 6px;
+    padding: 8px;
+    max-height: 400px;
+  }
 </style>
diff --git a/kystudio/src/components/query/query_tab.vue 
b/kystudio/src/components/query/query_tab.vue
index d9cd4eb117..07db2490fe 100644
--- a/kystudio/src/components/query/query_tab.vue
+++ b/kystudio/src/components/query/query_tab.vue
@@ -18,6 +18,11 @@
           <el-form :model="queryForm" :inline="true" ref="queryForm" 
@submit.native.prevent class="demo-form-inline">
             <el-form-item v-show="showHtrace">
               <el-checkbox class="ksd-mt-4" v-model="queryForm.isHtrace" 
@change="changeTrace">{{$t('trace')}}</el-checkbox>
+            </el-form-item><el-form-item 
v-show="currentSelectedProjectInternalTableEnabled">
+              <el-checkbox class="ksd-mt-4" 
v-model="queryForm.queryInternalTable">{{$t('queryInternalTable')}}</el-checkbox>
+            <el-tooltip effect="dark" :content="$t('queryInternalTableTips')" 
placement="top">
+              <i class="el-ksd-icon-more_info_16 icon ksd-fs-16 
query-inernal-table-tips"></i>
+            </el-tooltip>
             </el-form-item><el-form-item>
               <el-checkbox class="ksd-mt-4" v-model="queryForm.hasLimit" 
@change="changeLimit">Limit</el-checkbox>
             </el-form-item><el-form-item :rules="limitRule" prop="listRows" 
:show-message="false">
@@ -84,7 +89,8 @@ import { kylinConfirm, handleSuccess, handleError } from 
'../../util/business'
   },
   computed: {
     ...mapGetters([
-      'currentSelectedProject'
+      'currentSelectedProject',
+      'currentSelectedProjectInternalTableEnabled'
     ]),
     ...mapState({
       config: state => state.config
@@ -107,7 +113,9 @@ import { kylinConfirm, handleSuccess, handleError } from 
'../../util/business'
       htraceTips: 'Please make sure Zipkin server is properly deployed 
according to the manual of performance diagnose package.',
       queryError: 'The query fails.',
       queryTimeOut: 'The request timeout, please check the network situation 
and Kylin service instance status.',
-      queryTimeOutInCloud: 'The request timeout, please check the network 
situation and Kylin 5 service instance status.'
+      queryTimeOutInCloud: 'The request timeout, please check the network 
situation and Kylin 5 service instance status.',
+      queryInternalTable: 'Query Internal Table',
+      queryInternalTableTips: 'After it is checked, the model matching phase 
is skipped and the inner table data is directly queried'
     }
   }
 })
@@ -115,7 +123,8 @@ export default class QueryTab extends Vue {
   queryForm = {
     hasLimit: true,
     listRows: 500,
-    isHtrace: false
+    isHtrace: false,
+    queryInternalTable: false
   }
   sourceSchema = ''
   saveQueryFormVisible = false
@@ -230,7 +239,8 @@ export default class QueryTab extends Vue {
           offset: 0,
           project: this.currentSelectedProject,
           sql: querySql,
-          stopId: this.activeQueryId
+          stopId: this.activeQueryId,
+          forcedToPushDown: this.queryForm.queryInternalTable
         }
         this.$emit('addTab', 'query', queryObj)
       } else {
@@ -386,6 +396,11 @@ export default class QueryTab extends Vue {
       position: relative;
       top: -2px;
     }
+    .query-inernal-table-tips {
+      position: relative;
+      bottom: 3px;
+      margin-left: 4px;
+    }
   }
   #queryPanelBox {
     .el-progress{
diff --git 
a/kystudio/src/components/setting/SettingAdvanced/SettingAdvanced.vue 
b/kystudio/src/components/setting/SettingAdvanced/SettingAdvanced.vue
index 00cfa01621..9cd3e4ca23 100644
--- a/kystudio/src/components/setting/SettingAdvanced/SettingAdvanced.vue
+++ b/kystudio/src/components/setting/SettingAdvanced/SettingAdvanced.vue
@@ -181,6 +181,7 @@
     </EditableBlock>
     <!-- Snapshot -->
     <EditableBlock
+      v-if="!this.currentSelectedProjectInternalTableEnabled"
       :header-content="$t('snapshotTitle')"
       :isEditable="false">
       <div class="setting-item">
@@ -384,7 +385,8 @@ import { pageRefTags, pageCount } from 'config'
       'currentProjectData',
       'settingActions',
       'configList',
-      'isShowSecondStorage'
+      'isShowSecondStorage',
+      'currentSelectedProjectInternalTableEnabled'
     ]),
     ...mapState({
       platform: state => state.config.platform,
diff --git 
a/kystudio/src/components/setting/SettingInternalTable/SettingInternalTable.vue 
b/kystudio/src/components/setting/SettingInternalTable/SettingInternalTable.vue
new file mode 100644
index 0000000000..cee36b319c
--- /dev/null
+++ 
b/kystudio/src/components/setting/SettingInternalTable/SettingInternalTable.vue
@@ -0,0 +1,95 @@
+<template>
+  <EditableBlock
+    :header-content="$t('switchTitle')"
+    :is-editable="false"
+    :is-reset="false"
+    @submit="(scb, ecb) => handleSubmit(scb, ecb)"
+    @cancel="handleCancel">
+    <template slot-scope="parentScope">
+      <el-form>
+        <div class="setting-item">
+          <span class="setting-label font-medium">{{$t('switchLabel')}}</span>
+          <el-switch
+            v-model="internal_table_enabled"
+            :active-text="$t('kylinLang.common.OFF')"
+            :inactive-text="$t('kylinLang.common.ON')"
+            @change="parentScope.handleSubmit">
+          </el-switch>
+        </div>
+        <div class="setting-item setting-desc">{{$t('functionDesc')}}</div>
+      </el-form>
+    </template>
+  </EditableBlock>
+</template>
+
+<script>
+import Vue from 'vue'
+import { Component } from 'vue-property-decorator'
+import { mapActions, mapGetters } from 'vuex'
+import * as types from 'src/store/types'
+import locales from './locales'
+import { pageRefTags } from '../../../config'
+import EditableBlock from '../../common/EditableBlock/EditableBlock.vue'
+
+@Component({
+  locales,
+  props: {
+    project: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  methods: {
+    ...mapActions({
+      updateInternalTableEnabled: types.UPDATE_INTERNAL_TABLE_ENABLED
+    })
+  },
+  computed: {
+    ...mapGetters([
+      'currentSelectedProjectInternalTableEnabled'
+    ])
+  },
+  components: {
+    EditableBlock
+  }
+})
+export default class SettingInternalTable extends Vue {
+  pageRefTags = pageRefTags
+
+  internal_table_enabled = false
+
+  created () {
+    this.internal_table_enabled = 
this.currentSelectedProjectInternalTableEnabled
+  }
+
+  handleSubmit (scb, ecb) {
+    // 需要二次确认
+    this.$confirm(this.$t('confirmPrompt', { status: 
this.internal_table_enabled ? this.$t('kylinLang.common.ON') : 
this.$t('kylinLang.common.OFF') }), this.$t('confirmTitle'), {
+      confirmButtonText: this.$t('kylinLang.common.submit'),
+      cancelButtonText: this.$t('kylinLang.common.cancel'),
+      centerButton: true,
+      type: 'warning'
+    }).then(async () => {
+      try {
+        await this.updateInternalTableEnabled({ project: this.project.project, 
internal_table_enabled: this.internal_table_enabled })
+        scb()
+        this.$emit('reload-setting')
+        this.$message({ type: 'success', message: 
this.$t('kylinLang.common.updateSuccess') })
+      } catch (e) {
+        ecb()
+      }
+    }).catch(() => {
+      this.handleCancel()
+      return ecb()
+    })
+  }
+
+  handleCancel () {
+    this.internal_table_enabled = !this.internal_table_enabled
+  }
+}
+</script>
+
+<style lang="less">
+@import '../../../assets/styles/variables.less';
+</style>
diff --git a/kystudio/src/components/setting/SettingInternalTable/locales.js 
b/kystudio/src/components/setting/SettingInternalTable/locales.js
new file mode 100644
index 0000000000..adc12a4cb5
--- /dev/null
+++ b/kystudio/src/components/setting/SettingInternalTable/locales.js
@@ -0,0 +1,16 @@
+export default {
+  en: {
+    switchTitle: 'Internal Table',
+    switchLabel: 'Internal Table Function',
+    functionDesc: 'After the inner table function is enabled, the query is not 
pressed down to the data source, but answered by the inner table.',
+    confirmTitle: 'Modify Internal Table Function',
+    confirmPrompt: 'Confirm to {status} internal table function?'
+  },
+  'zh-cn': {
+    switchTitle: '内表',
+    switchLabel: '内表功能',
+    functionDesc: '开启内表功能后,查询下压将不再下压至数据源,而由内表回答。',
+    confirmTitle: '修改内表功能',
+    confirmPrompt: '确认{status}内表功能?'
+  }
+}
diff --git a/kystudio/src/components/setting/handler.js 
b/kystudio/src/components/setting/handler.js
index 7c6b90e3c4..13e717f3c0 100644
--- a/kystudio/src/components/setting/handler.js
+++ b/kystudio/src/components/setting/handler.js
@@ -1,6 +1,7 @@
 export const viewTypes = {
   BASIC: 'basicSetting',
   ADVANCED: 'advanceSetting',
+  INTERNAL_TABLE: 'internalTable',
   MODEL: 'model'
 }
 
diff --git a/kystudio/src/components/setting/locales.js 
b/kystudio/src/components/setting/locales.js
index f2c85a91d4..d060b174a5 100644
--- a/kystudio/src/components/setting/locales.js
+++ b/kystudio/src/components/setting/locales.js
@@ -2,6 +2,7 @@ export default {
   'en': {
     basic: 'Basic Settings',
     advanced: 'Advanced Settings',
+    internalTable: 'Internal Table Settings',
     model: 'Model Settings',
     indexGroup: 'Index Group Rewrite Settings'
   }
diff --git a/kystudio/src/components/setting/setting.vue 
b/kystudio/src/components/setting/setting.vue
index 1e501c4565..dd5abdd9c8 100644
--- a/kystudio/src/components/setting/setting.vue
+++ b/kystudio/src/components/setting/setting.vue
@@ -11,6 +11,9 @@
         <el-tab-pane :label="$t('advanced')" :name="viewTypes.ADVANCED">
           <SettingAdvanced :project="projectSettings" 
@reload-setting="getCurrentSettings" 
@form-changed="handleFormChanged"></SettingAdvanced>
         </el-tab-pane>
+        <el-tab-pane :label="$t('internalTable')" 
:name="viewTypes.INTERNAL_TABLE">
+          <SettingInternalTable :project="projectSettings" 
@reload-setting="getCurrentSettings" 
@form-changed="handleFormChanged"></SettingInternalTable>
+        </el-tab-pane>
         <el-tab-pane :label="modelSetting" :name="viewTypes.MODEL">
           <SettingModel :project="currentProjectData"></SettingModel>
         </el-tab-pane>
@@ -31,6 +34,7 @@ import { viewTypes } from './handler'
 import { handleError, handleSuccessAsync, cacheSessionStorage } from 
'../../util'
 import SettingBasic from './SettingBasic/SettingBasic.vue'
 import SettingAdvanced from './SettingAdvanced/SettingAdvanced.vue'
+import SettingInternalTable from 
'./SettingInternalTable/SettingInternalTable.vue'
 import SettingModel from './SettingModel/SettingModel.vue'
 
 @Component({
@@ -51,6 +55,7 @@ import SettingModel from './SettingModel/SettingModel.vue'
   components: {
     SettingBasic,
     SettingAdvanced,
+    SettingInternalTable,
     SettingModel
   },
   locales
@@ -62,7 +67,8 @@ export default class Setting extends Vue {
   projectSettings = null
   changedForm = {
     isBasicSettingChange: false,
-    isAdvanceSettingChange: false
+    isAdvanceSettingChange: false,
+    isInternalTableSettingChange: false
   }
   get modelSetting () {
     return this.$t('model')
diff --git 
a/kystudio/src/components/studio/InternalTable/DataManagement/DataManagement.vue
 
b/kystudio/src/components/studio/InternalTable/DataManagement/DataManagement.vue
new file mode 100644
index 0000000000..70f62945ff
--- /dev/null
+++ 
b/kystudio/src/components/studio/InternalTable/DataManagement/DataManagement.vue
@@ -0,0 +1,285 @@
+<template>
+  <el-dialog
+    :visible="open"
+    :title="dialogTitle"
+    class='data-management'
+    :before-close="handleDataClose">
+    <div v-show="!showLoadData">
+      <div>
+        <div class="ksd-fleft ky-no-br-space">
+          <div class="ke-it-other_actions ksd-fleft">
+            <!-- <el-button type="primary" text icon="el-ksd-icon-refresh_22" 
:disabled="selectedRows.length === 0" 
@click="handleRefreshData">{{$t('kylinLang.common.refresh')}}</el-button> -->
+            <el-button type="primary" text icon="el-ksd-icon-table_delete_22" 
:disabled="selectedRows.length === 0" 
@click="handleDeletePartitions">{{$t('kylinLang.common.delete')}}</el-button>
+            <el-button type="primary" text icon="el-ksd-icon-build_index_22" 
@click="handleShowLoadData">{{$t('loadData')}}</el-button>
+          </div>
+        </div>
+      </div>
+
+      <el-table
+        :data="internalTableDataList"
+        class="data-list-table"
+        style="width: 100%"
+        @selection-change="handleSelectionChange"
+        v-loading="internalTableDataListLoading">
+        <el-table-column
+          type="selection"
+          width="40">
+        </el-table-column>
+
+        <el-table-column
+          prop="uuid"
+          :label="$t('idColumnTitle')"
+          width="80"
+        >
+          <template slot-scope="scope">
+            <el-tooltip :content="'uuid: ' + scope.row.uuid" 
placement="follow-mouse">
+              <span class="text-ellipsis">{{scope.row.uuid}}</span>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+
+        <el-table-column
+          prop="storage_path"
+          :label="$t('partitionColumnTitle')"
+          width="400"
+        >
+        </el-table-column>
+
+        <el-table-column
+          prop="size_in_bytes"
+          :label="$t('storageVolumnColumnTitle')"
+          width="130"
+        >
+        </el-table-column>
+
+        <el-table-column
+          prop="file_count"
+          :label="$t('fileCountColumnTitle')"
+          width="100"
+        >
+        </el-table-column>
+
+        <el-table-column
+          prop="last_modified"
+          :formatter="dateFormatter"
+          :label="$t('updateColumnTitle')"
+          width="150"
+        >
+        </el-table-column>
+      </el-table>
+      <kap-pager
+        :totalSize="internalTableDataListTotal"
+        :perPageSize="internalTableDataListPageLimit"
+        :curPage="internalTableDataListPageOffset + 1"
+        @handleCurrentChange='handlePagerChange'
+        class="ksd-mtb-16 ksd-center">
+      </kap-pager>
+    </div>
+    <LoadData v-show="showLoadData" :tableInfo="tableInfo" 
:handleClose="handleCloseLoadData"/>
+  </el-dialog>
+</template>
+<script>
+import Vue from 'vue'
+import { Component, Watch } from 'vue-property-decorator'
+import { mapActions } from 'vuex'
+import dayjs from 'dayjs'
+import LoadData from './LoadData.vue'
+import * as types from 'src/store/types'
+import locales from './locales'
+
+@Component({
+  locales,
+  components: {
+    LoadData
+  },
+  props: {
+    open: {
+      type: Boolean,
+      default: false
+    },
+    openAction: {
+      type: String,
+      default: ''
+    },
+    tableInfo: {
+      type: Object
+    },
+    handleClose: {
+      type: Function,
+      default: () => {}
+    }
+  },
+
+  methods: {
+    ...mapActions({
+      getInternalTableDetails: types.GET_INTERNAL_TABLE_DETAILS,
+      loadSingleInternalTable: types.LOAD_SINGLE_INTERNAL_TABLE,
+      deletePartitions: types.DELETE_INTERNAL_TABLE_PARTITIONS,
+      deleteAllData: types.DELETE_ALL_DATA_INTERNAL_TABLE
+    })
+  },
+
+  computed: {
+    dialogTitle () {
+      return this.showLoadData ? this.$t('dialogLoadDataTitle') : 
this.$t('dialogTitle', { tableName: this.tableInfo.name })
+    }
+  }
+})
+export default class DataManagement extends Vue {
+  internalTableDataList = []
+  internalTableDataListTotal = 0
+  internalTableDataListPageOffset = 0
+  internalTableDataListPageLimit = 20
+  internalTableDataListLoading = false
+
+  showLoadData = false
+
+  selectedRows = []
+
+  @Watch('tableInfo')
+  tableInfoChanged (newVal, oldVal) {
+    if (newVal.database) {
+      this._getInternalTableDetails(this.internalTableDataListPageOffset, 
this.internalTableDataListPageLimit)
+    }
+  }
+
+  _getInternalTableDetails (page, pageSize, afterLoadData = false) {
+    if (!this.internalTableDataListLoading) {
+      this.internalTableDataListLoading = true
+      this.getInternalTableDetails({
+        database: this.tableInfo.database,
+        table: this.tableInfo.name,
+        project: this.tableInfo.project,
+        page_offset: page,
+        page_size: pageSize
+      }).then(result => {
+        this.internalTableDataList = (result.data.data && 
result.data.data.value) || []
+        this.internalTableDataListTotal = (result.data.data && 
result.data.data.total_size) || 0
+        this.internalTableDataListPageOffset = (result.data.data && 
result.data.data.offset) || 0
+        this.internalTableDataListPageLimit = (result.data.data && 
result.data.data.limit) || 0
+      }).finally(() => {
+        this.internalTableDataListLoading = false
+        this.showLoadData = afterLoadData ? false : this.openAction === 
'dataManagementLoadData'
+      })
+    }
+  }
+
+  dateFormatter (row) {
+    return dayjs(row.last_modified).format('YYYY-MM-DD HH:mm:ss')
+  }
+
+  handlePagerChange (page, pageSize) {
+    this._getInternalTableDetails(page, pageSize)
+  }
+
+  handleShowLoadData () {
+    this.showLoadData = true
+  }
+
+  handleCloseLoadData () {
+    this.showLoadData = false
+    if (this.openAction === 'dataManagementLoadData') this.handleClose()
+    else this._getInternalTableDetails(this.internalTableDataListPageOffset, 
this.internalTableDataListPageLimit, true)
+  }
+
+  handleDataClose () {
+    if (this.showLoadData) {
+      this.handleCloseLoadData()
+    } else {
+      this.handleClose()
+    }
+  }
+
+  handleSelectionChange (values) {
+    this.selectedRows = values
+  }
+
+  handleRefreshData () {
+    !this.internalTableDataListLoading && 
this.$confirm(this.$t('confirmRefreshDataPrompt', {}), 
this.$t('confirmRefreshTitle'), {
+      confirmButtonText: this.$t('kylinLang.common.submit'),
+      cancelButtonText: this.$t('kylinLang.common.cancel'),
+      centerButton: true,
+      type: 'warning'
+    }).then(() => {
+      this.internalTableDataListLoading = true
+      this.loadSingleInternalTable({
+        database: this.tableInfo.database,
+        table: this.tableInfo.name,
+        project: this.tableInfo.project,
+
+        incremental: false,
+        refresh: true,
+        yarn_queue: 'default'
+      }).then(() => {
+        this.$message({ type: 'success', message: 
this.$t('refreshDataSuccessTips', { tableName: this.tableInfo.name }) })
+      }).finally(() => {
+        this.internalTableDataListLoading = false
+      })
+    })
+  }
+
+  handleDeletePartitions () {
+    if (this.internalTableDataListLoading) {
+      return false
+    }
+    if (this.selectedRows.length === 0) {
+      this.$confirm(this.$t('confirmDeleteAllDataPrompt', {}), 
this.$t('confirmDeleteAllDataTitle'), {
+        confirmButtonText: this.$t('kylinLang.common.submit'),
+        cancelButtonText: this.$t('kylinLang.common.cancel'),
+        centerButton: true,
+        type: 'warning'
+      }).then(async () => {
+        this.internalTableDataListLoading = true
+        this.deleteAllData({
+          table: `${this.tableInfo.database}.${this.tableInfo.name}`,
+          project: this.tableInfo.project
+        }).finally(() => {
+          this.internalTableDataListLoading = false
+        }).then(
+          () => 
this._getInternalTableDetails(this.internalTableDataListPageOffset, 
this.internalTableDataListPageLimit)
+        )
+      })
+    } else {
+      this.$confirm(this.$t('confirmDeletePartitionsPrompt', {}), 
this.$t('confirmDeletePartionsTitle'), {
+        confirmButtonText: this.$t('kylinLang.common.submit'),
+        cancelButtonText: this.$t('kylinLang.common.cancel'),
+        centerButton: true,
+        type: 'warning'
+      }).then(async () => {
+        this.internalTableDataListLoading = true
+        this.deletePartitions({
+          table: `${this.tableInfo.database}.${this.tableInfo.name}`,
+          project: this.tableInfo.project,
+          partitions: this.selectedRows.map(row => 
row.partition_value).join(',')
+        }).finally(() => {
+          this.internalTableDataListLoading = false
+        }).then(
+          () => 
this._getInternalTableDetails(this.internalTableDataListPageOffset, 
this.internalTableDataListPageLimit)
+        )
+      })
+    }
+  }
+}
+</script>
+<style lang="less">
+  .data-management {
+    position: absolute;
+
+    .el-dialog {
+      width: 960px;
+      padding-bottom: 24px;
+
+      .el-dialog__body {
+        overflow: visible !important;
+      }
+      .data-list-table {
+        max-height: 430px;
+        overflow: auto;
+      }
+    }
+  }
+  .text-ellipsis {
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+</style>
diff --git 
a/kystudio/src/components/studio/InternalTable/DataManagement/LoadData.vue 
b/kystudio/src/components/studio/InternalTable/DataManagement/LoadData.vue
new file mode 100644
index 0000000000..4a18c79096
--- /dev/null
+++ b/kystudio/src/components/studio/InternalTable/DataManagement/LoadData.vue
@@ -0,0 +1,213 @@
+<template>
+  <div><el-form :model="ruleForm" :rules="rules" ref="ruleForm">
+    <el-tabs v-model="activeTab" type="card">
+      <el-tab-pane name="append" :disabled="!tableInfo.date_partition_format">
+        <span slot="label">{{$t('loadModelAppend')}}</span>
+        <div>
+          <el-row :gutter="20">
+            <el-col :span="16">
+              <div class="sub-title">{{$t('timePartitionOptionsTitle')}}</div>
+              <div>
+                <el-form-item prop="timePartitionColumn">
+                  <el-select v-model="ruleForm.timePartitionColumn" 
class='max-width' disabled>
+                    <el-option
+                      v-for="item in timePartitionOptions"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value">
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </div>
+            </el-col>
+            <el-col :span="8">
+              <div class="sub-title">{{$t('timePartitionFormatTitle')}}</div>
+              <div>
+                <el-form-item prop="timePartitionFormat">
+                  <el-select v-model="ruleForm.timePartitionFormat" 
class='max-width' disabled>
+                    <el-option
+                      v-for="item in timePartitionFormatOptions"
+                      :key="item.value"
+                      :label="item.label"
+                      :value="item.value">
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </div>
+            </el-col>
+          </el-row>
+          <div class="split-block">
+            <el-form-item prop="dateRange">
+              <el-date-picker
+                :append-to-body="false"
+                v-model="ruleForm.dateRange"
+                type="datetimerange"
+                :range-separator="$t('dateRangeSeparator')"
+                :start-placeholder="$t('dateRangeStartPlaceholder')"
+                :end-placeholder="$t('dateRangeEndPlaceholder')">
+              </el-date-picker>
+            </el-form-item>
+          </div>
+        </div>
+      </el-tab-pane>
+      <el-tab-pane name="full">
+        <span slot="label">{{$t('loadModelFull')}}</span>
+        <div>
+          <div class="sub-title">{{$t('partitionOptionsTitle')}}</div>
+          <div>
+            <el-select v-model="ruleForm.partitionColumn" class='max-width' 
disabled>
+              <el-option
+                v-for="item in partitionOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value">
+              </el-option>
+            </el-select>
+          </div>
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+
+    <div class="split-block dialog-footer">
+      <el-button size="small" 
@click="this.handleClose">{{$t('kylinLang.common.cancel')}}</el-button><el-button
+      type="primary" size="small" 
@click="this.handleSave">{{$t('kylinLang.common.load')}}</el-button>
+    </div>
+  </el-form></div>
+</template>
+<script>
+import Vue from 'vue'
+import { Component, Watch } from 'vue-property-decorator'
+import { mapActions } from 'vuex'
+import * as types from 'src/store/types'
+import { handleSuccessAsync } from 'src/util'
+import { timePartitionFormatOptions } from '../const'
+
+@Component({
+  props: {
+    tableInfo: {
+      type: Object
+    },
+    handleClose: {
+      type: Function,
+      default: () => {}
+    }
+  },
+
+  methods: {
+    ...mapActions({
+      loadSingleInternalTable: types.LOAD_SINGLE_INTERNAL_TABLE
+    })
+  },
+
+  computed: {
+    timePartitionOptions () {
+      return (this.tableInfo.columns ? this.tableInfo.columns.filter(c => 
c.datatype === 'date').map(c => ({ label: c.name, value: c.name })) : [])
+    },
+    partitionOptions () {
+      return (this.tableInfo.columns ? this.tableInfo.columns.map(c => ({ 
label: c.name, value: c.name })) : [])
+    },
+    rules () {
+      return {
+        // timePartitionColumn: { required: this.activeTab === 'append', 
message: this.$t('validateErrorTimeColumnSelect'), trigger: 'change' },
+        // timePartitionFormat: { required: this.activeTab === 'append', 
message: this.$t('validateErrorTimeFormatSelect'), trigger: 'change' },
+        dateRange: { required: this.activeTab === 'append', message: 
this.$t('validateErrorDateRangeSelect'), trigger: 'change' }
+      }
+    }
+  },
+
+  locales: {
+    en: {
+      timePartitionOptionsTitle: 'Time Partition Column',
+      timePartitionFormatTitle: 'Time Format',
+      validateErrorTimeFormatSelect: 'Please select time format',
+      validateErrorTimeColumnSelect: 'Please select time partition column',
+      validateErrorDateRangeSelect: 'Please select date range',
+      confirmPrompt: 'Are you sure to load data as {model}?',
+      confirmTitle: 'Load Data',
+      partitionOptionsTitle: 'Partition Column',
+      dateRangeSeparator: 'to'
+    },
+    'zh-cn': {
+      timePartitionOptionsTitle: '时间分区列',
+      timePartitionFormatTitle: '时间格式',
+      loadModelAppend: '增量加载',
+      loadModelFull: '全量加载',
+      validateErrorTimeFormatSelect: '请选择时间格式',
+      validateErrorTimeColumnSelect: '请选择时间分区列',
+      validateErrorDateRangeSelect: '请选择时间范围',
+      confirmPrompt: '确定要{model}数据?',
+      confirmTitle: '加载数据',
+      partitionOptionsTitle: '分区列',
+      dateRangeSeparator: '至',
+      dateRangeStartPlaceholder: '开始日期',
+      dateRangeEndPlaceholder: '结束日期'
+    }
+  }
+})
+export default class DataManagement extends Vue {
+  activeTab = this.tableInfo.date_partition_format ? 'append' : 'full'
+
+  @Watch('tableInfo')
+  tableInfoChanged (newVal, oldVal) {
+    this.activeTab = this.tableInfo.date_partition_format ? 'append' : 'full'
+  }
+
+  ruleForm = {
+    timePartitionColumn: this.tableInfo.time_partition_col,
+    timePartitionFormat: this.tableInfo.date_partition_format,
+    dateRange: (() => {
+      const currentDate = new Date()
+      return [
+        new Date(currentDate.getFullYear(), currentDate.getMonth() - 1, 
currentDate.getDate() - 1, 10, 10),
+        new Date(currentDate.getFullYear(), currentDate.getMonth(), 
currentDate.getDate() - 1, 10, 10)
+      ]
+    })(),
+
+    partitionColumn: this.tableInfo.time_partition_col
+  }
+
+  loading = false
+
+  timePartitionFormatOptions = timePartitionFormatOptions
+
+  handleSave () {
+    !this.loading && this.$refs.ruleForm.validate((valid) => {
+      if (valid) {
+        this.$confirm(
+          this.$t('confirmPrompt', { model: this.activeTab === 'append' ? 
this.$t('loadModelAppend') : this.$t('loadModelFull') }),
+          this.$t('confirmTitle'),
+          {
+            confirmButtonText: this.$t('kylinLang.common.submit'),
+            cancelButtonText: this.$t('kylinLang.common.cancel'),
+            centerButton: true,
+            type: 'warning'
+          }
+        ).then(async () => {
+          this.loading = true
+          return await this.loadSingleInternalTable({
+            database: this.tableInfo.database,
+            table: this.tableInfo.name,
+            project: this.tableInfo.project,
+
+            incremental: this.activeTab === 'append',
+            refresh: false,
+            start_date: this.activeTab === 'append' ? 
this.ruleForm.dateRange[0].getTime() : null,
+            end_date: this.activeTab === 'append' ? 
this.ruleForm.dateRange[1].getTime() : null,
+            yarn_queue: 'default'
+          }).then(handleSuccessAsync).catch(() => {
+            this.$message.error(this.$t('saveFailed'))
+          }).finally(() => {
+            this.loading = false
+            this.handleClose()
+          })
+        })
+      }
+    })
+  }
+}
+</script>
+<style lang="less">
+  .el-tabs__content {
+    overflow: visible;
+  }
+</style>
diff --git 
a/kystudio/src/components/studio/InternalTable/DataManagement/locales.js 
b/kystudio/src/components/studio/InternalTable/DataManagement/locales.js
new file mode 100644
index 0000000000..1a22f17519
--- /dev/null
+++ b/kystudio/src/components/studio/InternalTable/DataManagement/locales.js
@@ -0,0 +1,36 @@
+export default {
+  en: {
+    dialogTitle: 'Internal table data management | {tableName}',
+    dialogLoadDataTitle: 'Internal Table Data Loading',
+    idColumnTitle: 'ID',
+    partitionColumnTitle: 'Partition Info',
+    storageVolumnColumnTitle: 'Storage Volumn',
+    fileCountColumnTitle: 'File Count',
+    updateColumnTitle: 'Last Update Time',
+    loadData: 'Load Data',
+    confirmRefreshDataPrompt: 'Confirm to full refresh data?',
+    confirmRefreshTitle: 'Refresh Data',
+    confirmDeletePartitionsPrompt: 'Confirm to delete data?',
+    confirmDeletePartionsTitle: 'Delete Data',
+    confirmDeleteAllDataPrompt: 'Are you sure to clear the data?',
+    confirmDeleteAllDataTitle: 'Clear Data',
+    refreshDataSuccessTips: 'Refreshing data job for internal table 
[{tableName}] has been submitted successfully, you can view the job progress in 
the Monitor page.'
+  },
+  'zh-cn': {
+    dialogTitle: '内表数据管理 | {tableName}',
+    dialogLoadDataTitle: '内表数据加载',
+    idColumnTitle: '序号',
+    partitionColumnTitle: '分区信息',
+    storageVolumnColumnTitle: '存储容量',
+    fileCountColumnTitle: '文件数量',
+    updateColumnTitle: '最后更新时间',
+    loadData: '加载数据',
+    confirmRefreshDataPrompt: '确认要全量刷新数据么?',
+    confirmRefreshTitle: '刷新数据',
+    confirmDeletePartitionsPrompt: '确认要删除数据么?',
+    confirmDeletePartionsTitle: '删除数据',
+    confirmDeleteAllDataPrompt: '确认要清空数据么?',
+    confirmDeleteAllDataTitle: '清空数据',
+    refreshDataSuccessTips: '刷新内表 [{tableName}] 任务提交成功,您可以在监控页面查看任务进度。'
+  }
+}
diff --git a/kystudio/src/components/studio/InternalTable/List/index.vue 
b/kystudio/src/components/studio/InternalTable/List/index.vue
new file mode 100644
index 0000000000..142553422b
--- /dev/null
+++ b/kystudio/src/components/studio/InternalTable/List/index.vue
@@ -0,0 +1,406 @@
+<template>
+  <div class="internal-table-container">
+    <div class="ksd-title-page">
+      {{$t('internalTable')}}
+      <common-tip placement="bottom-start" popper-class="internal-table-tips">
+        <div slot="content" class="snapshot-desc">
+          <div class="ksd-mb-8 snapshot-desc">{{$t('internalTableDesc')}}</div>
+          <p>*&nbsp;{{$t('internalTableDesc1')}}</p>
+          <p>*&nbsp;{{$t('internalTableDesc2')}}</p>
+          <p>*&nbsp;{{$t('internalTableDesc3')}}</p>
+        </div>
+        <i class="el-ksd-icon-more_info_22 internal-table-icon ksd-fs-22"></i>
+      </common-tip>
+      <!-- <el-button type="primary" class="ksd-fright" 
icon="el-ksd-icon-add_22">{{$t('kylinLang.common.add')}}</el-button> -->
+    </div>
+
+    <div>
+      <div class="ksd-fleft ky-no-br-space">
+        <div class="ke-it-other_actions ksd-fleft">
+          <!-- <el-button type="primary" text icon="el-ksd-icon-refresh_22"
+            :disabled="selectedRows.length <= 0"
+            @click="handleRefreshInternalTable"
+          >{{$t('kylinLang.common.refresh')}}</el-button> -->
+          <!-- <el-button type="primary" text 
icon="el-ksd-icon-table_delete_22"
+            :disabled="selectedRows.length > 1 || selectedRows.length === 0"
+            @click="handleDeleteInternalTable"
+          >{{$t('kylinLang.common.delete')}}</el-button> -->
+          <!-- <el-button type="primary" text 
icon="el-ksd-icon-repair_22">{{$t('kylinLang.common.repair')}}</el-button> -->
+        </div>
+      </div>
+      <!-- <el-input class="ksd-fright ke-it-search_internal-table" 
prefix-icon="el-ksd-icon-search_22" size="medium"></el-input> -->
+    </div>
+    <div style="margin-bottom: 10px;">
+      <el-tag size="small" closable v-for="(item, index) in filterTags" 
:key="index" @close="handleClose(item)" style="margin-left: 
5px;">{{item}}</el-tag>
+    </div>
+    <el-table
+      :data="internalTableList"
+      style="width: 100%"
+      v-loading="somethingLoading"
+      @selection-change="handleSelectionChange">
+      <el-table-column
+        type=""
+        width="55">
+      </el-table-column>
+      <el-table-column
+        :label="$t('tableNameTitle')"
+        sortable
+        sort-by="table_name"
+        width="340"
+      >
+        <template slot-scope="scope">
+          <span class="layout-with-actions">
+            <span class="">{{scope.row.table_name}}</span>
+            <span :disabled="scope.row.row_count > 0" class="hover-action">
+              <i class="icon el-ksd-icon-edit_22 hover-action-icon" 
@click="handleEditPartation(scope.row)"></i>
+            </span>
+            <span class="hover-action">
+              <i class="icon el-ksd-icon-build_index_22 hover-action-icon" 
@click="handleDataManagement(scope.row, true)"></i>
+            </span>
+            <el-dropdown @command="(command) => handleCommand(command, 
scope.row)" :id="scope.row.uuid" trigger="click" class="hover-action">
+              <span class="el-dropdown-link" >
+                <el-button type="primary" text 
icon="el-ksd-icon-more_16"></el-button>
+              </span>
+
+              <el-dropdown-menu slot="dropdown"  :uuid='scope.row.uuid' 
:append-to-body="true" :popper-container="'modelListPage'" 
class="specialDropdown">
+                <el-dropdown-item command="viewDetails" 
:disabled="!scope.row.time_partition_col">{{$t('menuViewDetails')}}</el-dropdown-item>
+                <el-dropdown-item 
command="deleteInternalTable">{{$t('menuDeleteInternalTable')}}</el-dropdown-item>
+                <el-dropdown-item 
command="clearData">{{$t('menuClearData')}}</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="database_name"
+        :label="$t('databaseTitle')"
+        width="180">
+      </el-table-column>
+      <el-table-column
+        :label="$t('partationTitle')"
+        width="220">
+        <template slot-scope="scope">
+          <span>{{scope.row.time_partition_col}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="hit_count"
+        :label="$t('usedTimesTitle')"
+        width="100">
+      </el-table-column>
+      <el-table-column
+        prop="row_count"
+        :label="$t('rowCountTitle')"
+        width="100">
+      </el-table-column>
+      <el-table-column
+        prop="storage_size"
+        :label="$t('storageSizeTitle')">
+      </el-table-column>
+      <el-table-column
+        prop="update_time"
+        sortable
+        sort-by="update_time"
+        :label="$t('lastUpdateTitle')"
+        :formatter="dateFormatter"
+        width="180">
+      </el-table-column>
+    </el-table>
+     <kap-pager :totalSize="internalTableListTotal" 
:perPageSize="internalTableListPageLimit" :curPage="internalTableListPageOffset 
+ 1" @handleCurrentChange='handlePagerChange' class="ksd-mtb-16 ksd-center" 
></kap-pager>
+
+     <InternalTableSetting :open="openDialog === 'internalTable'" 
:tableInfo="currentEditTableInfo" :handleClose="handleInternalSettingClose"/>
+     <DataManagement :open="openDialog === 'dataManagement' || openDialog === 
'dataManagementLoadData'" :openAction="openDialog" 
:tableInfo="currentEditTableInfo" :handleClose="handleDataManagementClose"/>
+  </div>
+</template>
+<script>
+import Vue from 'vue'
+import { mapActions, mapGetters, mapState } from 'vuex'
+import { Component } from 'vue-property-decorator'
+import { pageRefTags } from 'config'
+import dayjs from 'dayjs'
+import * as types from 'src/store/types'
+import InternalTableSetting from '../Setting/InternalTableSetting.vue'
+import DataManagement from '../DataManagement/DataManagement.vue'
+import locales from './locales'
+
+@Component({
+  locales,
+  components: {
+    InternalTableSetting,
+    DataManagement
+  },
+  methods: {
+    ...mapActions({
+      getInternalTables: types.GET_INTERNAL_TABLES,
+      fetchTableDetails: types.FETCH_TABLES,
+      deleteInternalTable: types.DELETE_INTERNAL_TABLE,
+      batchLoadInternalTables: types.BATCH_LOAD_INTERNAL_TABLES,
+      deleteAllData: types.DELETE_ALL_DATA_INTERNAL_TABLE
+    })
+  },
+  computed: {
+    ...mapGetters([
+      'currentSelectedProject'
+    ]),
+    ...mapState({
+      internalTableList: state => state.internalTable.internalTableList,
+      internalTableListTotal: state => 
state.internalTable.internalTableListTotal,
+      internalTableListPageOffset: state => 
state.internalTable.internalTableListPageOffset,
+      internalTableListPageLimit: state => 
state.internalTable.internalTableListPageLimit
+    })
+  }
+})
+export default class InternalTableList extends Vue {
+  pageRefTags = pageRefTags
+  openDialog = ''
+  somethingLoading = false
+
+  currentEditTableInfo = {}
+
+  selectedRows = []
+
+  filterTags = []
+
+  created () {
+    this._getInternalTables(this.internalTableListPageOffset, 
this.internalTableListPageLimit)
+  }
+
+  _getInternalTables (page, pageSize) {
+    if (!this.somethingLoading) {
+      this.somethingLoading = true
+      this.getInternalTables({
+        project: this.currentSelectedProject,
+        page_offset: page,
+        page_size: pageSize
+      }).finally(() => {
+        this.somethingLoading = false
+      })
+    }
+  }
+
+  dateFormatter (row) {
+    return dayjs(row.update_time).format('YYYY-MM-DD HH:mm:ss')
+  }
+
+  filterTag (value) {
+    this.filterTags = value
+  }
+
+  filterHandler (value, row, column) {
+    const property = column.property
+    return row[property] === value
+  }
+
+  handlePagerChange (page, pageSize) {
+    this._getInternalTables(page, pageSize)
+  }
+
+  handleInternalSettingClose () {
+    this.openDialog = ''
+    this._getInternalTables(this.internalTableListPageOffset, 
this.internalTableListPageLimit)
+  }
+
+  handleEditPartation (row) {
+    if (!this.somethingLoading && !(row.row_count > 0)) {
+      this.somethingLoading = true
+      this._fetchTableDetails(row).then(() => {
+        if (this.currentEditTableInfo) {
+          this.openDialog = 'internalTable'
+        }
+      }).finally(() => {
+        this.somethingLoading = false
+      })
+    }
+  }
+
+  _fetchTableDetails (row) {
+    return this.fetchTableDetails(
+      { projectName: this.currentSelectedProject, databaseName: 
row.database_name, tableName: row.table_name, isExt: false, pageSize: 1, 
sourceType: 9 }
+    ).then(result => {
+      if (result.data.data && result.data.data.tables[0]) {
+        const tableInfo = result.data.data.tables[0]
+        tableInfo.project = this.currentSelectedProject
+        tableInfo.time_partition_col = row.time_partition_col
+        tableInfo.date_partition_format = row.date_partition_format
+        this.currentEditTableInfo = tableInfo
+      }
+    })
+  }
+
+  handleDataManagementClose () {
+    this.openDialog = ''
+  }
+
+  handleDataManagement (row, loadData = false) {
+    this.somethingLoading = true
+    return this._fetchTableDetails(row).then(() => {
+      if (this.currentEditTableInfo) {
+        this.openDialog = loadData ? 'dataManagementLoadData' : 
'dataManagement'
+      }
+    }).finally(() => {
+      this.somethingLoading = false
+    })
+  }
+
+  handleSelectionChange (values) {
+    this.selectedRows = values
+  }
+
+  handleDeleteInternalTable () {
+    if (!this.somethingLoading) {
+      this.$confirm(this.$t('confirmDeleteInternalTablePrompt', {}), 
this.$t('confirmDeleteTitle'), {
+        confirmButtonText: this.$t('kylinLang.common.submit'),
+        cancelButtonText: this.$t('kylinLang.common.cancel'),
+        centerButton: true,
+        type: 'warning'
+      }).then(() => {
+        this.somethingLoading = true
+        Promise.all(this.selectedRows.map(row => this.deleteInternalTable({
+          project: this.currentSelectedProject,
+          database: row.database_name,
+          table: row.table_name
+        }))).finally(() => {
+          this.somethingLoading = false
+        }).then(results => {
+          this._getInternalTables(this.internalTableListPageOffset, 
this.internalTableListPageLimit)
+        })
+      })
+    }
+  }
+
+  handleDeleteSingleInternalTable (row) {
+    if (!this.somethingLoading) {
+      this.$confirm(this.$t('confirmDeleteInternalTablePrompt', {}), 
this.$t('confirmDeleteTitle'), {
+        confirmButtonText: this.$t('kylinLang.common.submit'),
+        cancelButtonText: this.$t('kylinLang.common.cancel'),
+        centerButton: true,
+        type: 'warning'
+      }).then(() => {
+        this.somethingLoading = true
+        this.deleteInternalTable({
+          project: this.currentSelectedProject,
+          database: row.database_name,
+          table: row.table_name
+        }).finally(() => {
+          this.somethingLoading = false
+        }).then(results => {
+          this._getInternalTables(this.internalTableListPageOffset, 
this.internalTableListPageLimit)
+        })
+      })
+    }
+  }
+
+  handleRefreshInternalTable () {
+    if (!this.somethingLoading) {
+      this.$confirm(this.$t('confirmRefreshInternalTablePrompt', {}), 
this.$t('confirmRefreshTitle'), {
+        confirmButtonText: this.$t('kylinLang.common.submit'),
+        cancelButtonText: this.$t('kylinLang.common.cancel'),
+        centerButton: true,
+        type: 'warning'
+      }).then(async () => {
+        this.somethingLoading = true
+        this.batchLoadInternalTables({
+          project: this.currentSelectedProject,
+          tables: this.selectedRows.map(row => 
`${row.database_name}.${row.table_name}`),
+          incremental: false,
+          refresh: true
+        }).finally(() => {
+          this.somethingLoading = false
+        })
+      })
+    }
+  }
+
+  clearSingleTableData (row) {
+    this.$confirm(this.$t('confirmDeleteAllDataPrompt', {}), 
this.$t('confirmDeleteAllDataTitle'), {
+      confirmButtonText: this.$t('kylinLang.common.submit'),
+      cancelButtonText: this.$t('kylinLang.common.cancel'),
+      centerButton: true,
+      type: 'warning'
+    }).then(async () => {
+      this.somethingLoading = true
+      this.deleteAllData({
+        table: `${row.database_name}.${row.table_name}`,
+        project: this.currentSelectedProject
+      }).finally(() => {
+        this.somethingLoading = false
+      })
+    })
+  }
+
+  handleCommand (command, row) {
+    switch (command) {
+      case 'viewDetails':
+        this.handleDataManagement(row, false)
+        break
+      case 'deleteInternalTable':
+        this.handleDeleteSingleInternalTable(row)
+        break
+      case 'clearData':
+        this.clearSingleTableData(row)
+        break
+    }
+  }
+}
+</script>
+<style lang="less">
+  .internal-table-container {
+    margin: 32px 24px 24px;
+  }
+  .ke-it-search_internal-table {
+    width: 248px;
+  }
+  .internal-table-icon {
+    position: relative;
+    bottom: 5px;
+    left: -5px;
+  }
+  .layout-with-actions {
+    display: flex;
+    column-gap: 8px;
+    justify-content: space-between;
+    align-items: center;
+
+    :first-child {
+      flex: 1
+    }
+  }
+  .el-table__row {
+    .hover-action {
+      visibility: hidden;
+      cursor: pointer;
+    }
+    &:hover {
+      .hover-action {
+        visibility: visible;
+      }
+    }
+
+    .hover-action-icon {
+      vertical-align: middle;
+      font-size: 22px;
+      color: #2f374c;
+    }
+
+    .el-dropdown-link{
+      top: 2px;
+      position: relative;
+
+      .el-button--medium {
+        padding: 5px 0;
+      }
+    }
+
+    [disabled] .icon {
+      opacity: 0.3;
+      cursor: not-allowed;
+    }
+  }
+  .common-tip-layout {
+    max-width: 420px !important;
+
+    :first-child {
+      max-height: 100% !important;
+    }
+  }
+</style>
diff --git a/kystudio/src/components/studio/InternalTable/List/locales.js 
b/kystudio/src/components/studio/InternalTable/List/locales.js
new file mode 100644
index 0000000000..637e1b3d0e
--- /dev/null
+++ b/kystudio/src/components/studio/InternalTable/List/locales.js
@@ -0,0 +1,48 @@
+export default {
+  en: {
+    internalTable: 'Internal Table',
+    internalTableDesc: 'Internal Table',
+    internalTableDesc1: 'Data can be deleted from a non-partitioned table only 
in full',
+    internalTableDesc2: 'A partition table can be deleted by partition or in 
ful',
+    internalTableDesc3: 'Batch deletion of multiple tables supports only full 
deletion',
+    tableNameTitle: 'Table Name',
+    databaseTitle: 'Database',
+    partationTitle: 'Partation Colomns',
+    usedTimesTitle: 'Used Count',
+    rowCountTitle: 'Row Count',
+    storageSizeTitle: 'Storage Size',
+    lastUpdateTitle: 'Last Modify Time',
+    confirmDeleteTitle: 'Delete Internal Table',
+    confirmDeleteInternalTablePrompt: 'Are you sure to delete?',
+    confirmRefreshTitle: 'Refresh Internal Table',
+    confirmRefreshInternalTablePrompt: 'Are you sure to refresh selected 
tables?',
+    menuViewDetails: 'View Details',
+    menuDeleteInternalTable: 'Delete',
+    menuClearData: 'Clear Data',
+    confirmDeleteAllDataPrompt: 'Are you sure to clear the data?',
+    confirmDeleteAllDataTitle: 'Clear Data'
+  },
+  'zh-cn': {
+    internalTable: '内表',
+    internalTableDesc: '内表',
+    internalTableDesc1: '删除数据,非分区表仅支持全量删除',
+    internalTableDesc2: '分区表支持按分区删除或全量删除',
+    internalTableDesc3: '多个表批量删除仅支持全量删除',
+    tableNameTitle: '表名称',
+    databaseTitle: '数据库',
+    partationTitle: '分区列',
+    usedTimesTitle: '使用次数',
+    rowCountTitle: '行数',
+    storageSizeTitle: '存储大小',
+    lastUpdateTitle: '最后更新时间',
+    confirmDeleteTitle: '删除内表',
+    confirmDeleteInternalTablePrompt: '确认要删除内表么?',
+    confirmRefreshTitle: '刷新内表',
+    confirmRefreshInternalTablePrompt: '确认要刷新内表么?',
+    menuViewDetails: '查看详情',
+    menuDeleteInternalTable: '删除内表',
+    menuClearData: '清空数据',
+    confirmDeleteAllDataPrompt: '确认要清空数据么?',
+    confirmDeleteAllDataTitle: '清空数据'
+  }
+}
diff --git 
a/kystudio/src/components/studio/InternalTable/Setting/InternalTableSetting.vue 
b/kystudio/src/components/studio/InternalTable/Setting/InternalTableSetting.vue
new file mode 100644
index 0000000000..09476ffb55
--- /dev/null
+++ 
b/kystudio/src/components/studio/InternalTable/Setting/InternalTableSetting.vue
@@ -0,0 +1,374 @@
+<template>
+  <el-dialog
+    :visible="open"
+    :title="$t('dialogTitle')"
+    :class="'internal-table-setting' + (selectTableAttributeKeys ? ' 
with-selector' : '')"
+    :before-close="this.handleClose">
+    <el-form :model="ruleForm" :rules="rules" ref="ruleForm">
+      <div v-loading="loading">
+        <div>{{$t('settingDesc')}}</div>
+
+        <el-row :gutter="20" class="split-block">
+          <el-col :span="16">
+            <div class="sub-title">{{$t('partitionOptionsTitle')}}</div>
+            <div>
+              <el-select v-model="ruleForm.partitionColumn" 
:placeholder="$t('partitionDefaultOptionLabel')" clearable class='max-width'>
+                <el-option
+                  v-for="item in partitionOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value">
+                </el-option>
+              </el-select>
+            </div>
+          </el-col>
+          <el-col :span="8" v-show="doesPartionColumnNeedDateFormatter">
+            <div class="sub-title">{{$t('timePartitionFormatTitle')}}</div>
+            <div>
+              <el-form-item prop="timePartitionFormat">
+                <el-select v-model="ruleForm.timePartitionFormat" clearable 
class='max-width'>
+                  <el-option
+                    v-for="item in timePartitionFormatOptions"
+                    :key="item.value"
+                    :label="item.label"
+                    :value="item.value">
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </div>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20" class="split-block">
+          <el-col :span="16">
+            <div class="sub-title">{{$t('bucketColumnTitle')}}</div>
+            <div>
+              <el-select v-model="ruleForm.bucketColumn" 
:placeholder="$t('bucketDefaultOptionLabel')" clearable class='max-width'>
+                <el-option
+                  v-for="item in bucketOptions"
+                  :key="item.value"
+                  :label="item.label"
+                  :value="item.value">
+                </el-option>
+              </el-select>
+            </div>
+          </el-col>
+          <el-col :span="8" v-show="ruleForm.bucketColumn">
+            <div class="sub-title">{{$t('bucketCountTitle')}}</div>
+            <div>
+              <el-form-item prop="bucketCount">
+                <el-input v-model.number="ruleForm.bucketCount"></el-input>
+              </el-form-item>
+            </div>
+          </el-col>
+        </el-row>
+
+        <div class="split-block">
+          <span>{{$t('addNativeTableAttributeTitle')}}</span><el-tooltip 
effect="dark" :content="$t('attributeSettingTips')" placement="top" 
popper-class="internal-table-tips">
+            <i class="el-ksd-icon-more_info_16 icon ksd-fs-16 
query-inernal-table-tips"></i>
+          </el-tooltip>
+        </div>
+        <el-table :data="ruleForm.selectedTableAttributeKeys" 
:show-empty-img="false">
+          <el-table-column
+            :label="$t('attributeSettingPrimaryTitle')"
+            width="180">
+            <template slot-scope="scope">
+              {{scope.row.primaryKey ? scope.row.name: ''}}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="$t('attributeSettingSortByTitle')"
+            width="180">
+            <template slot-scope="scope">
+              {{scope.row.sortByKey ? scope.row.name: ''}}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :renderHeader="renderKeysHeader"
+            width="52">
+          </el-table-column>
+        </el-table>
+
+        <div class="split-block dialog-footer">
+          <el-button size="small" 
@click="this.handleClose">{{$t('kylinLang.common.cancel')}}</el-button><el-button
+          type="primary" size="small" @click="this.handleSave">{{isCreate ? 
$t('kylinLang.common.create') : $t('kylinLang.common.save')}}</el-button>
+        </div>
+      </div>
+      <div class="table-attribute-selector" v-show="selectTableAttributeKeys" 
v-loading="loading">
+        <el-table-draggable-wrapper handle=".draggable-td" 
@drop="_afterTableAttributesChanged">
+          <el-table
+            :data="tableAttributes"
+            style="padding: 0 8px">
+            <el-table-column width="45">
+              <template slot-scope="scope">
+                <i class="el-ksd-n-icon-grab-dots-outlined draggable-td" 
:key="scope.$index"></i>
+              </template>
+            </el-table-column>
+            <el-table-column
+              prop="name"
+              :label="$t('attributeSettingColumnTitle')"
+              width="180">
+            </el-table-column>
+            <el-table-column
+              :label="$t('attributeSettingPrimaryTitle')"
+              width="110">
+              <template slot-scope="scope">
+                <el-checkbox v-model="scope.row.primaryKey" @change="v => 
primaryKeyChanged(v, scope)"></el-checkbox>
+              </template>
+            </el-table-column>
+            <el-table-column
+              :label="$t('attributeSettingSortByTitle')"
+              width="110">
+              <template slot-scope="scope">
+                <el-checkbox v-model="scope.row.sortByKey" 
:checked="scope.row.primaryKey" :disabled="scope.row.primaryKey"  
@change="sortByKeyChanged"></el-checkbox>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-table-draggable-wrapper>
+      </div>
+    </el-form>
+  </el-dialog>
+</template>
+<script>
+import Vue from 'vue'
+import { mapActions } from 'vuex'
+import { Component, Watch } from 'vue-property-decorator'
+import * as types from 'src/store/types'
+import { handleSuccessAsync, handleError } from 'src/util'
+import locales from './locales'
+import { timePartitionFormatOptions } from '../const'
+
+@Component({
+  locales,
+  props: {
+    open: {
+      type: Boolean,
+      default: false
+    },
+    isCreate: {
+      type: Boolean,
+      default: false
+    },
+    tableInfo: {
+      type: Object
+    },
+    handleClose: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  computed: {
+    partitionOptions () {
+      return (this.tableInfo.columns ? this.tableInfo.columns.map(c => ({ 
label: c.name, value: c.name })) : [])
+    },
+    bucketOptions () {
+      return (this.tableInfo.columns ? this.tableInfo.columns.filter(c => 
c.name !== this.ruleForm.partitionColumn).map(c => ({ label: c.name, value: 
c.name })) : [])
+    },
+    doesPartionColumnNeedDateFormatter () {
+      const column = this.tableInfo.columns && this.tableInfo.columns.find(c 
=> c.name === this.ruleForm.partitionColumn)
+      return column && column.datatype === 'date'
+    },
+    rules () {
+      return {
+        timePartitionFormat: { required: 
this.doesPartionColumnNeedDateFormatter, message: 
this.$t('validateErrorSelect'), trigger: 'change' },
+        bucketCount: [
+          { required: !!this.bucketColumn, trigger: 'blur' },
+          { type: 'number', message: this.$t('validateErrorNumber'), min: 1, 
max: 5000, trigger: 'blur' }
+        ]
+      }
+    }
+  },
+  methods: {
+    ...mapActions({
+      updateInternalTable: types.UPDATE_INTERNAL_TABLE,
+      createInternalTable: types.CREATE_INTERNAL_TABLE
+    })
+  }
+})
+export default class InternalTableSetting extends Vue {
+  ruleForm = {
+    partitionColumn: '',
+    timePartitionFormat: '',
+    bucketColumn: '',
+    bucketCount: 1,
+    selectedTableAttributeKeys: []
+  }
+
+  selectTableAttributeKeys = false
+  loading = false
+
+  timePartitionFormatOptions = timePartitionFormatOptions
+
+  tableAttributes = this.getTableAttributesFromTableInfo()
+
+  getTableAttributesFromTableInfo () {
+    return (this.tableInfo && this.tableInfo.columns) ? 
this.tableInfo.columns.map(c => ({ name: c.name, primaryKey: false, sortByKey: 
false })) : []
+  }
+
+  @Watch('tableInfo')
+  onTableInfoChanged () {
+    if (this.tableInfo) {
+      this.tableAttributes = this.getTableAttributesFromTableInfo()
+      this.ruleForm.partitionColumn = this.tableInfo.time_partition_col || ''
+      this.ruleForm.timePartitionFormat = this.tableInfo.date_partition_format 
|| ''
+      this.ruleForm.bucketColumn = ''
+      this.ruleForm.bucketCount = 1
+      this.ruleForm.selectedTableAttributeKeys = []
+      this.selectTableAttributeKeys = false
+    }
+  }
+
+  @Watch('ruleForm.partitionColumn')
+  onPartitionColumnChanged (newVal) {
+    if (!this.doesPartionColumnNeedDateFormatter) 
this.ruleForm.timePartitionFormat = ''
+    if (this.ruleForm.bucketColumn === newVal) {
+      this.ruleForm.bucketColumn = ''
+      this.ruleForm.bucketCount = 1
+    }
+    this.tableAttributes = this.tableAttributes.filter(a => a.name !== newVal)
+    this._sortTableAttributes()
+  }
+
+  primaryKeyChanged (checked, scope) {
+    scope.row.sortByKey = checked
+    this._sortTableAttributes()
+  }
+
+  sortByKeyChanged () {
+    this._sortTableAttributes()
+  }
+
+  _sortTableAttributes () {
+    this.tableAttributes.sort((a, b) => {
+      const firstCompare = b.primaryKey - a.primaryKey
+      if (firstCompare === 0) {
+        return b.sortByKey - a.sortByKey
+      }
+      return firstCompare
+    })
+    this._afterTableAttributesChanged()
+  }
+
+  _afterTableAttributesChanged () {
+    this.ruleForm.selectedTableAttributeKeys = this.tableAttributes.filter(a 
=> a.primaryKey || a.sortByKey)
+  }
+
+  renderKeysHeader (h, { column }) {
+    return h('el-button', {
+      props: {
+        icon: this.selectTableAttributeKeys ? 
'el-ksd-n-icon-arrow-left-duo-outlined' : 
'el-ksd-n-icon-arrow-right-duo-outlined'
+      },
+      on: {
+        click: () => {
+          this.selectTableAttributeKeys = !this.selectTableAttributeKeys
+        }
+      }
+    })
+  }
+
+  handleSave () {
+    !this.loading && this.$refs.ruleForm.validate((valid) => {
+      if (valid) {
+        this.$confirm(this.isCreate ? this.$t('confirmCreatePrompt', {}) : 
this.$t('confirmPrompt', {}), this.$t('confirmTitle'), {
+          confirmButtonText: this.$t('kylinLang.common.submit'),
+          cancelButtonText: this.$t('kylinLang.common.cancel'),
+          centerButton: true,
+          type: 'warning'
+        }).then(async () => {
+          this.loading = true
+          this._createOrUpdateSetting({
+            database: this.tableInfo.database,
+            table: this.tableInfo.name,
+            project: this.tableInfo.project,
+
+            partition_cols: this.ruleForm.partitionColumn ? 
[this.ruleForm.partitionColumn] : [],
+            date_partition_format: this.doesPartionColumnNeedDateFormatter ? 
this.ruleForm.timePartitionFormat : null,
+            tbl_properties: {
+              primaryKey: this.ruleForm.selectedTableAttributeKeys.filter(k => 
k.primaryKey).map(k => k.name).join(',') || null,
+              orderByKey: this.ruleForm.selectedTableAttributeKeys.filter(k => 
k.sortByKey).map(k => k.name).join(',') || null,
+              bucketCol: this.ruleForm.bucketColumn || null,
+              bucketNum: this.ruleForm.bucketCount
+            }
+          }).then(handleSuccessAsync).then(
+            () => this.$message({ type: 'success', message: this.isCreate ? 
this.$t('createSuccess') : this.$t('saveSuccess') })
+          ).then(
+            () => this.handleClose()
+          ).finally(() => {
+            this.loading = false
+          }).catch(handleError)
+        })
+      }
+    })
+  }
+
+  _createOrUpdateSetting (params) {
+    if (this.isCreate) {
+      return this.createInternalTable(params)
+    }
+    return this.updateInternalTable(params)
+  }
+}
+</script>
+<style lang="less">
+  @dialog-width: 476px;
+
+  .internal-table-setting {
+    overflow: visible;
+
+    .el-dialog {
+      width: @dialog-width;
+    }
+  }
+  .with-selector {
+    padding-right: @dialog-width;
+  }
+  .el-dialog__body {
+    scrollbar-gutter: stable;
+  }
+  .internal-table-container {
+    margin: 32px 24px 24px;
+  }
+  .split-block {
+    margin-top: 24px;
+  }
+  .sub-title {
+    margin-bottom: 8px;
+  }
+  .table-attribute-selector {
+    position: absolute;
+    bottom: 0;
+    right: -@dialog-width - 12px;
+    width: @dialog-width;
+    box-sizing: border-box;
+    background-color: #fff;
+    padding: 6px;
+    border-radius: 12px;
+    .el-table-draggable-wrapper {
+      max-height: 560px;
+      overflow: overlay;
+    }
+  }
+  .dialog-footer {
+    margin-bottom: 24px;
+    text-align: right;
+  }
+  .query-inernal-table-tips {
+    position: relative;
+    top: -1px;
+    left: 2px;
+  }
+  .el-table__empty-block {
+    height: 0px;
+  }
+  .el-table {
+    min-height: 234px;
+  }
+  .max-width {
+    width: 100%;
+  }
+  .internal-table-tips {
+    :first-child {
+      max-height: 100% !important;
+    }
+  }
+</style>
diff --git a/kystudio/src/components/studio/InternalTable/Setting/locales.js 
b/kystudio/src/components/studio/InternalTable/Setting/locales.js
new file mode 100644
index 0000000000..a3723ec25a
--- /dev/null
+++ b/kystudio/src/components/studio/InternalTable/Setting/locales.js
@@ -0,0 +1,48 @@
+export default {
+  en: {
+    dialogTitle: 'Internal Table Settings',
+    settingDesc: 'After you set the time partition column, you can load data 
in incremental or full mode. Non-partitioned tables only support full load. 
Note that if incremental loading is required, set the partitioned column to be 
the same as the data source table level partition and to be a date-type column',
+    partitionOptionsTitle: 'Please choose the partition column',
+    timePartitionFormatTitle: 'Time column format',
+    bucketColumnTitle: 'Please choose the bucket column',
+    bucketCountTitle: 'Please set the count of bucket',
+    addNativeTableAttributeTitle: 'Set ordering column',
+    attributeSettingColumnTitle: 'Column',
+    attributeSettingPrimaryTitle: 'Primary Key',
+    attributeSettingSortByTitle: 'Sort By Key',
+    attributeSettingTips: 'You can set one or more columns to PrimaryKey and 
SortByKey. Doing so affects the column sorting of the data store file and 
improves the query performance in some scenarios. Note that to set PrimaryKey, 
SortByKey must be set, and PrimaryKey is the prefix subset of SortByKey',
+    partitionDefaultOptionLabel: 'No Partition',
+    bucketDefaultOptionLabel: 'No Bucket Column',
+    confirmTitle: 'Save the settings of internal table',
+    confirmPrompt: 'Do you want to save this modification? After saving, the 
running inner table data loading task will be stopped.',
+    confirmCreatePrompt: 'Do you want to create internal table?',
+    validateErrorSelect: 'Please select one',
+    validateErrorNumber: 'Please input integer from 1 to 5000',
+    saveFailed: 'Saving failed!',
+    saveSuccess: 'Saving success!',
+    createSuccess: 'Creating success, please go to Internal Table module for 
modifying and view details.'
+  },
+  'zh-cn': {
+    dialogTitle: '内表设置',
+    settingDesc: 
'设置时间分区列后可以使用增量或全量方式加载数据,非分区表仅支持全量加载。请注意,如需增量加载,请设置分区列与数据源表一级分区保持一致,且为日期类型列',
+    partitionOptionsTitle: '请选择分区列',
+    timePartitionFormatTitle: '时间分区列格式',
+    bucketColumnTitle: '设置分桶列',
+    bucketCountTitle: '设置分桶数',
+    addNativeTableAttributeTitle: '设置排序列',
+    attributeSettingColumnTitle: 'Column',
+    attributeSettingPrimaryTitle: 'Primary Key',
+    attributeSettingSortByTitle: 'Sort By Key',
+    attributeSettingTips: 
'可以将一个或多个列设置为PrimaryKey和SortByKey,这样做会影响数据存储文件的列排序,从而提升某些场景的查询性能。请注意,设置PrimaryKey
 则必须设置SortByKey,且PrimaryKey是SortByKey的前缀子集',
+    partitionDefaultOptionLabel: '无分区',
+    bucketDefaultOptionLabel: '无分桶列',
+    confirmTitle: '保存内表设置',
+    confirmPrompt: '确认要保存此次修改么?保存后正在跑的内表数据加载任务会被中止。',
+    confirmCreatePrompt: '确认要创建内表么?',
+    validateErrorSelect: '请选择',
+    validateErrorNumber: '请输入1到5000整数',
+    saveFailed: '保存失败!',
+    saveSuccess: '保存成功。',
+    createSuccess: '内表创建成功,请前往内表模块修改信息和查看详情。'
+  }
+}
diff --git a/kystudio/src/components/studio/InternalTable/const.js 
b/kystudio/src/components/studio/InternalTable/const.js
new file mode 100644
index 0000000000..2c0c9042d9
--- /dev/null
+++ b/kystudio/src/components/studio/InternalTable/const.js
@@ -0,0 +1,10 @@
+export const timePartitionFormatOptions = [
+  { label: 'yyyy-MM-dd', value: 'yyyy-MM-dd' },
+  { label: 'yyyyMMdd', value: 'yyyyMMdd' },
+  { label: 'yyyy-MM-dd HH:mm:ss', value: 'yyyy-MM-dd HH:mm:ss' },
+  { label: 'yyyy-MM-dd HH:mm:ss.SSS', value: 'yyyy-MM-dd HH:mm:ss.SSS' },
+  { label: 'yyyy/MM/dd', value: 'yyyy/MM/dd' },
+  { label: 'yyyy-MM', value: 'yyyy-MM' },
+  { label: 'yyyyMM', value: 'yyyyMM' },
+  { label: 'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'', value: 
'yyyy-MM-dd\'T\'HH:mm:ss.SSS\'Z\'' }
+]
diff --git a/kystudio/src/components/studio/StudioSource/index.vue 
b/kystudio/src/components/studio/StudioSource/index.vue
index 6e9751b687..1938cc66b2 100644
--- a/kystudio/src/components/studio/StudioSource/index.vue
+++ b/kystudio/src/components/studio/StudioSource/index.vue
@@ -33,6 +33,9 @@
             <h1 class="table-name" 
:title="selectedTable.fullName">{{selectedTable.fullName}}</h1>
             <h2 class="table-update-at">{{$t('updateAt')}} 
{{selectedTable.updateAt | toGMTDate}}</h2>
             <div class="table-actions ky-no-br-space">
+              <el-button type="primary" text 
icon="el-ksd-n-icon-data-base-outlined" @click="showInternalTableSetting" 
v-if="currentSelectedProjectInternalTableEnabled && 
datasourceActions.includes('sampleSourceTable') && [9, 
8].includes(selectedTable.datasource)">
+                {{selectedTable.has_Internal ? $t('viewNativeTable') : 
$t('createNativeTable')}}
+              </el-button>
               <el-button type="primary" text icon="el-ksd-icon-sample_22" 
@click="sampleTable" v-if="datasourceActions.includes('sampleSourceTable') && 
[9, 8].includes(selectedTable.datasource)">{{$t('sample')}}</el-button>
               <el-button type="primary" class="ksd-ml-2" text 
icon="el-ksd-icon-resure_22" :loading="reloadBtnLoading" @click="handleReload" 
v-if="datasourceActions.includes('reloadSourceTable') && [9, 
8].includes(selectedTable.datasource)">{{$t('reload')}}</el-button>
               <el-button type="primary" class="ksd-ml-2" text 
icon="el-ksd-icon-table_delete_22" :loading="delBtnLoading" 
v-if="datasourceActions.includes('delSourceTable')" 
@click="handleDelete">{{$t('delete')}}</el-button>
@@ -59,6 +62,9 @@
           <SourceManagement v-if="isShowSourcePage" 
:project="currentProjectData" 
@fresh-tables="handleFreshTable"></SourceManagement>
         </transition>
       </div>
+
+      <InternalTableSetting v-if="selectedTable" 
:open="this.internalTableSettingVisible" :isCreate="true" 
:tableInfo="selectedTable" :handleClose="handleInternalSettingClose"/>
+
       <el-dialog
         class="sample-dialog"
         @close="resetSampling"
@@ -114,6 +120,7 @@ import TableSamples from './TableSamples/TableSamples.vue'
 import KafkaCluster from './KafkaCluster/KafkaCluster.vue'
 import SourceManagement from './SourceManagement/SourceManagement.vue'
 import ReloadTable from './TableReload/reload.vue'
+import InternalTableSetting from 
'../InternalTable/Setting/InternalTableSetting.vue'
 import { handleSuccessAsync, handleError } from '../../../util'
 import { kylinConfirm } from '../../../util/business'
 import { getFormattedTable } from '../../../util/UtilTable'
@@ -126,13 +133,15 @@ import { getFormattedTable } from 
'../../../util/UtilTable'
     TableSamples,
     KafkaCluster,
     SourceManagement,
-    ReloadTable
+    ReloadTable,
+    InternalTableSetting
   },
   computed: {
     ...mapGetters([
       'currentProjectData',
       'currentSelectedProject',
-      'datasourceActions'
+      'datasourceActions',
+      'currentSelectedProjectInternalTableEnabled'
     ])
   },
   methods: {
@@ -161,6 +170,7 @@ export default class StudioSource extends Vue {
   isShowSourcePage = false
   reloadBtnLoading = false
   sampleVisible = false
+  internalTableSettingVisible = false
   sampleLoading = false
   delBtnLoading = false
   samplingRows = 20000000
@@ -233,6 +243,24 @@ export default class StudioSource extends Vue {
   sampleTable () {
     this.sampleVisible = true
   }
+
+  showInternalTableSetting () {
+    if (this.selectedTable.has_Internal) {
+      this.$router.push('/studio/internal_table')
+      this.internalTableSettingVisible = false
+    } else {
+      this.internalTableSettingVisible = true
+    }
+  }
+
+  async handleInternalSettingClose () {
+    const databaseName = this.selectedTable.database
+    const datasource = this.selectedTable.datasource
+    const tableName = this.selectedTable.name
+    await this.fetchTableDetail({ tableName, databaseName, sourceType: 
datasource })
+    this.internalTableSettingVisible = false
+  }
+
   cancelSample () {
     this.sampleVisible = false
   }
diff --git a/kystudio/src/components/studio/StudioSource/locales.js 
b/kystudio/src/components/studio/StudioSource/locales.js
index 5f4efe061f..03a99e89ef 100644
--- a/kystudio/src/components/studio/StudioSource/locales.js
+++ b/kystudio/src/components/studio/StudioSource/locales.js
@@ -32,6 +32,8 @@ export default {
     samplingTableJobBeginTips: 'Sampling job for table [{tableName}] has been 
submitted successfully, you can view the job progress in the Monitor page.',
     deleteAll: 'Delete All',
     deleteTable: 'Delete Table',
-    kafkaCluster: 'Kafka Info'
+    kafkaCluster: 'Kafka Info',
+    createNativeTable: 'Create Internal Table',
+    viewNativeTable: 'View Internal Table'
   }
 }
diff --git a/kystudio/src/config/index.js b/kystudio/src/config/index.js
index 038d28aa61..a6ed2796ee 100644
--- a/kystudio/src/config/index.js
+++ b/kystudio/src/config/index.js
@@ -59,6 +59,7 @@ export const menusData = [
     icon: 'el-ksd-icon-nav_model_24',
     children: [
       { name: 'source', path: '/studio/source' },
+      { name: 'internalTable', path: '/studio/internal_table' },
       { name: 'modelList', path: '/studio/model' },
       { name: 'snapshot', path: '/studio/snapshot' },
       { name: 'ddl', path: '/studio/ddl' },
diff --git a/kystudio/src/config/spec.js b/kystudio/src/config/spec.js
index acb782e64d..97100c5221 100644
--- a/kystudio/src/config/spec.js
+++ b/kystudio/src/config/spec.js
@@ -13,6 +13,7 @@ export default {
       { "id": "setting", "value": "setting", "title": "setting" },
       { "id": "source", "value": "source", "title": "Source" },
       { "id": "model", "value": "modellist", "title": "modelList" },
+      { "id": "internalTable", "value": "internaltable", "title": 
"internalTable" },
       { "id": "index", "value": "index", "title": "Index" },
       { "id": "modelEdit", "value": "modeledit", "title": "Model Edit" },
       { "id": "modelDetails", "value": "modeldetails", "title": "Model 
Details" },
@@ -156,12 +157,12 @@ export default {
       "keyPattern": "groupRole-projectRole-menu",
       "entries": [
         { "key": 
"systemAdmin-*-[project,user,group,groupDetail,projectAuthority,diagnostic]", 
"value": "admin,project,user,group,groupDetail,diagnostic,projectAuthority" },
-        { "key": 
"systemAdmin-*-[dashboard,query,insight,queryHistory,studio,setting,source,model,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob]",
 "value": 
"dashboard,query,insight,queryHistory,studio,setting,source,model,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob,admin"
 },
+        { "key": 
"systemAdmin-*-[dashboard,query,insight,queryHistory,studio,setting,source,model,internalTable,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob]",
 "value": 
"dashboard,query,insight,queryHistory,studio,setting,source,model,internalTable,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob,admin"
 },
         { "key": 
"systemUser-admin-[project,user,group,groupDetail,projectAuthority]", "value": 
"project,admin,projectAuthority" },
-        { "key": 
"systemUser-admin-[dashboard,query,insight,queryHistory,studio,setting,source,model,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob]",
 "value": 
"dashboard,query,insight,queryHistory,studio,setting,source,model,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob,admin"
 },
-        { "key": "systemUser-management-*", "value": 
"dashboard,query,insight,queryHistory,studio,source,model,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob"
 },
-        { "key": "systemUser-operation-*", "value": 
"dashboard,query,insight,queryHistory,studio,model,modelDetails,snapshot,ddl,index,monitor,job,streamingJob,modelSubPartitionValues"
 },
-        { "key": "systemUser-read-*", "value": 
"dashboard,query,insight,queryHistory,studio,model,modelDetails,snapshot,ddl,index"
 },
+        { "key": 
"systemUser-admin-[dashboard,query,insight,queryHistory,studio,setting,source,model,internalTable,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob]",
 "value": 
"dashboard,query,insight,queryHistory,studio,setting,source,model,internalTable,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob,admin"
 },
+        { "key": "systemUser-management-*", "value": 
"dashboard,query,insight,queryHistory,studio,source,model,internalTable,index,modelEdit,modelDetails,modelSubPartitionValues,snapshot,ddl,logicalView,monitor,job,streamingJob"
 },
+        { "key": "systemUser-operation-*", "value": 
"dashboard,query,insight,queryHistory,studio,model,internalTable,modelDetails,snapshot,ddl,index,monitor,job,streamingJob,modelSubPartitionValues"
 },
+        { "key": "systemUser-read-*", "value": 
"dashboard,query,insight,queryHistory,studio,model,internalTable,modelDetails,snapshot,ddl,index"
 },
         { "key": "systemUser-default-*", "value": "dashboard" }
       ]
     },
diff --git a/kystudio/src/locale/en.js b/kystudio/src/locale/en.js
index fd1b39f934..9958cbf6b4 100644
--- a/kystudio/src/locale/en.js
+++ b/kystudio/src/locale/en.js
@@ -496,6 +496,7 @@ exports.default = {
     datasource: 'Data Source',
     source: 'Data Source',
     modelList: 'Model',
+    internalTable: 'Internal Table',
     index: 'Index',
     modeledit: 'Model Edit',
     snapshot: 'Snapshot',
diff --git a/kystudio/src/router/index.js b/kystudio/src/router/index.js
index fa7b220e9a..a37fd66537 100644
--- a/kystudio/src/router/index.js
+++ b/kystudio/src/router/index.js
@@ -62,8 +62,11 @@ let routerOptions = {
         name: 'Source',
         path: 'studio/source',
         component: () => import('../components/studio/StudioSource/index.vue')
-      },
-      {
+      }, {
+        name: 'InternalTable',
+        path: 'studio/internal_table',
+        component: () => 
import('../components/studio/InternalTable/List/index.vue')
+      }, {
         name: 'ModelList',
         path: 'studio/model',
         component: () => 
import('../components/studio/StudioModel/ModelList/index.vue')
diff --git a/kystudio/src/service/api.js b/kystudio/src/service/api.js
index ffaa073188..4a0ceac2e4 100644
--- a/kystudio/src/service/api.js
+++ b/kystudio/src/service/api.js
@@ -2,6 +2,7 @@ import Vue from 'vue'
 import VueResource from 'vue-resource'
 import projectApi from './project'
 import modelApi from './model'
+import internalTableApi from './internalTable'
 import configApi from './config'
 import kafkaApi from './kafka'
 import userApi from './user'
@@ -20,5 +21,6 @@ export default {
   system: systemApi,
   datasource: datasourceApi,
   monitor: monitorApi,
-  dashboard: dashboardApi
+  dashboard: dashboardApi,
+  internalTable: internalTableApi
 }
diff --git a/kystudio/src/service/internalTable.js 
b/kystudio/src/service/internalTable.js
new file mode 100644
index 0000000000..87dc042789
--- /dev/null
+++ b/kystudio/src/service/internalTable.js
@@ -0,0 +1,43 @@
+import Vue from 'vue'
+import VueResource from 'vue-resource'
+import { apiUrl } from '../config'
+
+Vue.use(VueResource)
+
+export default {
+  getInternalTableList: (params) => {
+    return Vue.resource(apiUrl + 'internal_tables').get(params)
+  },
+
+  updateInternalTable: (params) => {
+    return Vue.resource(apiUrl + 
`internal_tables/${params.database}/${params.table}?project=${params.project}`).update(params)
+  },
+
+  createInternalTable: (params) => {
+    return Vue.resource(apiUrl + 
`internal_tables/${params.database}/${params.table}?project=${params.project}`).save(params)
+  },
+
+  getInternalTableDetails: (params) => {
+    return Vue.resource(apiUrl + 
'internal_tables{/database}{/table}').get(params)
+  },
+
+  deleteInternalTable: (params) => {
+    return Vue.resource(apiUrl + 
`internal_tables/${params.database}/${params.table}?project=${params.project}`).delete()
+  },
+
+  loadSingleInternalTable: (params) => {
+    return Vue.resource(apiUrl + 
`internal_tables/${params.project}/${params.database}/${params.table}`).save(params)
+  },
+
+  batchLoadInternalTables: (params) => {
+    return Vue.resource(apiUrl + 'internal_tables/data/batch').save(params)
+  },
+
+  deletePartitions: (params) => {
+    return Vue.resource(apiUrl + 'internal_tables/partitions').delete(params)
+  },
+
+  deleteAllData: (params) => {
+    return Vue.resource(apiUrl + 
'internal_tables/truncate_internal_table').delete(params)
+  }
+}
diff --git a/kystudio/src/service/project.js b/kystudio/src/service/project.js
index 447c166d33..cc51777fbc 100644
--- a/kystudio/src/service/project.js
+++ b/kystudio/src/service/project.js
@@ -68,6 +68,9 @@ export default {
   fetchProjectSettings: (project) => {
     return Vue.resource(apiUrl + 'projects/' + project + 
'/project_config').get()
   },
+  updateInternalTableEnabled (params) {
+    return Vue.resource(apiUrl + 'projects/' + params.project + 
'/internal_table_enabled').update(params)
+  },
   updateProjectGeneralInfo (body) {
     return Vue.resource(apiUrl + 'projects/' + body.project + 
'/project_general_info').update(body)
   },
diff --git a/kystudio/src/store/index.js b/kystudio/src/store/index.js
index dc51ad0d43..d94f002e49 100644
--- a/kystudio/src/store/index.js
+++ b/kystudio/src/store/index.js
@@ -11,12 +11,14 @@ import datasource from './datasource'
 import system from './system'
 import monitor from './monitor'
 import capacity from './capacity'
+import internalTable from './internalTable'
 import * as actionTypes from './types'
 import dashboard from './dashboard'
 
 export default new Vuex.Store({
   modules: {
     model: model,
+    internalTable: internalTable,
     project: project,
     config: config,
     kafka: kafka,
diff --git a/kystudio/src/store/internalTable.js 
b/kystudio/src/store/internalTable.js
new file mode 100644
index 0000000000..434e0bdc4d
--- /dev/null
+++ b/kystudio/src/store/internalTable.js
@@ -0,0 +1,57 @@
+import api from './../service/api'
+import * as types from './types'
+export default {
+  state: {
+    internalTableList: [],
+    internalTableListTotal: 0,
+    internalTableListPageOffset: 0,
+    internalTableListPageLimit: 20
+  },
+  mutations: {
+    [types.MUTATE_REPLACE_INTERNAL_TABLE_LIST]: function (state, result) {
+      state.internalTableList = result.value
+      state.internalTableListTotal = result.total_size
+      state.internalTableListPageOffset = result.offset
+      state.internalTableListPageLimit = result.limit
+    }
+  },
+  actions: {
+    [types.GET_INTERNAL_TABLES]: function ({ commit }, params) {
+      return new Promise((resolve, reject) => {
+        api.internalTable.getInternalTableList(params).then((response) => {
+          commit(types.MUTATE_REPLACE_INTERNAL_TABLE_LIST, response.data.data)
+          resolve(response)
+        }, (res) => {
+          commit(types.MUTATE_REPLACE_INTERNAL_TABLE_LIST, { value: [], 
total_size: 0, offset: 0, limit: 20 })
+          reject(res)
+        })
+      })
+    },
+    [types.UPDATE_INTERNAL_TABLE]: function ({ commit }, params) {
+      return api.internalTable.updateInternalTable(params)
+    },
+    [types.CREATE_INTERNAL_TABLE]: function ({ commit }, params) {
+      return api.internalTable.createInternalTable(params)
+    },
+    [types.GET_INTERNAL_TABLE_DETAILS]: function ({ commit }, params) {
+      return api.internalTable.getInternalTableDetails(params)
+    },
+    [types.DELETE_INTERNAL_TABLE]: function ({ commit }, params) {
+      return api.internalTable.deleteInternalTable(params)
+    },
+    [types.LOAD_SINGLE_INTERNAL_TABLE]: function ({ commit }, params) {
+      return api.internalTable.loadSingleInternalTable(params)
+    },
+    [types.BATCH_LOAD_INTERNAL_TABLES]: function ({ commit }, params) {
+      return api.internalTable.batchLoadInternalTables(params)
+    },
+    [types.DELETE_INTERNAL_TABLE_PARTITIONS]: function ({ commit }, params) {
+      return api.internalTable.deletePartitions(params)
+    },
+    [types.DELETE_ALL_DATA_INTERNAL_TABLE]: function ({ commit }, params) {
+      return api.internalTable.deleteAllData(params)
+    }
+  },
+  getters: {
+  }
+}
diff --git a/kystudio/src/store/project.js b/kystudio/src/store/project.js
index 917c702859..aa20038607 100644
--- a/kystudio/src/store/project.js
+++ b/kystudio/src/store/project.js
@@ -271,6 +271,9 @@ export default {
     [types.RESET_PROJECT_CONFIG]: function ({ commit }, para) {
       return api.project.resetConfig(para)
     },
+    [types.UPDATE_INTERNAL_TABLE_ENABLED]: function ({ commit }, para) {
+      return api.project.updateInternalTableEnabled(para)
+    },
     [types.UPDATE_DEFAULT_DB_SETTINGS]: function ({ commit }, para) {
       return api.project.updateDefaultDBSettings(para)
     },
@@ -351,6 +354,9 @@ export default {
     currentSelectedProject: (state) => {
       return state.selected_project
     },
+    currentSelectedProjectInternalTableEnabled: (state) => {
+      return (state.projectConfig && state.projectConfig.projectDefaultDB && 
state.projectConfig.projectDefaultDB.override_kylin_properties['kylin.internal-table-enabled']
 === 'true') || false
+    },
     currentProjectData: (state, getters, rootState) => {
       const _filterable = state.allProject.filter(p => {
         return p.name === state.selected_project
diff --git a/kystudio/src/store/types.js b/kystudio/src/store/types.js
index 5d7e7fc15b..bd7931e8d3 100644
--- a/kystudio/src/store/types.js
+++ b/kystudio/src/store/types.js
@@ -42,6 +42,7 @@ export const UPDATE_STORAGE_QUOTA = 'UPDATE_STORAGE_QUOTA'
 export const UPDATE_JOB_ALERT_SETTINGS = 'UPDATE_JOB_ALERT_SETTINGS'
 export const UPDATE_PROJECT_DATASOURCE = 'UPDATE_PROJECT_DATASOURCE'
 export const RESET_PROJECT_CONFIG = 'RESET_PROJECT_CONFIG'
+export const UPDATE_INTERNAL_TABLE_ENABLED = 'UPDATE_INTERNAL_TABLE_ENABLED'
 export const UPDATE_DEFAULT_DB_SETTINGS = 'UPDATE_DEFAULT_DB_SETTINGS'
 export const CACHE_PROJECT_DEFAULT_DB = 'CACHE_PROJECT_DEFAULT_DB'
 export const UPDATE_PROJECT_SEMI_AUTOMATIC_STATUS = 
'UPDATE_PROJECT_SEMI_AUTOMATIC_STATUS'
@@ -467,3 +468,14 @@ export const GET_QUERY_STATISTICS = 'GET_QUERY_STATISTICS'
 export const GET_CHART_DATA = 'GET_CHART_DATA'
 export const GET_JOB_STATISTICS = 'GET_JOB_STATISTICS'
 
+// internal table actions and mutations
+export const MUTATE_REPLACE_INTERNAL_TABLE_LIST = 
'MUTATE_REPLACE_INTERNAL_TABLE_LIST'
+export const GET_INTERNAL_TABLES = 'GET_INTERNAL_TABLES'
+export const UPDATE_INTERNAL_TABLE = 'UPDATE_INTERNAL_TABLE'
+export const CREATE_INTERNAL_TABLE = 'CREATE_INTERNAL_TABLE'
+export const GET_INTERNAL_TABLE_DETAILS = 'GET_INTERNAL_TABLE_DETAILS'
+export const DELETE_INTERNAL_TABLE = 'DELETE_INTERNAL_TABLE'
+export const LOAD_SINGLE_INTERNAL_TABLE = 'LOAD_SINGLE_INTERNAL_TABLE'
+export const BATCH_LOAD_INTERNAL_TABLES = 'BATCH_LOAD_INTERNAL_TABLES'
+export const DELETE_INTERNAL_TABLE_PARTITIONS = 
'DELETE_INTERNAL_TABLE_PARTITIONS'
+export const DELETE_ALL_DATA_INTERNAL_TABLE = 'DELETE_ALL_DATA_INTERNAL_TABLE'
diff --git a/kystudio/src/util/UtilTable.js b/kystudio/src/util/UtilTable.js
index e872c38555..5299abdbbc 100644
--- a/kystudio/src/util/UtilTable.js
+++ b/kystudio/src/util/UtilTable.js
@@ -12,6 +12,7 @@ export function getFormattedTable (originData = {}) {
     fullName: `${originData.database}.${originData.name}`,
     updateAt: originData.last_modified,
     datasource: originData.source_type,
+    has_Internal: originData.has_Internal,
     cardinality: originData.cardinality,
     last_build_job_id: originData.last_build_job_id,
     partitionColumn,
@@ -23,6 +24,7 @@ export function getFormattedTable (originData = {}) {
     dateTypeColumns,
     startTime,
     endTime,
+    project: originData.project,
     create_time: originData.create_time,
     sampling_rows: originData.sampling_rows,
     kafka_bootstrap_servers: originData.kafka_bootstrap_servers,

Reply via email to