diff --git a/paimon-web-ui-new/package.json b/paimon-web-ui-new/package.json index c5a1f4e30..e3443bbd8 100644 --- a/paimon-web-ui-new/package.json +++ b/paimon-web-ui-new/package.json @@ -13,6 +13,7 @@ }, "dependencies": { "dart-sass": "^1.25.0", + "monaco-editor": "^0.43.0", "pinia": "^2.1.6", "pinia-plugin-persistedstate": "^3.2.0", "sass": "^1.66.1", diff --git a/paimon-web-ui-new/pnpm-lock.yaml b/paimon-web-ui-new/pnpm-lock.yaml index 05a47af95..17b654a3a 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 + monaco-editor: + specifier: ^0.43.0 + version: 0.43.0 pinia: specifier: ^2.1.6 version: 2.1.6(typescript@5.1.6)(vue@3.3.4) @@ -2776,6 +2779,10 @@ packages: ufo: 1.3.0 dev: true + /monaco-editor@0.43.0: + resolution: {integrity: sha512-cnoqwQi/9fml2Szamv1XbSJieGJ1Dc8tENVMD26Kcfl7xGQWp7OBKMjlwKVGYFJ3/AXJjSOGvcqK7Ry/j9BM1Q==} + dev: false + /ms@2.1.2: resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} dev: true diff --git a/paimon-web-ui-new/src/components/monaco-editor/index.tsx b/paimon-web-ui-new/src/components/monaco-editor/index.tsx new file mode 100644 index 000000000..5bf54afe1 --- /dev/null +++ b/paimon-web-ui-new/src/components/monaco-editor/index.tsx @@ -0,0 +1,124 @@ +/* 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 * as monaco from 'monaco-editor' +import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker' +import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker' +import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker' +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' + +// @ts-ignore: worker +self.MonacoEnvironment = { + getWorker(_: string, label: string) { + if (label === 'json') { + return new jsonWorker() + } + if (['css', 'scss', 'less'].includes(label)) { + return new cssWorker() + } + if (['html', 'handlebars', 'razor'].includes(label)) { + return new htmlWorker() + } + if (['typescript', 'javascript'].includes(label)) { + return new tsWorker() + } + return new EditorWorker() + } +} + +export default defineComponent({ + name: 'MonacoEditor', + props: editorProps, + emits: ['update:modelValue', 'change', 'EditorMounted'], + setup(props, { emit }) { + const configStore = useConfigStore() + const monacoEditorThemeRef = ref(configStore.getCurrentTheme === 'dark' ? 'vs-dark' : 'vs') + let editor: monaco.editor.IStandaloneCodeEditor + const codeEditBox = ref() + const init = () => { + monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ + noSemanticValidation: true, + noSyntaxValidation: false + }) + monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ + target: monaco.languages.typescript.ScriptTarget.ES2020, + allowNonTsExtensions: true + }) + editor = monaco.editor.create(codeEditBox.value, { + value: props.modelValue, + language: props.language, + theme: monacoEditorThemeRef.value, + ...props.options + }) + editor.onDidChangeModelContent(() => { + const value = editor.getValue() + emit('update:modelValue', value) + emit('change', value) + }) + emit('EditorMounted', editor) + } + watch( + () => props.modelValue, + newValue => { + if (editor) { + const value = editor.getValue() + if (newValue !== value) { + editor.setValue(newValue) + } + } + } + ) + watch( + () => props.options, + newValue => { + editor.updateOptions(newValue) + }, + { deep: true } + ) + watch( + () => props.language, + newValue => { + monaco.editor.setModelLanguage(editor.getModel()!, newValue) + } + ) + watch( + () => configStore.getCurrentTheme, + () => { + editor?.dispose() + monacoEditorThemeRef.value = configStore.getCurrentTheme === 'dark' ? 'vs-dark' : 'vs' + init() + } + ) + onBeforeUnmount(() => { + editor.dispose() + }) + onMounted(() => { + init() + }) + return { codeEditBox } + }, + render () { + return ( +
+ ) + } +}) diff --git a/paimon-web-ui-new/src/components/monaco-editor/type.ts b/paimon-web-ui-new/src/components/monaco-editor/type.ts new file mode 100644 index 000000000..a611096da --- /dev/null +++ b/paimon-web-ui-new/src/components/monaco-editor/type.ts @@ -0,0 +1,79 @@ +/* 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. */ + +export type Theme = 'vs' | 'vs-dark' +export type FoldingStrategy = 'auto' | 'indentation' +export type RenderLineHighlight = 'all' | 'line' | 'none' | 'gutter' +export interface Options { + automaticLayout?: boolean + foldingStrategy?: FoldingStrategy + renderLineHighlight?: RenderLineHighlight + selectOnLineNumbers?: boolean + minimap?: { + enabled: boolean + } + readOnly: boolean + contextmenu: boolean + fontSize?: number + scrollBeyondLastLine?: boolean + overviewRulerBorder?: boolean +} + +export const editorProps = { + modelValue: { + type: String as PropType, + default: null + }, + width: { + type: [String, Number] as PropType, + default: '100%' + }, + height: { + type: [String, Number] as PropType, + default: '100%' + }, + language: { + type: String as PropType, + default: 'javascript' + }, + theme: { + type: String as PropType, + validator(value: string): boolean { + return ['vs', 'vs-dark'].includes(value) + }, + default: 'vs' + }, + options: { + type: Object as PropType, + default() { + return { + automaticLayout: true, + foldingStrategy: 'indentation', + renderLineHighlight: 'line', + selectOnLineNumbers: true, + minimap: { + enabled: true + }, + readOnly: false, + contextmenu: true, + fontSize: 16, + scrollBeyondLastLine: false, + overviewRulerBorder: false + } + } + } +} diff --git a/paimon-web-ui-new/src/layouts/content/components/toolbar/index.tsx b/paimon-web-ui-new/src/layouts/content/components/toolbar/index.tsx index 0925a43da..a436959f4 100644 --- a/paimon-web-ui-new/src/layouts/content/components/toolbar/index.tsx +++ b/paimon-web-ui-new/src/layouts/content/components/toolbar/index.tsx @@ -17,7 +17,7 @@ under the License. */ import i18n from '@/locales' import { useConfigStore } from '@/store/config' -import { LogoGithub, Moon, SunnyOutline, Language, PersonCircleOutline } from '@vicons/ionicons5' +import { LogoGithub, Moon, SunnyOutline, Language } from '@vicons/ionicons5' export default defineComponent({ name: 'ToolBar', diff --git a/paimon-web-ui-new/src/locales/en/index.ts b/paimon-web-ui-new/src/locales/en/index.ts index 08e479fc0..db4b7138e 100644 --- a/paimon-web-ui-new/src/locales/en/index.ts +++ b/paimon-web-ui-new/src/locales/en/index.ts @@ -17,8 +17,10 @@ under the License. */ import layout from './modules/layout' import login from './modules/login' +import playground from './modules/playground' export default { login, - layout -} \ No newline at end of file + layout, + playground +} diff --git a/paimon-web-ui-new/src/locales/en/modules/playground.ts b/paimon-web-ui-new/src/locales/en/modules/playground.ts new file mode 100644 index 000000000..feedda3e8 --- /dev/null +++ b/paimon-web-ui-new/src/locales/en/modules/playground.ts @@ -0,0 +1,21 @@ +/* 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. */ + +export default { + select_catalog: 'Select Catalog', + search: 'Search' +} diff --git a/paimon-web-ui-new/src/locales/zh/index.ts b/paimon-web-ui-new/src/locales/zh/index.ts index 08e479fc0..db4b7138e 100644 --- a/paimon-web-ui-new/src/locales/zh/index.ts +++ b/paimon-web-ui-new/src/locales/zh/index.ts @@ -17,8 +17,10 @@ under the License. */ import layout from './modules/layout' import login from './modules/login' +import playground from './modules/playground' export default { login, - layout -} \ No newline at end of file + layout, + playground +} diff --git a/paimon-web-ui-new/src/locales/zh/modules/playground.ts b/paimon-web-ui-new/src/locales/zh/modules/playground.ts new file mode 100644 index 000000000..e9350cb32 --- /dev/null +++ b/paimon-web-ui-new/src/locales/zh/modules/playground.ts @@ -0,0 +1,21 @@ +/* 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. */ + +export default { + select_catalog: '选择 Catalog', + search: '搜索' +} diff --git a/paimon-web-ui-new/src/router/index.ts b/paimon-web-ui-new/src/router/index.ts index c55aaae0e..e2670e99f 100644 --- a/paimon-web-ui-new/src/router/index.ts +++ b/paimon-web-ui-new/src/router/index.ts @@ -15,25 +15,31 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import { createRouter, createWebHistory } from 'vue-router' +import { + createRouter, + createWebHistory, + type RouteLocationNormalized, + type NavigationGuardNext +} from 'vue-router' +import routes from './routes' + const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), - routes: [ - { - path: '/', - name: 'homepage', - // route level code-splitting - // this generates a separate chunk (About.[hash].js) for this route - // which is lazy-loaded when the route is visited. - component: () => import('../layouts/content') - }, - { - path: '/login', - name: 'login', - component: () => import('../views/login') - } - ] + routes }) +/** + * Routing to intercept + */ +router.beforeEach( + async ( + to: RouteLocationNormalized, + from: RouteLocationNormalized, + next: NavigationGuardNext + ) => { + next() + } +) + export default router diff --git a/paimon-web-ui-new/src/router/modules/playground.ts b/paimon-web-ui-new/src/router/modules/playground.ts new file mode 100644 index 000000000..b8333c1c6 --- /dev/null +++ b/paimon-web-ui-new/src/router/modules/playground.ts @@ -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. */ + +export default [ + { + path: '/', + name: 'homepage', + meta: { title: '首页' }, + redirect: { name: 'playground' }, + component: () => import('@/layouts/content'), + children: [ + { + path: '/playground', + name: 'playground', + meta: { title: '查询控制台' }, + component: () => import('@/views/playground') + }, + ] + } +] diff --git a/paimon-web-ui-new/src/router/routes.ts b/paimon-web-ui-new/src/router/routes.ts new file mode 100644 index 000000000..15339e49b --- /dev/null +++ b/paimon-web-ui-new/src/router/routes.ts @@ -0,0 +1,42 @@ +/* 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 type { RouteRecordRaw } from 'vue-router' +import playground_routes from './modules/playground' + +/** + * Basic page + */ +const basePage: RouteRecordRaw[] = [ + ...playground_routes, +] + +/** + * Login page + */ +const loginPage: RouteRecordRaw[] = [ + { + path: '/login', + name: 'login', + component: () => import('../views/login') + } +] + + +const routes: RouteRecordRaw[] = [...basePage, ...loginPage] + +export default routes diff --git a/paimon-web-ui-new/src/store/config/index.ts b/paimon-web-ui-new/src/store/config/index.ts index ec3e8bf34..dac0a6c9a 100644 --- a/paimon-web-ui-new/src/store/config/index.ts +++ b/paimon-web-ui-new/src/store/config/index.ts @@ -24,6 +24,7 @@ export const useConfigStore = defineStore({ theme: 'light', locale: 'zh' }), + persist: true, getters: { getCurrentLocale(): Locale { return this.locale @@ -40,4 +41,4 @@ export const useConfigStore = defineStore({ this.theme = theme } } -}) \ No newline at end of file +}) diff --git a/paimon-web-ui-new/src/views/homepage/index.tsx b/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss similarity index 68% rename from paimon-web-ui-new/src/views/homepage/index.tsx rename to paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss index 11292ebec..e32b957e6 100644 --- a/paimon-web-ui-new/src/views/homepage/index.tsx +++ b/paimon-web-ui-new/src/views/playground/components/catalog/index.module.scss @@ -15,22 +15,10 @@ KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ -import { defineComponent } from 'vue'; +.container { + width: 100%; -export default defineComponent({ - name: 'HomePage', - setup() { - const handleRequest = () => {} - - return { handleRequest } - }, - render() { - return ( -
-
- Request Test -
-
- ); - }, -}); + .select-catalog { + padding-bottom: 10px; + } +} 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 new file mode 100644 index 000000000..ea44e8c56 --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/components/catalog/index.tsx @@ -0,0 +1,88 @@ +/* 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/index.module.scss b/paimon-web-ui-new/src/views/playground/index.module.scss new file mode 100644 index 000000000..839bb211e --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/index.module.scss @@ -0,0 +1,46 @@ +/* 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 { + height: 100%; + width: 100%; + + :global { + .n-tabs { + height: 100%; + } + } + + .content { + display: flex; + 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 new file mode 100644 index 000000000..448b0de6e --- /dev/null +++ b/paimon-web-ui-new/src/views/playground/index.tsx @@ -0,0 +1,101 @@ +/* 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 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'; + + +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 + } + }, + render() { + return ( +
+ + ( + + + + ) + }} + > +
+
+ +
+
+ +
+
+
+ ( + + + + ) + }} + > + Saved Queries + + ( + + + + ) + }} + > + History + +
+
+ ); + }, +});