diff --git a/paimon-web-ui-new/package.json b/paimon-web-ui-new/package.json index e3443bbd8..6fb7ba914 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 17b654a3a..eb3e50d76 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(sass@1.66.1)(webpack@5.88.2) + sql-formatter: + specifier: ^13.0.0 + version: 13.0.0 vue: specifier: ^3.3.4 version: 3.3.4 @@ -1393,7 +1399,6 @@ packages: /argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - dev: true /array-buffer-byte-length@1.0.0: resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} @@ -1756,6 +1761,10 @@ packages: path-type: 4.0.0 dev: true + /discontinuous-range@1.0.0: + resolution: {integrity: sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ==} + dev: false + /doctrine@3.0.0: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} @@ -2224,6 +2233,11 @@ packages: has-symbols: 1.0.3 dev: true + /get-stdin@8.0.0: + resolution: {integrity: sha512-sY22aA6xchAzprjyqmSEQv4UbAAzRN0L2dQB0NlN5acTTK9Don6nhoc3eAbUnpZiCANAMfd/+40kVdKfFygohg==} + engines: {node: '>=10'} + dev: false + /get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} @@ -2770,6 +2784,10 @@ packages: brace-expansion: 2.0.1 dev: true + /mitt@3.0.1: + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} + dev: false + /mlly@1.4.2: resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} dependencies: @@ -2783,6 +2801,10 @@ packages: resolution: {integrity: sha512-cnoqwQi/9fml2Szamv1XbSJieGJ1Dc8tENVMD26Kcfl7xGQWp7OBKMjlwKVGYFJ3/AXJjSOGvcqK7Ry/j9BM1Q==} dev: false + /moo@0.5.2: + resolution: {integrity: sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true @@ -2830,6 +2852,16 @@ packages: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} dev: true + /nearley@2.20.1: + 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 + /neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} dev: false @@ -3139,6 +3171,18 @@ packages: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} dev: true + /railroad-diagrams@1.0.0: + resolution: {integrity: sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A==} + dev: false + + /randexp@0.4.6: + resolution: {integrity: sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==} + engines: {node: '>=0.12'} + dependencies: + discontinuous-range: 1.0.0 + ret: 0.1.15 + dev: false + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: @@ -3187,6 +3231,11 @@ packages: supports-preserve-symlinks-flag: 1.0.0 dev: true + /ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + dev: false + /reusify@1.0.4: 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 + /sql-formatter@13.0.0: + 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 + /string.prototype.padend@3.1.5: 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 96768c73c..90b23e0e5 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' }} > - + + + } }) 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 5bf54afe1..55a917dfa 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 a611096da..abfd5c171 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 feedda3e8..9babf1786 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 e9350cb32..2787b5b2e 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 82a46d970..303ab837e 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 b8333c1c6..07863afe2 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 ea44e8c56..000000000 --- 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 ( -
-
- -
- - - - -
- ); - }, -}); diff --git a/paimon-web-ui-new/src/views/playground/components/query/components/console/index.module.scss b/paimon-web-ui-new/src/views/playground/components/query/components/console/index.module.scss new file mode 100644 index 000000000..626d6f8b2 --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/query/components/console/index.module.scss @@ -0,0 +1,29 @@ +/* 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. */ + + +.editor-console { + width: 100%; + height: 100%; + position: relative; + + .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 000000000..4ecf92a78 --- /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 ( +
+ + + {this.t('playground.logs')} + + + {this.t('playground.result')} + + +
+ + ( + + }} + > + + ) + }}> + {this.t('playground.clear')} + + ( + + }} + > + + ) + }}> + {this.t('playground.unfold')} + + ( + + }} + > + + ) + }}> + {this.t('playground.collapse')} + + +
+
+ ) + } +}) diff --git a/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.module.scss b/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.module.scss new file mode 100644 index 000000000..0ab88421f --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/query/components/debugger/index.module.scss @@ -0,0 +1,34 @@ +/* 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. */ + + +.container { + display: flex; + align-items: center; + width: 100%; + height: 100%; + + .run { + display: flex; + } + + .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 000000000..044317272 --- /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 ( +
+ + , + default: () => { + return
+ {this.t('playground.run')} + + + + +
+ } + }} + >
+ + +
+
+ + ( + + }} + > + + ) + }}> + {this.t('playground.format')} + + ( + + }} + > + + ) + }}> + {this.t('playground.save')} + + +
+
+ ); + } +}); 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% rename from paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss rename to paimon-web-ui-new/src/views/playground/components/query/components/menu-tree/index.module.scss index e32b957e6..536333ae0 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 000000000..552a78baa --- /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 ( +
+ + + + + + }} + > + + + + + + + + }} + > + + + + + + + + }} + > + + + + + + +
+ ); + } +}); diff --git a/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.module.scss b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.module.scss new file mode 100644 index 000000000..9b67bd79a --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/query/components/tabs/index.module.scss @@ -0,0 +1,35 @@ +/* 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. */ + + +.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 000000000..706953cf7 --- /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 ( +
+ '', + suffix: () => '' + }} + > + { + this.panelsList.map((item: any) => ( + ( +
+
+
{item.tableName}
+ {!item.isSaved &&
*
} +
+ ) + }} + >
+ )) + } +
+
+ ); + } +}); diff --git a/paimon-web-ui-new/src/views/playground/components/query/index.module.scss b/paimon-web-ui-new/src/views/playground/components/query/index.module.scss new file mode 100644 index 000000000..369f097e4 --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/query/index.module.scss @@ -0,0 +1,59 @@ +/* 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. */ + +.query { + display: flex; + width: 100%; + height: 100%; + + .menu-tree { + width: 20%; + height: 100%; + } + + .editor-area { + width: 80%; + height: 100%; + + :global { + .n-card { + height: 100%; + } + } + + .tabs { + display: flex; + height: 41px; + width: 100%; + } + + .debugger { + display: flex; + height: 64px; + } + + .editor { + 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 000000000..2a2ee2d50 --- /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 ( +
+
+ +
+
+ +
+ +
+
+ +
+
+ { + this.tabData.panelsList?.length > 0 && + + item.key === this.tabData.chooseTab).content} + language={this.language} + onEditorMounted={this.editorMounted} + onEditorSave={this.editorSave} + onChange={this.handleContentChange} + /> + + } +
+
+ { + this.tabData.panelsList?.length > 0 && + + + + } +
+
+
+
+ ); + } +}); diff --git a/paimon-web-ui-new/src/views/playground/components/slider/index.module.scss b/paimon-web-ui-new/src/views/playground/components/slider/index.module.scss new file mode 100644 index 000000000..2fdba5bef --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/slider/index.module.scss @@ -0,0 +1,50 @@ +/* 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. */ + +.slider { + display: flex; + flex-wrap: wrap; + flex-direction: column; + width: 60px; + height: 100%; + padding: 20px 0; + box-sizing: border-box; + background-color: var(--n-color); + + .workspace { + display: flex; + flex: 1; + justify-content: center; + width: 100%; + } + + .functional-domain { + display: flex; + justify-content: center; + align-items: flex-end; + width: 100%; + height: 200px; + } +} + +.light { + background-color: #fff; +} + +.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 000000000..819476945 --- /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 ( +
+
+ + { + this.workspaceList.map((item: any, index: number) => { + return ( + ( + this.handleClick(index, 'workspace')} + v-slots={{ + icon: () => item.icon() + }} + > + + ) + }}> + {item.description} + + ) + }) + } + +
+
+ + { + this.domainList.map((item: any, index: number) => { + return ( + ( + this.handleClick(index, 'domain')} + v-slots={{ + icon: () => item.icon() + }} + > + + ) + }}> + {item.description} + + ) + }) + } + +
+
+ ); + }, +}); diff --git a/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.module.scss b/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.module.scss new file mode 100644 index 000000000..626d6f8b2 --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/workbench/components/console/index.module.scss @@ -0,0 +1,29 @@ +/* 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. */ + + +.editor-console { + width: 100%; + height: 100%; + position: relative; + + .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 000000000..4ecf92a78 --- /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 ( +
+ + + {this.t('playground.logs')} + + + {this.t('playground.result')} + + +
+ + ( + + }} + > + + ) + }}> + {this.t('playground.clear')} + + ( + + }} + > + + ) + }}> + {this.t('playground.unfold')} + + ( + + }} + > + + ) + }}> + {this.t('playground.collapse')} + + +
+
+ ) + } +}) diff --git a/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.module.scss b/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.module.scss new file mode 100644 index 000000000..0ab88421f --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/workbench/components/debugger/index.module.scss @@ -0,0 +1,34 @@ +/* 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. */ + + +.container { + display: flex; + align-items: center; + width: 100%; + height: 100%; + + .run { + display: flex; + } + + .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 000000000..044317272 --- /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 ( +
+ + , + default: () => { + return
+ {this.t('playground.run')} + + + + +
+ } + }} + >
+ + +
+
+ + ( + + }} + > + + ) + }}> + {this.t('playground.format')} + + ( + + }} + > + + ) + }}> + {this.t('playground.save')} + + +
+
+ ); + } +}); diff --git a/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.module.scss b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.module.scss new file mode 100644 index 000000000..536333ae0 --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/workbench/components/menu-tree/index.module.scss @@ -0,0 +1,33 @@ +/* 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. */ + +.container { + width: 100%; + height: 100%; + + .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 000000000..0cbc3688a --- /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 ( +
+ + + + }} + > + + + + +
+ ); + } +}); diff --git a/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.module.scss b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.module.scss new file mode 100644 index 000000000..9b67bd79a --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/workbench/components/tabs/index.module.scss @@ -0,0 +1,35 @@ +/* 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. */ + + +.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 000000000..706953cf7 --- /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 ( +
+ '', + suffix: () => '' + }} + > + { + this.panelsList.map((item: any) => ( + ( +
+
+
{item.tableName}
+ {!item.isSaved &&
*
} +
+ ) + }} + >
+ )) + } +
+
+ ); + } +}); diff --git a/paimon-web-ui-new/src/views/playground/components/workbench/index.module.scss b/paimon-web-ui-new/src/views/playground/components/workbench/index.module.scss new file mode 100644 index 000000000..d77f10b4a --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/workbench/index.module.scss @@ -0,0 +1,59 @@ +/* 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. */ + +.workbench { + display: flex; + width: 100%; + height: 100%; + + .menu-tree { + width: 20%; + height: 100%; + } + + .editor-area { + width: 80%; + height: 100%; + + :global { + .n-card { + height: 100%; + } + } + + .tabs { + display: flex; + height: 41px; + width: 100%; + } + + .debugger { + display: flex; + height: 64px; + } + + .editor { + 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 000000000..602436970 --- /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 ( +
+
+ +
+
+ +
+ +
+
+ +
+
+ { + this.tabData.panelsList?.length > 0 && + + item.key === this.tabData.chooseTab).content} + language={this.language} + onEditorMounted={this.editorMounted} + onEditorSave={this.editorSave} + onChange={this.handleContentChange} + /> + + } +
+
+ { + this.tabData.panelsList?.length > 0 && + + + + } +
+
+
+
+ ); + } +}); 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 839bb211e..fe437d971 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 448b0de6e..1d133cfbe 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('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 (
- - ( - - - - ) - }} - > -
-
- -
-
- -
-
-
- ( - - - - ) - }} - > - Saved Queries - - ( - - - - ) - }} - > - History - -
+ +
+ +
); },