This is an automated email from the ASF dual-hosted git repository.
nicholasjiang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-paimon-webui.git
The following commit(s) were added to refs/heads/main by this push:
new fb38935 [Feature] Introduce playground module (#46)
fb38935 is described below
commit fb389351c9b41267b530a0ea607c362d39c172f1
Author: labbomb <[email protected]>
AuthorDate: Fri Oct 6 12:09:17 2023 +0800
[Feature] Introduce playground module (#46)
---
paimon-web-ui-new/package.json | 2 +
paimon-web-ui-new/pnpm-lock.yaml | 60 ++++-
paimon-web-ui-new/src/App.tsx | 4 +-
.../src/components/monaco-editor/index.tsx | 38 +++-
.../src/components/monaco-editor/type.ts | 4 +-
.../src/locales/en/modules/playground.ts | 20 +-
.../src/locales/zh/modules/playground.ts | 20 +-
paimon-web-ui-new/src/main.ts | 2 +
paimon-web-ui-new/src/router/modules/playground.ts | 17 +-
.../views/playground/components/catalog/index.tsx | 88 --------
.../components/console}/index.module.scss | 11 +-
.../components/query/components/console/index.tsx | 109 +++++++++
.../components/debugger}/index.module.scss | 14 +-
.../components/query/components/debugger/index.tsx | 139 ++++++++++++
.../components/menu-tree}/index.module.scss | 13 +-
.../query/components/menu-tree/index.tsx | 247 +++++++++++++++++++++
.../components/tabs}/index.module.scss | 19 +-
.../components/query/components/tabs/index.tsx | 109 +++++++++
.../{ => components/query}/index.module.scss | 47 ++--
.../views/playground/components/query/index.tsx | 138 ++++++++++++
.../{ => components/slider}/index.module.scss | 46 ++--
.../views/playground/components/slider/index.tsx | 143 ++++++++++++
.../components/console}/index.module.scss | 11 +-
.../workbench/components/console/index.tsx | 109 +++++++++
.../components/debugger}/index.module.scss | 14 +-
.../workbench/components/debugger/index.tsx | 139 ++++++++++++
.../components/menu-tree}/index.module.scss | 13 +-
.../workbench/components/menu-tree/index.tsx | 159 +++++++++++++
.../components/tabs}/index.module.scss | 19 +-
.../components/workbench/components/tabs/index.tsx | 109 +++++++++
.../{ => components/workbench}/index.module.scss | 47 ++--
.../playground/components/workbench/index.tsx | 138 ++++++++++++
.../src/views/playground/index.module.scss | 24 +-
paimon-web-ui-new/src/views/playground/index.tsx | 79 +------
34 files changed, 1882 insertions(+), 269 deletions(-)
diff --git a/paimon-web-ui-new/package.json b/paimon-web-ui-new/package.json
index e3443bb..6fb7ba9 100644
--- a/paimon-web-ui-new/package.json
+++ b/paimon-web-ui-new/package.json
@@ -13,11 +13,13 @@
},
"dependencies": {
"dart-sass": "^1.25.0",
+ "mitt": "^3.0.1",
"monaco-editor": "^0.43.0",
"pinia": "^2.1.6",
"pinia-plugin-persistedstate": "^3.2.0",
"sass": "^1.66.1",
"sass-loader": "^13.3.2",
+ "sql-formatter": "^13.0.0",
"vue": "^3.3.4",
"vue-i18n": "^9.4.0",
"vue-router": "^4.2.4"
diff --git a/paimon-web-ui-new/pnpm-lock.yaml b/paimon-web-ui-new/pnpm-lock.yaml
index 17b654a..eb3e50d 100644
--- a/paimon-web-ui-new/pnpm-lock.yaml
+++ b/paimon-web-ui-new/pnpm-lock.yaml
@@ -23,6 +23,9 @@ dependencies:
dart-sass:
specifier: ^1.25.0
version: 1.25.0
+ mitt:
+ specifier: ^3.0.1
+ version: 3.0.1
monaco-editor:
specifier: ^0.43.0
version: 0.43.0
@@ -38,6 +41,9 @@ dependencies:
sass-loader:
specifier: ^13.3.2
version: 13.3.2([email protected])([email protected])
+ sql-formatter:
+ specifier: ^13.0.0
+ version: 13.0.0
vue:
specifier: ^3.3.4
version: 3.3.4
@@ -1393,7 +1399,6 @@ packages:
/[email protected]:
resolution: {integrity:
sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
- dev: true
/[email protected]:
resolution: {integrity:
sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
@@ -1756,6 +1761,10 @@ packages:
path-type: 4.0.0
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
engines: {node: '>=6.0.0'}
@@ -2224,6 +2233,11 @@ packages:
has-symbols: 1.0.3
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==}
+ engines: {node: '>=10'}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
engines: {node: '>=10'}
@@ -2770,6 +2784,10 @@ packages:
brace-expansion: 2.0.1
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==}
dependencies:
@@ -2783,6 +2801,10 @@ packages:
resolution: {integrity:
sha512-cnoqwQi/9fml2Szamv1XbSJieGJ1Dc8tENVMD26Kcfl7xGQWp7OBKMjlwKVGYFJ3/AXJjSOGvcqK7Ry/j9BM1Q==}
dev: false
+ /[email protected]:
+ resolution: {integrity:
sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: true
@@ -2830,6 +2852,16 @@ packages:
resolution: {integrity:
sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==}
+ hasBin: true
+ dependencies:
+ commander: 2.20.3
+ moo: 0.5.2
+ railroad-diagrams: 1.0.0
+ randexp: 0.4.6
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
dev: false
@@ -3139,6 +3171,18 @@ packages:
resolution: {integrity:
sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==}
+ dev: false
+
+ /[email protected]:
+ resolution: {integrity:
sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==}
+ engines: {node: '>=0.12'}
+ dependencies:
+ discontinuous-range: 1.0.0
+ ret: 0.1.15
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
dependencies:
@@ -3187,6 +3231,11 @@ packages:
supports-preserve-symlinks-flag: 1.0.0
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==}
+ engines: {node: '>=0.12'}
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
@@ -3407,6 +3456,15 @@ packages:
resolution: {integrity:
sha512-XkD+zwiqXHikFZm4AX/7JSCXA98U5Db4AFd5XUg/+9UNtnH75+Z9KxtpYiJZx36mUDVOwH83pl7yvCer6ewM3w==}
dev: true
+ /[email protected]:
+ resolution: {integrity:
sha512-V21cVvge4rhn9Fa7K/fTKcmPM+x1yee6Vhq8ZwgaWh3VPBqApgsaoFB5kLAhiqRo5AmSaRyLU7LIdgnNwH01/w==}
+ hasBin: true
+ dependencies:
+ argparse: 2.0.1
+ get-stdin: 8.0.0
+ nearley: 2.20.1
+ dev: false
+
/[email protected]:
resolution: {integrity:
sha512-DOB27b/2UTTD+4myKUFh+/fXWcu/UDyASIXfg+7VzoCNNGOfWvoyU/x5pvVHr++ztyt/oSYI1BcWBBG/hmlNjA==}
engines: {node: '>= 0.4'}
diff --git a/paimon-web-ui-new/src/App.tsx b/paimon-web-ui-new/src/App.tsx
index 96768c7..90b23e0 100644
--- a/paimon-web-ui-new/src/App.tsx
+++ b/paimon-web-ui-new/src/App.tsx
@@ -47,7 +47,9 @@ export default defineComponent({
date-locale={this.locale === 'en' ? dateEnUS : dateZhCN}
style={{ width: '100%', height: '100vh' }}
>
- <router-view />
+ <n-message-provider>
+ <router-view />
+ </n-message-provider>
</n-config-provider>
}
})
diff --git a/paimon-web-ui-new/src/components/monaco-editor/index.tsx
b/paimon-web-ui-new/src/components/monaco-editor/index.tsx
index 5bf54af..55a917d 100644
--- a/paimon-web-ui-new/src/components/monaco-editor/index.tsx
+++ b/paimon-web-ui-new/src/components/monaco-editor/index.tsx
@@ -23,6 +23,7 @@ import tsWorker from
'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
import { editorProps } from './type'
import { useConfigStore } from '@/store/config'
+import { format } from 'sql-formatter'
// @ts-ignore: worker
self.MonacoEnvironment = {
@@ -46,7 +47,7 @@ self.MonacoEnvironment = {
export default defineComponent({
name: 'MonacoEditor',
props: editorProps,
- emits: ['update:modelValue', 'change', 'EditorMounted'],
+ emits: ['update:modelValue', 'change', 'EditorMounted', 'EditorSave'],
setup(props, { emit }) {
const configStore = useConfigStore()
const monacoEditorThemeRef = ref(configStore.getCurrentTheme === 'dark' ?
'vs-dark' : 'vs')
@@ -61,12 +62,45 @@ export default defineComponent({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true
})
+ monaco.languages.registerCompletionItemProvider('sql', {
+ provideCompletionItems: function(model, position) {
+ const word = model.getWordUntilPosition(position);
+ const range = {
+ startLineNumber: position.lineNumber,
+ endLineNumber: position.lineNumber,
+ startColumn: word.startColumn,
+ endColumn: word.endColumn
+ };
+ const suggestions = [];
+ const sqlStr = ['select','from','where','and','or','limit','order
by','group by'];
+ for(const i in sqlStr){
+ suggestions.push({
+ label: sqlStr[i],
+ kind: monaco.languages.CompletionItemKind['Function'],
+ insertText: sqlStr[i],
+ detail: '',
+ range:range
+ });
+ }
+ return {
+ suggestions: suggestions
+ };
+ },
+ });
+
editor = monaco.editor.create(codeEditBox.value, {
value: props.modelValue,
language: props.language,
theme: monacoEditorThemeRef.value,
...props.options
})
+
+ editor.addCommand(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyS, function
() {
+ emit('EditorSave')
+ })
+
+ editor.setValue(format(toRaw(editor).getValue()))
+
editor.onDidChangeModelContent(() => {
const value = editor.getValue()
emit('update:modelValue', value)
@@ -81,6 +115,7 @@ export default defineComponent({
const value = editor.getValue()
if (newValue !== value) {
editor.setValue(newValue)
+ editor.setValue(format(toRaw(editor).getValue()))
}
}
}
@@ -106,6 +141,7 @@ export default defineComponent({
init()
}
)
+
onBeforeUnmount(() => {
editor.dispose()
})
diff --git a/paimon-web-ui-new/src/components/monaco-editor/type.ts
b/paimon-web-ui-new/src/components/monaco-editor/type.ts
index a611096..abfd5c1 100644
--- a/paimon-web-ui-new/src/components/monaco-editor/type.ts
+++ b/paimon-web-ui-new/src/components/monaco-editor/type.ts
@@ -66,12 +66,12 @@ export const editorProps = {
renderLineHighlight: 'line',
selectOnLineNumbers: true,
minimap: {
- enabled: true
+ enabled: false
},
readOnly: false,
contextmenu: true,
fontSize: 16,
- scrollBeyondLastLine: false,
+ scrollBeyondLastLine: true,
overviewRulerBorder: false
}
}
diff --git a/paimon-web-ui-new/src/locales/en/modules/playground.ts
b/paimon-web-ui-new/src/locales/en/modules/playground.ts
index feedda3..9babf17 100644
--- a/paimon-web-ui-new/src/locales/en/modules/playground.ts
+++ b/paimon-web-ui-new/src/locales/en/modules/playground.ts
@@ -16,6 +16,22 @@ specific language governing permissions and limitations
under the License. */
export default {
- select_catalog: 'Select Catalog',
- search: 'Search'
+ query: 'Query',
+ workbench: 'Workbench',
+ task_operation_and_maintenance: 'Task Operation and Maintenance',
+ settings: 'Settings',
+ terminal: 'Terminal',
+ git_branch: 'Git Branch',
+ data: 'Data',
+ saved_query: 'Saved Query',
+ query_record: 'Query Record',
+ search: 'Search',
+ run: 'Run',
+ format: 'Format',
+ save: 'Save',
+ clear: 'Clear',
+ unfold: 'Unfold',
+ collapse: 'Collapse',
+ logs: 'Logs',
+ result: 'Result',
}
diff --git a/paimon-web-ui-new/src/locales/zh/modules/playground.ts
b/paimon-web-ui-new/src/locales/zh/modules/playground.ts
index e9350cb..2787b5b 100644
--- a/paimon-web-ui-new/src/locales/zh/modules/playground.ts
+++ b/paimon-web-ui-new/src/locales/zh/modules/playground.ts
@@ -16,6 +16,22 @@ specific language governing permissions and limitations
under the License. */
export default {
- select_catalog: '选择 Catalog',
- search: '搜索'
+ query: '查询',
+ workbench: '工作台',
+ task_operation_and_maintenance: '任务运维',
+ settings: '设置',
+ terminal: '终端',
+ git_branch: 'Git 分支',
+ data: '数据',
+ saved_query: '已保存查询',
+ query_record: '查询记录',
+ search: '搜索',
+ run: '运行',
+ format: '格式化',
+ save: '保存',
+ clear: '清空',
+ unfold: '展开',
+ collapse: '折叠',
+ logs: '日志',
+ result: '结果',
}
diff --git a/paimon-web-ui-new/src/main.ts b/paimon-web-ui-new/src/main.ts
index 82a46d9..303ab83 100644
--- a/paimon-web-ui-new/src/main.ts
+++ b/paimon-web-ui-new/src/main.ts
@@ -24,6 +24,7 @@ import naive from 'naive-ui'
import i18n from './locales'
import { Setting } from './config'
import '@/assets/styles/main.scss'
+import mitt from 'mitt'
const app = createApp(App)
const pinia = createPinia()
@@ -33,6 +34,7 @@ app.use(pinia)
pinia.use(piniaPluginPersistedstate)
app.use(router)
app.use(naive)
+app.config.globalProperties.mittBus = mitt()
app.mount('#app')
diff --git a/paimon-web-ui-new/src/router/modules/playground.ts
b/paimon-web-ui-new/src/router/modules/playground.ts
index b8333c1..07863af 100644
--- a/paimon-web-ui-new/src/router/modules/playground.ts
+++ b/paimon-web-ui-new/src/router/modules/playground.ts
@@ -27,7 +27,22 @@ export default [
path: '/playground',
name: 'playground',
meta: { title: '查询控制台' },
- component: () => import('@/views/playground')
+ redirect: { name: 'playground-query' },
+ component: () => import('@/views/playground'),
+ children: [
+ {
+ path: '/playground/query',
+ name: 'playground-query',
+ meta: { title: '查询' },
+ component: () => import('@/views/playground/components/query')
+ },
+ {
+ path: '/playground/workbench',
+ name: 'playground-workbench',
+ meta: { title: '工作台' },
+ component: () => import('@/views/playground/components/workbench')
+ },
+ ]
},
]
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.tsx
b/paimon-web-ui-new/src/views/playground/components/catalog/index.tsx
deleted file mode 100644
index ea44e8c..0000000
--- a/paimon-web-ui-new/src/views/playground/components/catalog/index.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-/* Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements. See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership. The ASF licenses this file
-to you under the Apache License, Version 2.0 (the
-"License"); you may not use this file except in compliance
-with the License. You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing,
-software distributed under the License is distributed on an
-"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-KIND, either express or implied. See the License for the
-specific language governing permissions and limitations
-under the License. */
-
-import i18n from "@/locales"
-import type { TreeOption } from "naive-ui";
-import styles from './index.module.scss'
-
-export default defineComponent({
- name: 'CataLogPage',
- setup() {
- const selectedValue= ref(null)
-
- const options= [
- {
- label: 'catalog1',
- value: 'catalog1'
- },
- {
- label: 'catalog2',
- value: 'catalog2'
- }
- ]
-
- const searchVal = ref('')
-
- const treeData: TreeOption[] = [
- {
- label: 'paimon',
- key: 'paimon',
- children: [
- {
- label: 'paimon-table-01',
- key: 'paimon-table-01',
- children: [
- { label: 'id', key: 'id' },
- { label: 'name', key: 'name' }
- ]
- },
- {
- label: 'paimon-table-02',
- key: 'paimon-table-02',
- children: [
- { label: 'Osaka', key: 'Osaka' },
- ]
- }
- ]
- }
- ]
-
- return { selectedValue, options, searchVal, treeData }
- },
- render() {
- return (
- <div class={styles.container}>
- <div class={styles['select-catalog']}>
- <n-select
- v-model:value={this.selectedValue}
- filterable
- placeholder={i18n.global.t('playground.select_catalog')}
- options={this.options}
- />
- </div>
- <n-space vertical size={12}>
- <n-input v-model:value={this.searchVal}
placeholder={i18n.global.t('playground.search')} />
- <n-tree
- pattern={this.searchVal}
- data={this.treeData}
- block-line
- />
- </n-space>
- </div>
- );
- },
-});
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/query/components/console/index.module.scss
similarity index 85%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/query/components/console/index.module.scss
index e32b957..626d6f8 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/console/index.module.scss
@@ -15,10 +15,15 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
+
+.editor-console {
width: 100%;
+ height: 100%;
+ position: relative;
- .select-catalog {
- padding-bottom: 10px;
+ .operations {
+ position: absolute;
+ top: 17px;
+ right: 20px;
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/query/components/console/index.tsx
b/paimon-web-ui-new/src/views/playground/components/query/components/console/index.tsx
new file mode 100644
index 0000000..4ecf92a
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/console/index.tsx
@@ -0,0 +1,109 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { ChevronDown, ChevronUp, TrashOutline } from '@vicons/ionicons5'
+import styles from './index.module.scss'
+
+export default defineComponent({
+ name: 'EditorConsole',
+ emits: ['ConsoleUp', 'ConsoleDown'],
+ setup(props, { emit }) {
+ const { t } = useLocaleHooks()
+
+ const handleUp = () => {
+ emit('ConsoleUp', 'up')
+ }
+
+ const handleDown = () => {
+ emit('ConsoleDown', 'down')
+ }
+
+ return {
+ t,
+ handleUp,
+ handleDown
+ }
+ },
+ render() {
+ return (
+ <div class={styles['editor-console']}>
+ <n-tabs
+ type="line"
+ size="large"
+ default-value="logs"
+ tabs-padding={20}
+ pane-style="padding: 20px;"
+ >
+ <n-tab-pane name="logs" tab={this.t('playground.logs')}>
+ {this.t('playground.logs')}
+ </n-tab-pane>
+ <n-tab-pane name="result" tab={this.t('playground.result')}>
+ {this.t('playground.result')}
+ </n-tab-pane>
+ </n-tabs>
+ <div class={styles.operations}>
+ <n-space>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ v-slots={{
+ icon: () => <n-icon component={TrashOutline}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.clear')}</span>
+ </n-popover>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ onClick={this.handleUp}
+ v-slots={{
+ icon: () => <n-icon component={ChevronUp}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.unfold')}</span>
+ </n-popover>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ onClick={this.handleDown}
+ v-slots={{
+ icon: () => <n-icon component={ChevronDown}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.collapse')}</span>
+ </n-popover>
+ </n-space>
+ </div>
+ </div>
+ )
+ }
+})
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.module.scss
similarity index 82%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.module.scss
index e32b957..0ab8842 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.module.scss
@@ -15,10 +15,20 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
+
.container {
+ display: flex;
+ align-items: center;
width: 100%;
+ height: 100%;
+
+ .run {
+ display: flex;
+ }
- .select-catalog {
- padding-bottom: 10px;
+ .operations {
+ display: flex;
+ flex: 1;
+ justify-content: flex-end;
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.tsx
b/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.tsx
new file mode 100644
index 0000000..0443172
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.tsx
@@ -0,0 +1,139 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { Play, ChevronDown, ReaderOutline, Save } from '@vicons/ionicons5';
+import styles from './index.module.scss'
+
+export default defineComponent({
+ name: 'EditorDebugger',
+ emits: ['handleFormat', 'handleSave'],
+ setup(props, { emit }) {
+ const { t } = useLocaleHooks()
+
+ const debuggerVariables = reactive({
+ operatingConditionOptions: [
+ {
+ label: 'Limit 100 items',
+ key: "100"
+ },
+ {
+ label: 'Limit 1000 items',
+ key: "1000"
+ },
+ ],
+ conditionValue: 'Flink',
+ bigDataOptions: [
+ {
+ label: 'Flink',
+ value: "Flink"
+ },
+ {
+ label: 'Spark',
+ value: "Spark"
+ },
+ ],
+ conditionValue2: 'test1',
+ clusterOptions: [
+ {
+ label: 'test1',
+ value: "test1"
+ },
+ {
+ label: 'test2',
+ value: "test2"
+ },
+ ]
+ })
+
+ const handleSelect = (key: string) => {
+ console.log(key)
+ }
+
+ const handleFormat = () => {
+ emit('handleFormat')
+ }
+
+ const handleSave = () => {
+ emit('handleSave')
+ }
+
+ return {
+ t,
+ ...toRefs(debuggerVariables),
+ handleSelect,
+ handleFormat,
+ handleSave
+ }
+ },
+ render() {
+ return (
+ <div class={styles.container}>
+ <n-space>
+ <n-button
+ type="primary"
+ v-slots={{
+ icon: () => <n-icon component={Play} />,
+ default: () => {
+ return <div class={styles.run}>
+ {this.t('playground.run')}
+ <n-divider vertical />
+ <n-dropdown trigger="hover" show-arrow
options={this.operatingConditionOptions} on-select={this.handleSelect}>
+ <n-icon component={ChevronDown} />
+ </n-dropdown>
+ </div>
+ }
+ }}
+ ></n-button>
+ <n-select style={'width:160px;'} v-model:value={this.conditionValue}
options={this.bigDataOptions} />
+ <n-select style={'width:160px;'}
v-model:value={this.conditionValue2} options={this.clusterOptions} />
+ </n-space>
+ <div class={styles.operations}>
+ <n-space>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ onClick={this.handleFormat}
+ v-slots={{
+ icon: () => <n-icon component={ReaderOutline}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.format')}</span>
+ </n-popover>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ onClick={this.handleSave}
+ v-slots={{
+ icon: () => <n-icon component={Save}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.save')}</span>
+ </n-popover>
+ </n-space>
+ </div>
+ </div>
+ );
+ }
+});
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.module.scss
similarity index 86%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.module.scss
index e32b957..536333a 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.module.scss
@@ -17,8 +17,17 @@ under the License. */
.container {
width: 100%;
+ height: 100%;
- .select-catalog {
- padding-bottom: 10px;
+ .card {
+ height: 100%;
+
+ .search {
+ display: flex;
+ }
+
+ .icon {
+ display: flex;
+ }
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.tsx
b/paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.tsx
new file mode 100644
index 0000000..552a78b
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.tsx
@@ -0,0 +1,247 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { CodeSlash, FileTrayFullOutline, Search, ServerOutline } from
'@vicons/ionicons5';
+import styles from './index.module.scss'
+import { NIcon, type TreeOption } from 'naive-ui';
+
+export default defineComponent({
+ name: 'MenuTree',
+ setup() {
+ const { t } = useLocaleHooks()
+
+ const treeVariables = reactive({
+ treeData: [
+ {
+ key: 'paimon',
+ label: 'paimon',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(ServerOutline)
+ }),
+ children: [
+ {
+ key: 'user',
+ label: 'user',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(ServerOutline)
+ }),
+ children: [
+ {
+ label: 'user_table',
+ key: '1',
+ content: 'select * from abc where abc.a="abc";select * from
cba where cba.a="cba";',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(FileTrayFullOutline)
+ })
+ },
+ {
+ label: 'people_table',
+ key: '2',
+ content: 'select * from abc where abc.a="abc";',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(FileTrayFullOutline)
+ })
+ }
+ ]
+ },
+ {
+ key: 'role',
+ label: 'role',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(ServerOutline)
+ }),
+ children: [
+ {
+ label: 'user_table',
+ key: '3',
+ content: 'select * from kkk;',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(FileTrayFullOutline)
+ })
+ },
+ ]
+ }
+ ]
+ }
+ ],
+ filterValue: '',
+ selectedKeys: []
+ })
+
+ const nodeProps = ({ option }: { option: TreeOption }) => {
+ return {
+ onClick () {
+ if (option.children) return
+ if (tabData.value.panelsList?.some((item: any) => item.key ===
option.key)) {
+ tabData.value.chooseTab = option.key
+ return
+ }
+ tabData.value.panelsList.push({
+ tableName: option.label,
+ key: option.key,
+ isSaved: false,
+ content: option.content
+ })
+ tabData.value.chooseTab = option.key
+ },
+ }
+ }
+
+ const handleTreeSelect = (value: never[], option: { children: any; }[]) =>
{
+ if (option[0]?.children) return
+ treeVariables.selectedKeys = value
+ }
+
+ // mitt - handle tab choose
+ const tabData = ref({}) as any
+ const { mittBus } =
getCurrentInstance()!.appContext.config.globalProperties
+ mittBus.on('initTabData', (data: any) => {
+ tabData.value = data
+ })
+
+ const savedQueryList = ref([
+ {
+ key: 1,
+ label: 'test1',
+ prefix: () =>
+ h(NIcon, {color: '#0066FF'}, {
+ default: () => h(CodeSlash)
+ }),
+ content: ''
+ },
+ {
+ key: 2,
+ label: 'test2',
+ prefix: () =>
+ h(NIcon, {color: '#0066FF'}, {
+ default: () => h(CodeSlash)
+ }),
+ content: ''
+ }
+ ]) as any
+
+ const recordList = ref([
+ {
+ key: 3,
+ label: 'test3',
+ prefix: () =>
+ h(NIcon, {color: '#0066FF'}, {
+ default: () => h(CodeSlash)
+ }),
+ content: ''
+ },
+ {
+ key: 4,
+ label: 'test4',
+ prefix: () =>
+ h(NIcon, {color: '#0066FF'}, {
+ default: () => h(CodeSlash)
+ }),
+ content: ''
+ }
+ ]) as any
+
+ onMounted(() => {
+ mittBus.emit('initTreeData', treeVariables)
+ })
+
+ return {
+ t,
+ ...toRefs(treeVariables),
+ nodeProps,
+ handleTreeSelect,
+ savedQueryList,
+ recordList
+ }
+ },
+ render() {
+ return (
+ <div class={styles.container}>
+ <n-card class={styles.card} content-style={'padding:7px 18px;'}>
+ <n-tabs default-value="data" justify-content="space-between"
type="line">
+ <n-tab-pane name="data" tab={this.t('playground.data')}>
+ <n-space vertical>
+ <n-input placeholder={this.t('playground.search')}
style="width: 100%;"
+ v-model:value={this.filterValue}
+ v-slots={{
+ prefix: () => <n-icon component={Search} />
+ }}
+ >
+ </n-input>
+ <n-tree
+ block-line
+ expand-on-click
+ selected-keys={this.selectedKeys}
+ on-update:selected-keys={this.handleTreeSelect}
+ data={this.treeData}
+ pattern={this.filterValue}
+ node-props={this.nodeProps}
+ />
+ </n-space>
+ </n-tab-pane>
+ <n-tab-pane name="saved_query"
tab={this.t('playground.saved_query')}>
+ <n-space vertical>
+ <n-input placeholder={this.t('playground.search')}
style="width: 100%;"
+ v-model:value={this.filterValue}
+ v-slots={{
+ prefix: () => <n-icon component={Search} />
+ }}
+ >
+ </n-input>
+ <n-tree
+ block-line
+ expand-on-click
+ selected-keys={this.selectedKeys}
+ on-update:selected-keys={this.handleTreeSelect}
+ data={this.savedQueryList}
+ pattern={this.filterValue}
+ node-props={this.nodeProps}
+ />
+ </n-space>
+ </n-tab-pane>
+ <n-tab-pane name="query_record"
tab={this.t('playground.query_record')}>
+ <n-space vertical>
+ <n-input placeholder={this.t('playground.search')}
style="width: 100%;"
+ v-model:value={this.filterValue}
+ v-slots={{
+ prefix: () => <n-icon component={Search} />
+ }}
+ >
+ </n-input>
+ <n-tree
+ block-line
+ expand-on-click
+ selected-keys={this.selectedKeys}
+ on-update:selected-keys={this.handleTreeSelect}
+ data={this.recordList}
+ pattern={this.filterValue}
+ node-props={this.nodeProps}
+ />
+ </n-space>
+ </n-tab-pane>
+ </n-tabs>
+ </n-card>
+ </div>
+ );
+ }
+});
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.module.scss
similarity index 76%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.module.scss
index e32b957..9b67bd7 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.module.scss
@@ -15,10 +15,21 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
- width: 100%;
- .select-catalog {
- padding-bottom: 10px;
+.tabs {
+ display: flex;
+ align-items: center;
+
+ .dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: #33994A;
+ margin-right: 8px;
+ }
+
+ .asterisk {
+ color: #C82E2E;
+ padding-left: 10px;
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
new file mode 100644
index 0000000..706953c
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.tsx
@@ -0,0 +1,109 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import styles from './index.module.scss'
+
+export default defineComponent({
+ name: 'EditorTabs',
+ setup() {
+ const { mittBus } =
getCurrentInstance()!.appContext.config.globalProperties
+
+ const tabVariables = reactive({
+ chooseTab: '',
+ panelsList: [] as any,
+ })
+
+ const handleAdd = () => {
+ tabVariables.panelsList.push({
+ tableName: 'test' + (tabVariables.panelsList.length + 1),
+ key: 'test' + (tabVariables.panelsList.length + 1),
+ isSaved: false,
+ content: ''
+ })
+ tabVariables.chooseTab = 'test' + tabVariables.panelsList.length
+ }
+
+ const handleClose = (key: any) => {
+ const index = tabVariables.panelsList.findIndex((item: any) => item.key
=== key)
+ tabVariables.panelsList.splice(index, 1)
+ if (key === tabVariables.chooseTab) {
+ if (tabVariables.panelsList[index - 1]) {
+ tabVariables.chooseTab = tabVariables.panelsList[index - 1].key
+ } else {
+ tabVariables.chooseTab = tabVariables.panelsList[index]?.key || ''
+ }
+ }
+ }
+
+ // mitt - handle tree choose
+ const treeData = ref({}) as any
+ const changeTreeChoose = (value: string) => {
+ treeData.value.selectedKeys = [value]
+ tabVariables.chooseTab = value
+ }
+ mittBus.on('initTreeData', (data: any) => {
+ treeData.value = data
+ })
+
+ onMounted(() => {
+ mittBus.emit('initTabData', tabVariables)
+ })
+
+ return {
+ ...toRefs(tabVariables),
+ handleAdd,
+ handleClose,
+ changeTreeChoose
+ }
+ },
+ render() {
+ return (
+ <div class={styles.container}>
+ <n-tabs
+ v-model:value={this.chooseTab}
+ type="card"
+ addable
+ closable
+ tab-style="min-width: 160px;"
+ on-close={this.handleClose}
+ on-add={this.handleAdd}
+ on-update:value={this.changeTreeChoose}
+ v-slots={{
+ prefix: () => '',
+ suffix: () => ''
+ }}
+ >
+ {
+ this.panelsList.map((item: any) => (
+ <n-tab-pane name={item.key}
+ v-slots={{
+ tab: () => (
+ <div class={styles.tabs}>
+ <div class={styles.dot}></div>
+ <div>{item.tableName}</div>
+ {!item.isSaved && <div class={styles.asterisk}>*</div>}
+ </div>
+ )
+ }}
+ ></n-tab-pane>
+ ))
+ }
+ </n-tabs>
+ </div>
+ );
+ }
+});
diff --git a/paimon-web-ui-new/src/views/playground/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/query/index.module.scss
similarity index 67%
copy from paimon-web-ui-new/src/views/playground/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/query/index.module.scss
index 839bb21..369f097 100644
--- a/paimon-web-ui-new/src/views/playground/index.module.scss
+++ b/paimon-web-ui-new/src/views/playground/components/query/index.module.scss
@@ -15,32 +15,45 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
- height: 100%;
+.query {
+ display: flex;
width: 100%;
+ height: 100%;
- :global {
- .n-tabs {
- height: 100%;
- }
+ .menu-tree {
+ width: 20%;
+ height: 100%;
}
- .content {
- display: flex;
+ .editor-area {
+ width: 80%;
height: 100%;
- .catalog {
- width: 350px;
- height: 100%;
- padding: 10px;
- box-sizing: border-box;
+ :global {
+ .n-card {
+ height: 100%;
+ }
}
+ .tabs {
+ display: flex;
+ height: 41px;
+ width: 100%;
+ }
+
+ .debugger {
+ display: flex;
+ height: 64px;
+ }
+
.editor {
- height: 100%;
- flex: 1;
- padding: 10px;
- box-sizing: border-box;
+ height: 60%;
+ overflow-y: scroll;
+ }
+
+ .console {
+ height: 40%;
+ overflow-y: scroll;
}
}
}
diff --git a/paimon-web-ui-new/src/views/playground/components/query/index.tsx
b/paimon-web-ui-new/src/views/playground/components/query/index.tsx
new file mode 100644
index 0000000..2a2ee2d
--- /dev/null
+++ b/paimon-web-ui-new/src/views/playground/components/query/index.tsx
@@ -0,0 +1,138 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import styles from './index.module.scss'
+import MenuTree from './components/menu-tree';
+import EditorTabs from './components/tabs';
+import EditorDebugger from './components/debugger';
+import * as monaco from 'monaco-editor'
+import MonacoEditor from '@/components/monaco-editor';
+import EditorConsole from './components/console';
+import { format } from 'sql-formatter';
+import { useMessage } from 'naive-ui'
+
+export default defineComponent({
+ name: 'QueryPage',
+ setup() {
+ const message = useMessage()
+
+ const editorVariables = reactive({
+ editor: {} as any,
+ language: 'sql'
+ })
+
+ const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => {
+ editorVariables.editor = editor
+ }
+
+ const handleFormat = () => {
+
toRaw(editorVariables.editor).setValue(format(toRaw(editorVariables.editor).getValue()))
+ }
+
+ const editorSave = () => {
+ message.success('Save success')
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).content = toRaw(editorVariables.editor).getValue()
+ handleFormat()
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).isSaved = true
+ }
+
+ const handleContentChange = (value: string) => {
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).content = value
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).isSaved = false
+ }
+
+ const consoleHeightType = ref('down')
+
+ const handleConsoleUp = (type: string) => {
+ consoleHeightType.value = type
+ }
+
+ const handleConsoleDown = (type: string) => {
+ consoleHeightType.value = type
+ }
+
+
+ watch(
+ () => consoleHeightType.value,
+ () => {
+ if (tabData.value.panelsList?.length > 0) {
+ editorVariables.editor?.layout()
+ }
+ }
+ )
+
+ // mitt - handle tab choose
+ const tabData = ref({}) as any
+ const { mittBus } =
getCurrentInstance()!.appContext.config.globalProperties
+ mittBus.on('initTabData', (data: any) => {
+ tabData.value = data
+ })
+
+ return {
+ ...toRefs(editorVariables),
+ editorMounted,
+ editorSave,
+ handleContentChange,
+ handleFormat,
+ tabData,
+ handleConsoleUp,
+ handleConsoleDown,
+ consoleHeightType
+ }
+ },
+ render() {
+ return (
+ <div class={styles.query}>
+ <div class={styles['menu-tree']}>
+ <MenuTree />
+ </div>
+ <div class={styles['editor-area']}>
+ <n-card class={styles.card} content-style={'padding: 5px
18px;display: flex;flex-direction: column;'}>
+ <div class={styles.tabs}>
+ <EditorTabs />
+ </div>
+ <div class={styles.debugger}>
+ <EditorDebugger onHandleFormat={this.handleFormat}
onHandleSave={this.editorSave} />
+ </div>
+ <div class={styles.editor} style={`height:
${this.consoleHeightType === 'up' ? '20%' : '60%'}`}>
+ {
+ this.tabData.panelsList?.length > 0 &&
+ <n-card content-style={'padding: 0;'}>
+ <MonacoEditor
+ v-model={this.tabData.panelsList.find((item: any) =>
item.key === this.tabData.chooseTab).content}
+ language={this.language}
+ onEditorMounted={this.editorMounted}
+ onEditorSave={this.editorSave}
+ onChange={this.handleContentChange}
+ />
+ </n-card>
+ }
+ </div>
+ <div class={styles.console} style={`height:
${this.consoleHeightType === 'up' ? '80%' : '40%'}`}>
+ {
+ this.tabData.panelsList?.length > 0 &&
+ <n-card content-style={'padding: 0;'}>
+ <EditorConsole onConsoleDown={this.handleConsoleDown}
onConsoleUp={this.handleConsoleUp} />
+ </n-card>
+ }
+ </div>
+ </n-card>
+ </div>
+ </div>
+ );
+ }
+});
diff --git a/paimon-web-ui-new/src/views/playground/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/slider/index.module.scss
similarity index 64%
copy from paimon-web-ui-new/src/views/playground/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/slider/index.module.scss
index 839bb21..2fdba5b 100644
--- a/paimon-web-ui-new/src/views/playground/index.module.scss
+++ b/paimon-web-ui-new/src/views/playground/components/slider/index.module.scss
@@ -15,32 +15,36 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
+.slider {
+ display: flex;
+ flex-wrap: wrap;
+ flex-direction: column;
+ width: 60px;
height: 100%;
- width: 100%;
+ padding: 20px 0;
+ box-sizing: border-box;
+ background-color: var(--n-color);
- :global {
- .n-tabs {
- height: 100%;
- }
+ .workspace {
+ display: flex;
+ flex: 1;
+ justify-content: center;
+ width: 100%;
}
- .content {
+ .functional-domain {
display: flex;
- height: 100%;
+ justify-content: center;
+ align-items: flex-end;
+ width: 100%;
+ height: 200px;
+ }
+}
- .catalog {
- width: 350px;
- height: 100%;
- padding: 10px;
- box-sizing: border-box;
- }
+.light {
+ background-color: #fff;
+}
- .editor {
- height: 100%;
- flex: 1;
- padding: 10px;
- box-sizing: border-box;
- }
- }
+.dark {
+ background-color: #18181c;
}
diff --git a/paimon-web-ui-new/src/views/playground/components/slider/index.tsx
b/paimon-web-ui-new/src/views/playground/components/slider/index.tsx
new file mode 100644
index 0000000..8194769
--- /dev/null
+++ b/paimon-web-ui-new/src/views/playground/components/slider/index.tsx
@@ -0,0 +1,143 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import styles from './index.module.scss'
+import { Layers, CodeSlashSharp, Settings, Terminal, GitBranch } from
'@vicons/ionicons5';
+import { useConfigStore } from '@/store/config'
+import { NIcon } from 'naive-ui';
+
+export default defineComponent({
+ name: 'SliderPage',
+ setup() {
+ const configStore = useConfigStore()
+ const { t } = useLocaleHooks()
+ const router = useRouter()
+
+ const renderIcon = (icon: any) => {
+ return () => h(NIcon, { size: 24 }, { default: () => h(icon) })
+ }
+
+ const sliderVariables = reactive({
+ workspaceList: [
+ {
+ icon: renderIcon(Layers),
+ title: 'Layers',
+ description: computed(() => (t('playground.query'))),
+ isClick: true,
+ path: '/playground/query'
+ },
+ {
+ icon: renderIcon(CodeSlashSharp),
+ title: 'Code',
+ description: computed(() => (t('playground.workbench'))),
+ isClick: false,
+ path: '/playground/workbench'
+ },
+ ],
+ domainList: [
+ {
+ icon: renderIcon(Settings),
+ title: 'Settings',
+ description: computed(() => (t('playground.settings')))
+ },
+ {
+ icon: renderIcon(Terminal),
+ title: 'Terminal',
+ description: computed(() => (t('playground.terminal')))
+ },
+ {
+ icon: renderIcon(GitBranch),
+ title: 'GitBranch',
+ description: computed(() => (t('playground.git_branch'))),
+ }
+ ],
+ })
+
+ const handleClick = (index: number, type: string) => {
+ if (type === 'workspace') {
+ for (const i in sliderVariables.workspaceList) {
+ sliderVariables.workspaceList[i].isClick = false
+ }
+ sliderVariables.workspaceList[index].isClick = true
+ router.push(sliderVariables.workspaceList[index].path)
+ }
+ }
+
+ return {
+ configStore,
+ handleClick,
+ ...toRefs(sliderVariables)
+ }
+ },
+ render() {
+ return (
+ <div class={[this.configStore.getCurrentTheme === 'light' ? styles.light
: styles.dark, styles.slider]}>
+ <div class={styles.workspace}>
+ <n-space vertical size={20}>
+ {
+ this.workspaceList.map((item: any, index: number) => {
+ return (
+ <n-popover trigger="hover" placement="right"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ type={item.isClick ? 'primary' : 'default'}
+ text
+ onClick={() => this.handleClick(index, 'workspace')}
+ v-slots={{
+ icon: () => item.icon()
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{item.description}</span>
+ </n-popover>
+ )
+ })
+ }
+ </n-space>
+ </div>
+ <div class={styles['functional-domain']}>
+ <n-space vertical size={20}>
+ {
+ this.domainList.map((item: any, index: number) => {
+ return (
+ <n-popover trigger="hover" placement="right"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ onClick={() => this.handleClick(index, 'domain')}
+ v-slots={{
+ icon: () => item.icon()
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{item.description}</span>
+ </n-popover>
+ )
+ })
+ }
+ </n-space>
+ </div>
+ </div>
+ );
+ },
+});
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.module.scss
similarity index 85%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.module.scss
index e32b957..626d6f8 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.module.scss
@@ -15,10 +15,15 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
+
+.editor-console {
width: 100%;
+ height: 100%;
+ position: relative;
- .select-catalog {
- padding-bottom: 10px;
+ .operations {
+ position: absolute;
+ top: 17px;
+ right: 20px;
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.tsx
b/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.tsx
new file mode 100644
index 0000000..4ecf92a
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.tsx
@@ -0,0 +1,109 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { ChevronDown, ChevronUp, TrashOutline } from '@vicons/ionicons5'
+import styles from './index.module.scss'
+
+export default defineComponent({
+ name: 'EditorConsole',
+ emits: ['ConsoleUp', 'ConsoleDown'],
+ setup(props, { emit }) {
+ const { t } = useLocaleHooks()
+
+ const handleUp = () => {
+ emit('ConsoleUp', 'up')
+ }
+
+ const handleDown = () => {
+ emit('ConsoleDown', 'down')
+ }
+
+ return {
+ t,
+ handleUp,
+ handleDown
+ }
+ },
+ render() {
+ return (
+ <div class={styles['editor-console']}>
+ <n-tabs
+ type="line"
+ size="large"
+ default-value="logs"
+ tabs-padding={20}
+ pane-style="padding: 20px;"
+ >
+ <n-tab-pane name="logs" tab={this.t('playground.logs')}>
+ {this.t('playground.logs')}
+ </n-tab-pane>
+ <n-tab-pane name="result" tab={this.t('playground.result')}>
+ {this.t('playground.result')}
+ </n-tab-pane>
+ </n-tabs>
+ <div class={styles.operations}>
+ <n-space>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ v-slots={{
+ icon: () => <n-icon component={TrashOutline}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.clear')}</span>
+ </n-popover>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ onClick={this.handleUp}
+ v-slots={{
+ icon: () => <n-icon component={ChevronUp}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.unfold')}</span>
+ </n-popover>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ text
+ onClick={this.handleDown}
+ v-slots={{
+ icon: () => <n-icon component={ChevronDown}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.collapse')}</span>
+ </n-popover>
+ </n-space>
+ </div>
+ </div>
+ )
+ }
+})
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.module.scss
similarity index 82%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.module.scss
index e32b957..0ab8842 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.module.scss
@@ -15,10 +15,20 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
+
.container {
+ display: flex;
+ align-items: center;
width: 100%;
+ height: 100%;
+
+ .run {
+ display: flex;
+ }
- .select-catalog {
- padding-bottom: 10px;
+ .operations {
+ display: flex;
+ flex: 1;
+ justify-content: flex-end;
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.tsx
b/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.tsx
new file mode 100644
index 0000000..0443172
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.tsx
@@ -0,0 +1,139 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { Play, ChevronDown, ReaderOutline, Save } from '@vicons/ionicons5';
+import styles from './index.module.scss'
+
+export default defineComponent({
+ name: 'EditorDebugger',
+ emits: ['handleFormat', 'handleSave'],
+ setup(props, { emit }) {
+ const { t } = useLocaleHooks()
+
+ const debuggerVariables = reactive({
+ operatingConditionOptions: [
+ {
+ label: 'Limit 100 items',
+ key: "100"
+ },
+ {
+ label: 'Limit 1000 items',
+ key: "1000"
+ },
+ ],
+ conditionValue: 'Flink',
+ bigDataOptions: [
+ {
+ label: 'Flink',
+ value: "Flink"
+ },
+ {
+ label: 'Spark',
+ value: "Spark"
+ },
+ ],
+ conditionValue2: 'test1',
+ clusterOptions: [
+ {
+ label: 'test1',
+ value: "test1"
+ },
+ {
+ label: 'test2',
+ value: "test2"
+ },
+ ]
+ })
+
+ const handleSelect = (key: string) => {
+ console.log(key)
+ }
+
+ const handleFormat = () => {
+ emit('handleFormat')
+ }
+
+ const handleSave = () => {
+ emit('handleSave')
+ }
+
+ return {
+ t,
+ ...toRefs(debuggerVariables),
+ handleSelect,
+ handleFormat,
+ handleSave
+ }
+ },
+ render() {
+ return (
+ <div class={styles.container}>
+ <n-space>
+ <n-button
+ type="primary"
+ v-slots={{
+ icon: () => <n-icon component={Play} />,
+ default: () => {
+ return <div class={styles.run}>
+ {this.t('playground.run')}
+ <n-divider vertical />
+ <n-dropdown trigger="hover" show-arrow
options={this.operatingConditionOptions} on-select={this.handleSelect}>
+ <n-icon component={ChevronDown} />
+ </n-dropdown>
+ </div>
+ }
+ }}
+ ></n-button>
+ <n-select style={'width:160px;'} v-model:value={this.conditionValue}
options={this.bigDataOptions} />
+ <n-select style={'width:160px;'}
v-model:value={this.conditionValue2} options={this.clusterOptions} />
+ </n-space>
+ <div class={styles.operations}>
+ <n-space>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ onClick={this.handleFormat}
+ v-slots={{
+ icon: () => <n-icon component={ReaderOutline}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.format')}</span>
+ </n-popover>
+ <n-popover trigger="hover" placement="bottom"
+ v-slots={{
+ trigger: () => (
+ <n-button
+ onClick={this.handleSave}
+ v-slots={{
+ icon: () => <n-icon component={Save}></n-icon>
+ }}
+ >
+ </n-button>
+ )
+ }}>
+ <span>{this.t('playground.save')}</span>
+ </n-popover>
+ </n-space>
+ </div>
+ </div>
+ );
+ }
+});
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.module.scss
similarity index 86%
copy from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.module.scss
index e32b957..536333a 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.module.scss
@@ -17,8 +17,17 @@ under the License. */
.container {
width: 100%;
+ height: 100%;
- .select-catalog {
- padding-bottom: 10px;
+ .card {
+ height: 100%;
+
+ .search {
+ display: flex;
+ }
+
+ .icon {
+ display: flex;
+ }
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
new file mode 100644
index 0000000..0cbc368
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.tsx
@@ -0,0 +1,159 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import { CodeSlash, FileTrayFullOutline, Search, ServerOutline } from
'@vicons/ionicons5';
+import styles from './index.module.scss'
+import { NIcon, type TreeOption } from 'naive-ui';
+
+export default defineComponent({
+ name: 'MenuTree',
+ setup() {
+ const { t } = useLocaleHooks()
+
+ const treeVariables = reactive({
+ treeData: [
+ {
+ key: 'paimon2',
+ label: 'paimon2',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(ServerOutline)
+ }),
+ children: [
+ {
+ key: 'user',
+ label: 'user',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(ServerOutline)
+ }),
+ children: [
+ {
+ label: 'user_table',
+ key: '1',
+ content: 'select * from abc where abc.a="abc";select * from
cba where cba.a="cba";',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(FileTrayFullOutline)
+ })
+ },
+ {
+ label: 'people_table',
+ key: '2',
+ content: 'select * from abc where abc.a="abc";',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(FileTrayFullOutline)
+ })
+ }
+ ]
+ },
+ {
+ key: 'role',
+ label: 'role',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(ServerOutline)
+ }),
+ children: [
+ {
+ label: 'user_table',
+ key: '3',
+ content: 'select * from kkk;',
+ prefix: () =>
+ h(NIcon, null, {
+ default: () => h(FileTrayFullOutline)
+ })
+ },
+ ]
+ }
+ ]
+ }
+ ],
+ filterValue: '',
+ selectedKeys: []
+ })
+
+ const nodeProps = ({ option }: { option: TreeOption }) => {
+ return {
+ onClick () {
+ if (option.children) return
+ if (tabData.value.panelsList?.some((item: any) => item.key ===
option.key)) {
+ tabData.value.chooseTab = option.key
+ return
+ }
+ tabData.value.panelsList.push({
+ tableName: option.label,
+ key: option.key,
+ isSaved: false,
+ content: option.content
+ })
+ tabData.value.chooseTab = option.key
+ },
+ }
+ }
+
+ const handleTreeSelect = (value: never[], option: { children: any; }[]) =>
{
+ if (option[0]?.children) return
+ treeVariables.selectedKeys = value
+ }
+
+ // mitt - handle tab choose
+ const tabData = ref({}) as any
+ const { mittBus } =
getCurrentInstance()!.appContext.config.globalProperties
+ mittBus.on('initTabData', (data: any) => {
+ tabData.value = data
+ })
+
+ onMounted(() => {
+ mittBus.emit('initTreeData', treeVariables)
+ })
+
+ return {
+ t,
+ ...toRefs(treeVariables),
+ nodeProps,
+ handleTreeSelect
+ }
+ },
+ render() {
+ return (
+ <div class={styles.container}>
+ <n-card class={styles.card} content-style={'padding:20px 18px;'}>
+ <n-space vertical>
+ <n-input placeholder={this.t('playground.search')} style="width:
100%;"
+ v-model:value={this.filterValue}
+ v-slots={{
+ prefix: () => <n-icon component={Search} />
+ }}
+ >
+ </n-input>
+ <n-tree
+ block-line
+ expand-on-click
+ selected-keys={this.selectedKeys}
+ on-update:selected-keys={this.handleTreeSelect}
+ data={this.treeData}
+ pattern={this.filterValue}
+ node-props={this.nodeProps}
+ />
+ </n-space>
+ </n-card>
+ </div>
+ );
+ }
+});
diff --git
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.module.scss
similarity index 76%
rename from
paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
rename to
paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.module.scss
index e32b957..9b67bd7 100644
---
a/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.module.scss
@@ -15,10 +15,21 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
- width: 100%;
- .select-catalog {
- padding-bottom: 10px;
+.tabs {
+ display: flex;
+ align-items: center;
+
+ .dot {
+ width: 8px;
+ height: 8px;
+ border-radius: 50%;
+ background-color: #33994A;
+ margin-right: 8px;
+ }
+
+ .asterisk {
+ color: #C82E2E;
+ padding-left: 10px;
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
new file mode 100644
index 0000000..706953c
--- /dev/null
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.tsx
@@ -0,0 +1,109 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import styles from './index.module.scss'
+
+export default defineComponent({
+ name: 'EditorTabs',
+ setup() {
+ const { mittBus } =
getCurrentInstance()!.appContext.config.globalProperties
+
+ const tabVariables = reactive({
+ chooseTab: '',
+ panelsList: [] as any,
+ })
+
+ const handleAdd = () => {
+ tabVariables.panelsList.push({
+ tableName: 'test' + (tabVariables.panelsList.length + 1),
+ key: 'test' + (tabVariables.panelsList.length + 1),
+ isSaved: false,
+ content: ''
+ })
+ tabVariables.chooseTab = 'test' + tabVariables.panelsList.length
+ }
+
+ const handleClose = (key: any) => {
+ const index = tabVariables.panelsList.findIndex((item: any) => item.key
=== key)
+ tabVariables.panelsList.splice(index, 1)
+ if (key === tabVariables.chooseTab) {
+ if (tabVariables.panelsList[index - 1]) {
+ tabVariables.chooseTab = tabVariables.panelsList[index - 1].key
+ } else {
+ tabVariables.chooseTab = tabVariables.panelsList[index]?.key || ''
+ }
+ }
+ }
+
+ // mitt - handle tree choose
+ const treeData = ref({}) as any
+ const changeTreeChoose = (value: string) => {
+ treeData.value.selectedKeys = [value]
+ tabVariables.chooseTab = value
+ }
+ mittBus.on('initTreeData', (data: any) => {
+ treeData.value = data
+ })
+
+ onMounted(() => {
+ mittBus.emit('initTabData', tabVariables)
+ })
+
+ return {
+ ...toRefs(tabVariables),
+ handleAdd,
+ handleClose,
+ changeTreeChoose
+ }
+ },
+ render() {
+ return (
+ <div class={styles.container}>
+ <n-tabs
+ v-model:value={this.chooseTab}
+ type="card"
+ addable
+ closable
+ tab-style="min-width: 160px;"
+ on-close={this.handleClose}
+ on-add={this.handleAdd}
+ on-update:value={this.changeTreeChoose}
+ v-slots={{
+ prefix: () => '',
+ suffix: () => ''
+ }}
+ >
+ {
+ this.panelsList.map((item: any) => (
+ <n-tab-pane name={item.key}
+ v-slots={{
+ tab: () => (
+ <div class={styles.tabs}>
+ <div class={styles.dot}></div>
+ <div>{item.tableName}</div>
+ {!item.isSaved && <div class={styles.asterisk}>*</div>}
+ </div>
+ )
+ }}
+ ></n-tab-pane>
+ ))
+ }
+ </n-tabs>
+ </div>
+ );
+ }
+});
diff --git a/paimon-web-ui-new/src/views/playground/index.module.scss
b/paimon-web-ui-new/src/views/playground/components/workbench/index.module.scss
similarity index 66%
copy from paimon-web-ui-new/src/views/playground/index.module.scss
copy to
paimon-web-ui-new/src/views/playground/components/workbench/index.module.scss
index 839bb21..d77f10b 100644
--- a/paimon-web-ui-new/src/views/playground/index.module.scss
+++
b/paimon-web-ui-new/src/views/playground/components/workbench/index.module.scss
@@ -15,32 +15,45 @@ KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License. */
-.container {
- height: 100%;
+.workbench {
+ display: flex;
width: 100%;
+ height: 100%;
- :global {
- .n-tabs {
- height: 100%;
- }
+ .menu-tree {
+ width: 20%;
+ height: 100%;
}
- .content {
- display: flex;
+ .editor-area {
+ width: 80%;
height: 100%;
- .catalog {
- width: 350px;
- height: 100%;
- padding: 10px;
- box-sizing: border-box;
+ :global {
+ .n-card {
+ height: 100%;
+ }
}
+ .tabs {
+ display: flex;
+ height: 41px;
+ width: 100%;
+ }
+
+ .debugger {
+ display: flex;
+ height: 64px;
+ }
+
.editor {
- height: 100%;
- flex: 1;
- padding: 10px;
- box-sizing: border-box;
+ height: 60%;
+ overflow-y: scroll;
+ }
+
+ .console {
+ height: 40%;
+ overflow-y: scroll;
}
}
}
diff --git
a/paimon-web-ui-new/src/views/playground/components/workbench/index.tsx
b/paimon-web-ui-new/src/views/playground/components/workbench/index.tsx
new file mode 100644
index 0000000..6024369
--- /dev/null
+++ b/paimon-web-ui-new/src/views/playground/components/workbench/index.tsx
@@ -0,0 +1,138 @@
+/* Licensed to the Apache Software Foundation (ASF) under one
+or more contributor license agreements. See the NOTICE file
+distributed with this work for additional information
+regarding copyright ownership. The ASF licenses this file
+to you under the Apache License, Version 2.0 (the
+"License"); you may not use this file except in compliance
+with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing,
+software distributed under the License is distributed on an
+"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied. See the License for the
+specific language governing permissions and limitations
+under the License. */
+
+import styles from './index.module.scss'
+import MenuTree from './components/menu-tree';
+import EditorTabs from './components/tabs';
+import EditorDebugger from './components/debugger';
+import * as monaco from 'monaco-editor'
+import MonacoEditor from '@/components/monaco-editor';
+import EditorConsole from './components/console';
+import { format } from 'sql-formatter';
+import { useMessage } from 'naive-ui'
+
+export default defineComponent({
+ name: 'WorkbenchPage',
+ setup() {
+ const message = useMessage()
+
+ const editorVariables = reactive({
+ editor: {} as any,
+ language: 'sql'
+ })
+
+ const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => {
+ editorVariables.editor = editor
+ }
+
+ const handleFormat = () => {
+
toRaw(editorVariables.editor).setValue(format(toRaw(editorVariables.editor).getValue()))
+ }
+
+ const editorSave = () => {
+ message.success('Save success')
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).content = toRaw(editorVariables.editor).getValue()
+ handleFormat()
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).isSaved = true
+ }
+
+ const handleContentChange = (value: string) => {
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).content = value
+ tabData.value.panelsList.find((item: any) => item.key ===
tabData.value.chooseTab).isSaved = false
+ }
+
+ const consoleHeightType = ref('down')
+
+ const handleConsoleUp = (type: string) => {
+ consoleHeightType.value = type
+ }
+
+ const handleConsoleDown = (type: string) => {
+ consoleHeightType.value = type
+ }
+
+
+ watch(
+ () => consoleHeightType.value,
+ () => {
+ if (tabData.value.panelsList?.length > 0) {
+ editorVariables.editor?.layout()
+ }
+ }
+ )
+
+ // mitt - handle tab choose
+ const tabData = ref({}) as any
+ const { mittBus } =
getCurrentInstance()!.appContext.config.globalProperties
+ mittBus.on('initTabData', (data: any) => {
+ tabData.value = data
+ })
+
+ return {
+ ...toRefs(editorVariables),
+ editorMounted,
+ editorSave,
+ handleContentChange,
+ handleFormat,
+ tabData,
+ handleConsoleUp,
+ handleConsoleDown,
+ consoleHeightType
+ }
+ },
+ render() {
+ return (
+ <div class={styles.workbench}>
+ <div class={styles['menu-tree']}>
+ <MenuTree />
+ </div>
+ <div class={styles['editor-area']}>
+ <n-card class={styles.card} content-style={'padding: 5px
18px;display: flex;flex-direction: column;'}>
+ <div class={styles.tabs}>
+ <EditorTabs />
+ </div>
+ <div class={styles.debugger}>
+ <EditorDebugger onHandleFormat={this.handleFormat}
onHandleSave={this.editorSave} />
+ </div>
+ <div class={styles.editor} style={`height:
${this.consoleHeightType === 'up' ? '20%' : '60%'}`}>
+ {
+ this.tabData.panelsList?.length > 0 &&
+ <n-card content-style={'padding: 0;'}>
+ <MonacoEditor
+ v-model={this.tabData.panelsList.find((item: any) =>
item.key === this.tabData.chooseTab).content}
+ language={this.language}
+ onEditorMounted={this.editorMounted}
+ onEditorSave={this.editorSave}
+ onChange={this.handleContentChange}
+ />
+ </n-card>
+ }
+ </div>
+ <div class={styles.console} style={`height:
${this.consoleHeightType === 'up' ? '80%' : '40%'}`}>
+ {
+ this.tabData.panelsList?.length > 0 &&
+ <n-card content-style={'padding: 0;'}>
+ <EditorConsole onConsoleDown={this.handleConsoleDown}
onConsoleUp={this.handleConsoleUp} />
+ </n-card>
+ }
+ </div>
+ </n-card>
+ </div>
+ </div>
+ );
+ }
+});
diff --git a/paimon-web-ui-new/src/views/playground/index.module.scss
b/paimon-web-ui-new/src/views/playground/index.module.scss
index 839bb21..fe437d9 100644
--- a/paimon-web-ui-new/src/views/playground/index.module.scss
+++ b/paimon-web-ui-new/src/views/playground/index.module.scss
@@ -16,31 +16,13 @@ specific language governing permissions and limitations
under the License. */
.container {
- height: 100%;
+ display: flex;
width: 100%;
-
- :global {
- .n-tabs {
- height: 100%;
- }
- }
+ height: 100%;
.content {
display: flex;
+ width: calc(100% - 60px);
height: 100%;
-
- .catalog {
- width: 350px;
- height: 100%;
- padding: 10px;
- box-sizing: border-box;
- }
-
- .editor {
- height: 100%;
- flex: 1;
- padding: 10px;
- box-sizing: border-box;
- }
}
}
diff --git a/paimon-web-ui-new/src/views/playground/index.tsx
b/paimon-web-ui-new/src/views/playground/index.tsx
index 448b0de..1d133cf 100644
--- a/paimon-web-ui-new/src/views/playground/index.tsx
+++ b/paimon-web-ui-new/src/views/playground/index.tsx
@@ -16,85 +16,18 @@ specific language governing permissions and limitations
under the License. */
import styles from './index.module.scss';
-import type { TabsProps } from 'naive-ui';
-import { Layers, CodeSlashSharp, SyncCircleOutline } from '@vicons/ionicons5';
-import CataLog from './components/catalog';
-import * as monaco from 'monaco-editor'
-import MonacoEditor from '@/components/monaco-editor';
-
+import Slider from './components/slider';
export default defineComponent({
name: 'PlaygroundPage',
- setup() {
- const type = ref<TabsProps['type']>('bar')
-
- const content = ref('')
- const language = ref('javascript')
- const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => {
- console.log('Loaded editor instance.', editor)
- }
-
- return {
- type,
- content,
- language,
- editorMounted
- }
- },
+ setup() {},
render() {
return (
<div class={styles.container}>
- <n-tabs
- type={this.type}
- animated
- placement="left"
- default-value="oasis"
- >
- <n-tab-pane name="oasis"
- v-slots={{
- tab: () => (
- <n-icon size="24">
- <Layers />
- </n-icon>
- )
- }}
- >
- <div class={styles.content}>
- <div class={styles.catalog}>
- <CataLog />
- </div>
- <div class={styles.editor}>
- <MonacoEditor
- v-model={this.content}
- language={this.language}
- onEditorMounted={this.editorMounted}
- />
- </div>
- </div>
- </n-tab-pane>
- <n-tab-pane name="the beatles" tab="the Beatles"
- v-slots={{
- tab: () => (
- <n-icon size="24">
- <CodeSlashSharp />
- </n-icon>
- )
- }}
- >
- Saved Queries
- </n-tab-pane>
- <n-tab-pane name="jay chou" tab="Jay Chou"
- v-slots={{
- tab: () => (
- <n-icon size="24">
- <SyncCircleOutline />
- </n-icon>
- )
- }}
- >
- History
- </n-tab-pane>
- </n-tabs>
+ <Slider />
+ <div class={styles.content}>
+ <router-view />
+ </div>
</div>
);
},