diff --git a/ui/README.md b/ui/README.md new file mode 100644 index 0000000000..b4861ef5f5 --- /dev/null +++ b/ui/README.md @@ -0,0 +1,73 @@ + + +## Getting Started + +Installation dependencies + +```bash +$ npm install +``` +or use yarn +``` +$ npm install -g yarn +$ yarn install --pure-lockfile +``` + +Start server. + +```bash +$ npm run dev + +# visit http://localhost:8233 +``` + +Submit code + +```bash +$ git commit -m "xxx" will automatically run npm run lint to check grammar rules +``` + +Construct + +```bash +$ npm run build +``` + +Technology stack convention + +``` +react + react-router-dom + ant-design + rxjs +``` + +## File introduction + +``` + public: some static resources + + src: development home directory + assets: static resources, pictures, etc., refer to webpack + components: common components + pages: subpages, can contain subcomponents + utils: public methods + + webpack.config.js: webpack configuration + +``` + diff --git a/ui/config/helpers.js b/ui/config/helpers.js new file mode 100644 index 0000000000..e0250a53a8 --- /dev/null +++ b/ui/config/helpers.js @@ -0,0 +1,31 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +const path = require('path'); + +const rootPath = path.resolve(__dirname, '..'); +const pingoPath = path.resolve(__dirname, '../../'); +const root = (...args) => path.join(...[rootPath].concat(args)); + +module.exports = {root, pingoPath}; diff --git a/ui/config/paths.js b/ui/config/paths.js new file mode 100644 index 0000000000..18772a64db --- /dev/null +++ b/ui/config/paths.js @@ -0,0 +1,41 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +const helpers = require('./helpers'); + +module.exports = { + entryApp: helpers.root('/src/index.tsx'), + entryHTML: helpers.root('/src/index.ejs'), + entryJSP: helpers.root('/src/index.jsp'), + distSrc: helpers.root('/dist'), + staticPath: helpers.root('/public'), + Components: helpers.root('/src/components'), + Src: helpers.root('/src'), + Utils: helpers.root('/src/utils'), + Models: helpers.root('/src/models'), + Services: helpers.root('/src/services'), + Constants: helpers.root('/src/constants'), + '@hooks': helpers.root('/src/hooks'), + '@src': helpers.root('/src') +}; diff --git a/ui/config/webpack.common.js b/ui/config/webpack.common.js new file mode 100644 index 0000000000..fb2ccfd5dc --- /dev/null +++ b/ui/config/webpack.common.js @@ -0,0 +1,241 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +const path = require('path'); +const paths = require('./paths'); +const HtmlWebpackPlugin = require('html-webpack-plugin'); +const {CleanWebpackPlugin} = require('clean-webpack-plugin'); +const transformerFactory = require('ts-import-plugin'); +const MiniCssExtractPlugin = require('mini-css-extract-plugin'); +const postCssLoader = () => { + return { + loader: 'postcss-loader', + options: { + plugins: [ + require('autoprefixer'), + require('cssnano') + ] + } + }; +}; +const devMode = process.env.NODE_ENV === 'dev'; +module.exports = { + // 入口文件 + entry: paths.entryApp, + output: { + path: paths.distSrc, + publicPath: '/', + filename: '[name].[hash].js', + chunkFilename: '[name].[hash].js' + }, + devtool: 'source-map', + resolve: { + extensions: ['*', '.ts', '.tsx', 'jsx', '.js', 'json'], + alias: { + Components: paths.Components, + Src: paths.Src, + Utils: paths.Utils, + Models: paths.Models, + Services: paths.Services, + Constants: paths.Constants, + '@hooks': paths['@hooks'] + } + }, + module: { + rules: [ + // { + // test: /\.ext$/, + // use: ['cache-loader'], + // include: path.resolve('src') + // }, + { + test: /\.(ts|tsx)$/, + exclude: /node_modules/, + include: paths.srcPath, + use: [ + 'cache-loader', + { + loader: 'ts-loader', + options: { + // disable type checker + transpileOnly: true, + // ts import plugin + getCustomTransformers: () => ({ + before: [ + transformerFactory({style: true}), + transformerFactory({ + style: false, + libraryName: 'lodash', + camel2DashComponentName: false + }) + ] + }), + compilerOptions: { + module: 'es2015' + } + } + }, + { + loader: 'thread-loader', + options: { + workers: 2 + } + } + ] + + }, + { + test: /\.(png|jpg|gif|ttf|eot|svg|woff|woff2)$/, + loader: 'url-loader', + options: { + limit: 1000 + } + }, + { + test: /\.css$/, + use: [ + devMode ? {loader: 'style-loader'} : MiniCssExtractPlugin.loader, + {loader: 'css-loader'}, + postCssLoader(), + { + loader: 'thread-loader', + options: { + workers: 2 + } + } + ] + }, + // For pure CSS (without CSS modules) + { + test: /\.less?$/, + include: /node_modules/, + use: [ + devMode ? {loader: 'style-loader'} : MiniCssExtractPlugin.loader, + { + loader: 'css-loader' + }, + postCssLoader(), + { + loader: 'less-loader', + options: { + // modifyVars: {...themes}, + javascriptEnabled: true + } + }, + { + loader: 'thread-loader', + options: { + workers: 2 + } + } + ] + }, + // For CSS modules + { + test: /\.less?$/, + exclude: /node_modules/, + use: [ + devMode ? {loader: 'style-loader'} : MiniCssExtractPlugin.loader, + { + loader: 'css-loader', + options: { + modules: true + } + }, + postCssLoader(), + { + loader: 'less-loader', + options: { + // modifyVars: {...themes}, + javascriptEnabled: true + } + }, + { + loader: 'thread-loader', + options: { + workers: 2 + } + } + ] + } + ] + }, + optimization: { + splitChunks: { + cacheGroups: { + commons: { + name: 'commons', + test: /[\\/]node_modules[\\/]/, + chunks: 'async', + minSize: 300000, + maxSize: 500000, + minChunks: 2 + }, + lodash: { + name: 'lodash', + chunks: 'all', + test: /lodash/, + minChunks: 2, + priority: 10 + }, + moment: { + name: 'moment', + chunks: 'all', + test: /moment/, + minChunks: 2, + priority: 10 + }, + codeMirror: { + name: 'codemirror', + chunks: 'all', + test: /codemirror/, + minChunks: 2, + priority: 10 + }, + styles: { + name: 'styles', + test: /\.(le|c)ss$/, + chunks: 'all', + enforce: true, + priority: 100, + minSize: 0, + maxSize: 30000000 + } + } + } + }, + // 配置相应的插件 + plugins: [ + new HtmlWebpackPlugin({ + template: './src/index.html', + inject: 'body', + favicon: './src/favicon.ico' + }), + new MiniCssExtractPlugin({ + filename: '[name].[hash].css', + ignoreOrder: true + }), + new CleanWebpackPlugin() + ] +}; diff --git a/ui/config/webpack.dev.js b/ui/config/webpack.dev.js new file mode 100644 index 0000000000..768cb7f3d7 --- /dev/null +++ b/ui/config/webpack.dev.js @@ -0,0 +1,63 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +const merge = require('webpack-merge'); +const baseConfig = require('./webpack.common.js'); +const path = require('path'); + +module.exports = merge(baseConfig, { + // 设置为开发模式 + mode: 'development', + devtool: 'inline-source-map', + optimization: { + minimize: false + }, + // 配置服务端目录和端口 + devServer: { + historyApiFallback: true, + disableHostCheck: true, + stats: 'minimal', + compress: true, + overlay: true, + hot: false, + host: 'localhost', + open: true, + contentBase: path.join(__dirname, 'dist'), + port: 8233, + proxy: { + '/api': { + target: 'http://127.0.0.1:8030', + changeOrigin: true, + secure: false + // pathRewrite: {'^/commonApi': '/commonApi'} + }, + '/rest': { + target: 'http://127.0.0.1:8030', + changeOrigin: true, + secure: false + // pathRewrite: {'^/commonApi': '/commonApi'} + } + } + } +}); diff --git a/ui/config/webpack.prod.js b/ui/config/webpack.prod.js new file mode 100644 index 0000000000..719ed2a8c7 --- /dev/null +++ b/ui/config/webpack.prod.js @@ -0,0 +1,31 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +const merge = require('webpack-merge'); +const baseConfig = require('./webpack.common.js'); + +module.exports = merge(baseConfig, { + // 设置为生产模式 + mode: 'production' +}); diff --git a/ui/package.json b/ui/package.json new file mode 100644 index 0000000000..32e192af71 --- /dev/null +++ b/ui/package.json @@ -0,0 +1,80 @@ +{ + "name": "Doris", + "version": "1.0.0", + "description": "{{description}}", + "main": "index.js", + "scripts": { + "dev": "cross-env NODE_ENV=dev webpack-dev-server --progress --profile --process.env.PRODUCT_MODEL='DEVELOP' ", + "build": "cross-env NODE_ENV=prod webpack" + }, + "lint-staged": { + "src/**/*.js": [ + "eslint --fix", + "git add" + ], + "*.ts": [ + "eslint --fix" + ] + }, + "browserslist": [ + "defaults", + "not ie < 11", + "last 2 versions", + "> 1%", + "iOS 7", + "last 3 iOS versions" + ], + "author": "lpx", + "license": "ISC", + "dependencies": { + "@ant-design/compatible": "^1.0.2", + "@ant-design/icons": "^4.1.0", + "@umijs/hooks": "^1.9.3", + "antd": "^4.5.4", + "axios": "^0.19.2", + "i18next": "^19.7.0", + "i18next-browser-languagedetector": "^6.0.1", + "lodash-decorators": "^6.0.1", + "path-to-regexp": "^2.2.0", + "react": "^16.13.1", + "react-codemirror2": "^7.1.0", + "react-dom": "^16.13.1", + "react-i18next": "^11.7.2", + "react-resizable": "^1.10.1", + "react-router-config": "^5.1.1", + "react-router-dom": "^5.2.0", + "react-syntax-highlighter": "^12.2.1", + "rxjs": "^7.0.0-beta.0", + "sql-formatter": "^2.3.3" + }, + "devDependencies": { + "@typescript-eslint/eslint-plugin": "^2.24.0", + "@typescript-eslint/parser": "^2.24.0", + "autoprefixer": "^9.6.0", + "cache-loader": "^4.1.0", + "clean-webpack-plugin": "^3.0.0", + "codemirror": "^5.54.0", + "cross-env": "^7.0.1", + "css-loader": "^3.5.3", + "cssnano": "^4.1.10", + "eslint": "^6.8.0", + "file-loader": "^1.1.10", + "html-webpack-plugin": "^3.2.0", + "less": "^3.10.3", + "less-loader": "^5.0.0", + "mini-css-extract-plugin": "^0.8.2", + "postcss-loader": "^3.0.0", + "source-map-loader": "^0.2.4", + "speed-measure-webpack-plugin": "^1.3.3", + "style-loader": "^0.23.1", + "thread-loader": "^2.1.3", + "ts-import-plugin": "^1.6.6", + "ts-loader": "^7.0.5", + "typescript": "^3.9.3", + "url-loader": "^1.0.1", + "webpack": "^4.33.0", + "webpack-cli": "^3.3.3", + "webpack-dev-server": "^3.7.1", + "webpack-merge": "^4.2.1" + } +} diff --git a/ui/postcss.config.js b/ui/postcss.config.js new file mode 100644 index 0000000000..689c25e08f --- /dev/null +++ b/ui/postcss.config.js @@ -0,0 +1,18 @@ +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +module.exports = { + plugins: { + // 兼容浏览器,添加前缀 + 'autoprefixer': { + overrideBrowserslist: [ + 'Chrome > 31', + 'ff > 31', + 'ie >= 8' + ], + grid: true + } + } +}; \ No newline at end of file diff --git a/ui/public/fonts/Read Me.txt b/ui/public/fonts/Read Me.txt new file mode 100644 index 0000000000..8491652f88 --- /dev/null +++ b/ui/public/fonts/Read Me.txt @@ -0,0 +1,7 @@ +Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures. + +To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts + +You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects. + +You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection. diff --git a/ui/public/fonts/fonts/icomoon.eot b/ui/public/fonts/fonts/icomoon.eot new file mode 100644 index 0000000000..a209c8f472 Binary files /dev/null and b/ui/public/fonts/fonts/icomoon.eot differ diff --git a/ui/public/fonts/fonts/icomoon.svg b/ui/public/fonts/fonts/icomoon.svg new file mode 100644 index 0000000000..81f85d43ca --- /dev/null +++ b/ui/public/fonts/fonts/icomoon.svg @@ -0,0 +1,25 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ui/public/fonts/fonts/icomoon.ttf b/ui/public/fonts/fonts/icomoon.ttf new file mode 100644 index 0000000000..475b47363a Binary files /dev/null and b/ui/public/fonts/fonts/icomoon.ttf differ diff --git a/ui/public/fonts/fonts/icomoon.woff b/ui/public/fonts/fonts/icomoon.woff new file mode 100644 index 0000000000..a1024c9cd3 Binary files /dev/null and b/ui/public/fonts/fonts/icomoon.woff differ diff --git a/ui/public/fonts/iconfont/iconfont.eot b/ui/public/fonts/iconfont/iconfont.eot new file mode 100644 index 0000000000..f7a7e2d517 Binary files /dev/null and b/ui/public/fonts/iconfont/iconfont.eot differ diff --git a/ui/public/fonts/iconfont/iconfont.svg b/ui/public/fonts/iconfont/iconfont.svg new file mode 100644 index 0000000000..3824505439 --- /dev/null +++ b/ui/public/fonts/iconfont/iconfont.svg @@ -0,0 +1,927 @@ + + + + + +Created by iconfont + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui/public/fonts/iconfont/iconfont.ttf b/ui/public/fonts/iconfont/iconfont.ttf new file mode 100644 index 0000000000..2abf03542c Binary files /dev/null and b/ui/public/fonts/iconfont/iconfont.ttf differ diff --git a/ui/public/fonts/iconfont/iconfont.woff b/ui/public/fonts/iconfont/iconfont.woff new file mode 100644 index 0000000000..70ca476f57 Binary files /dev/null and b/ui/public/fonts/iconfont/iconfont.woff differ diff --git a/ui/public/fonts/selection.json b/ui/public/fonts/selection.json new file mode 100644 index 0000000000..625700fed2 --- /dev/null +++ b/ui/public/fonts/selection.json @@ -0,0 +1 @@ +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M941.248 850.752l-210.752-210.752-90.496 90.496 210.752 210.752-82.752 82.752h256v-256zM82.752 173.248l210.752 210.752 90.496-90.496-210.752-210.752 82.752-82.752h-256v256zM850.752 82.752l-210.752 210.752 90.496 90.496 210.752-210.752 82.752 82.752v-256h-256zM384 730.496l-90.496-90.496-210.752 210.752-82.752-82.752v256h256l-82.752-82.752z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["fullscreen-alt","expand"],"grid":16},"attrs":[{}],"properties":{"order":1,"id":1,"name":"fullscreen","prevSize":32,"code":59648},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M896 640h-256v256l82.752-82.752 210.752 210.752 90.496-90.496-210.752-210.752zM813.248 301.248l210.752-210.752-90.496-90.496-210.752 210.752-82.752-82.752v256h256zM90.496 0l-90.496 90.496 210.752 210.752-82.752 82.752h256v-256l-82.752 82.752zM210.752 722.752l-210.752 210.752 90.496 90.496 210.752-210.752 82.752 82.752v-256h-256z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["fullscreen-exit-alt","collapse","shrink","reduce"],"grid":16},"attrs":[{}],"properties":{"order":2,"id":0,"name":"fullscreen-exit","prevSize":32,"code":59649},"setIdx":0,"setId":2,"iconIdx":1},{"icon":{"paths":["M64 0v1024h896v-1024h-896zM896 960h-768v-896h768v896zM332.8 499.2l44.8-44.8-57.6-57.6h288v-64h-288l57.6-57.6-44.8-44.8-134.4 134.4 44.8 44.8zM409.6 588.8v64h288l-57.6 57.6 44.8 44.8 96-89.6 44.8-44.8-140.8-134.4-44.8 44.8 57.6 57.6z"],"isMulticolor":false,"isMulticolor2":false,"tags":["dealrecord"],"defaultCode":59525,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":112,"order":10,"ligatures":"","prevSize":32,"code":59525,"name":"dealrecord"},"setIdx":2,"setId":0,"iconIdx":111},{"icon":{"paths":["M938.667 256c-85.333-153.6-247.467-256-426.667-256-238.933 0-452.267 179.2-486.4 418.133l85.333 8.533c25.6-196.267 204.8-341.333 401.067-341.333 162.133 0 298.667 93.867 366.933 230.4l-110.933 110.933h256v-256l-85.333 85.333zM512 938.667c-162.133 0-298.667-93.867-366.933-230.4l110.933-110.933h-256v256l85.333-85.333c85.333 153.6 247.467 256 426.667 256 238.933 0 452.267-179.2 486.4-418.133l-85.333-8.533c-25.6 187.733-204.8 341.333-401.067 341.333zM298.667 597.333v85.333h170.667v170.667h85.333v-170.667h170.667v-85.333h-170.667v-85.333h170.667v-85.333h-128l102.4-110.933-59.733-59.733-128 128-128-128-59.733 59.733 102.4 110.933h-128v85.333h170.667v85.333z"],"isMulticolor":false,"isMulticolor2":false,"tags":["auto-renew"],"defaultCode":59470,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":184,"order":8,"ligatures":"","prevSize":32,"code":59470,"name":"auto-renew"},"setIdx":2,"setId":0,"iconIdx":183},{"icon":{"paths":["M73.143 0v1024h877.714v-1024h-877.714zM877.714 950.857h-731.429v-877.714h731.429v877.714zM402.286 256h365.714v73.143h-365.714zM256 256h73.143v73.143h-73.143zM402.286 475.429h365.714v73.143h-365.714zM256 475.429h73.143v73.143h-73.143zM402.286 694.857h365.714v73.143h-365.714zM256 694.857h73.143v73.143h-73.143z"],"isMulticolor":false,"isMulticolor2":false,"tags":["overview-instance"],"defaultCode":59414,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":323,"order":7,"ligatures":"","prevSize":32,"code":59414,"name":"overview-instance"},"setIdx":2,"setId":0,"iconIdx":322},{"icon":{"paths":["M652.8 217.6l-25.6 57.6c140.8 51.2 236.8 179.2 236.8 332.8 0 192-160 352-352 352s-352-160-352-352c0-147.2 96-281.6 236.8-332.8l-25.6-57.6c-166.4 57.6-275.2 211.2-275.2 390.4 0 230.4 185.6 416 416 416s416-185.6 416-416c0-179.2-108.8-332.8-275.2-390.4zM480 0h64v640h-64z"],"isMulticolor":false,"isMulticolor2":false,"tags":["quit"],"defaultCode":59189,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":338,"order":2,"ligatures":"","prevSize":32,"code":59189,"name":"exit"},"setIdx":2,"setId":0,"iconIdx":337},{"icon":{"paths":["M60.235 0v1024l150.588-150.588 150.588 150.588 150.588-150.588 150.588 150.588 150.588-150.588 150.588 150.588v-1024h-903.529zM903.529 879.435l-90.353-90.353-150.588 150.588-150.588-150.588-150.588 150.588-150.588-150.588-90.353 90.353v-819.2h783.059v819.2zM331.294 331.294v60.235h150.588v90.353h-150.588v60.235h150.588v150.588h60.235v-150.588h150.588v-60.235h-150.588v-90.353h150.588v-60.235h-63.247l99.388-99.388-42.165-42.165-141.553 141.553h-66.259l-141.553-141.553-42.165 42.165 99.388 99.388z"],"isMulticolor":false,"isMulticolor2":false,"tags":["zhangdanguanli"],"defaultCode":59176,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":382,"order":3,"ligatures":"","prevSize":32,"code":59176,"name":"zhangdanguanli"},"setIdx":2,"setId":0,"iconIdx":381},{"icon":{"paths":["M284.444 341.333c0 0 0 0 0 0 0 62.838 50.94 113.778 113.778 113.778s113.778-50.94 113.778-113.778c0 0 0 0 0 0s0 0 0 0c0-62.838-50.94-113.778-113.778-113.778s-113.778 50.94-113.778 113.778c0 0 0 0 0 0zM398.222 568.889c-96.711 0-170.667 73.956-170.667 170.667h341.333c0-96.711-73.956-170.667-170.667-170.667zM512 0c-284.444 0-512 227.556-512 512s227.556 512 512 512 512-227.556 512-512-227.556-512-512-512zM625.778 796.444h-455.111v-56.889c0-125.156 102.4-227.556 227.556-227.556-96.711 0-170.667-73.956-170.667-170.667s73.956-170.667 170.667-170.667 170.667 73.956 170.667 170.667-73.956 170.667-170.667 170.667c125.156 0 227.556 102.4 227.556 227.556v56.889zM910.222 739.556h-227.556v-56.889h227.556v56.889zM910.222 568.889h-284.444v-56.889h284.444v56.889zM625.778 398.222v-56.889h284.444v56.889h-284.444z"],"isMulticolor":false,"isMulticolor2":false,"tags":["verify"],"defaultCode":59308,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":459,"order":16,"ligatures":"","prevSize":32,"code":59308,"name":"verify"},"setIdx":2,"setId":0,"iconIdx":458},{"icon":{"paths":["M739.556 341.333h-455.111l227.556 170.667zM512 580.267l-17.067-11.378-267.378-199.111v364.089h568.889v-364.089l-267.378 199.111zM512 0c-284.444 0-512 227.556-512 512s227.556 512 512 512 512-227.556 512-512-227.556-512-512-512zM853.333 790.756h-682.667v-506.311h682.667v506.311z"],"isMulticolor":false,"isMulticolor2":false,"tags":["verify-email"],"defaultCode":59307,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":464,"order":12,"ligatures":"","prevSize":32,"code":59307,"name":"verify-email"},"setIdx":2,"setId":0,"iconIdx":463},{"icon":{"paths":["M341.333 227.556h341.333v398.222h-341.333zM682.667 682.667h-341.333v113.778h341.333v-113.778zM540.444 768h-56.889v-56.889h56.889v56.889zM512 0c-284.444 0-512 227.556-512 512s227.556 512 512 512 512-227.556 512-512-227.556-512-512-512zM739.556 853.333h-455.111v-682.667h455.111v682.667z"],"isMulticolor":false,"isMulticolor2":false,"tags":["verify-mobile"],"defaultCode":59309,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":465,"order":15,"ligatures":"","prevSize":32,"code":59309,"name":"verify-mobile"},"setIdx":2,"setId":0,"iconIdx":464},{"icon":{"paths":["M704 384c0-108.8-83.2-192-192-192s-192 83.2-192 192v320h384v-320zM512 256c70.4 0 128 57.6 128 128h-256c0-70.4 57.6-128 128-128zM640 640h-256v-192h256v192zM0 64v768h1024v-768h-1024zM960 768h-896v-640h896v640zM128 896h768v64h-768z"],"isMulticolor":false,"isMulticolor2":false,"tags":["iam-user"],"defaultCode":59264,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":488,"order":11,"ligatures":"","prevSize":32,"code":59264,"name":"iam-user"},"setIdx":2,"setId":0,"iconIdx":487},{"icon":{"paths":["M64 0v448c0 384 448 576 448 576s448-192 448-576v-448h-896zM896 448c0 288-294.4 460.8-384 505.6-89.6-44.8-384-217.6-384-505.6v-384h768v384zM704 384c0-108.8-83.2-192-192-192s-192 83.2-192 192v320h384v-320zM512 256c70.4 0 128 57.6 128 128h-256c0-70.4 57.6-128 128-128zM640 640h-256v-192h256v192z"],"isMulticolor":false,"isMulticolor2":false,"tags":["authinfo"],"defaultCode":59241,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":496,"order":14,"ligatures":"","prevSize":32,"code":59241,"name":"authinfo"},"setIdx":2,"setId":0,"iconIdx":495},{"icon":{"paths":["M710.4 416c38.4-38.4 57.6-96 57.6-160 0-44.8-12.8-83.2-25.6-115.2-12.8-19.2-19.2-38.4-38.4-51.2-44.8-57.6-115.2-89.6-192-89.6-140.8 0-256 115.2-256 256s115.2 256 256 256c57.6 0 108.8-19.2 147.2-51.2 19.2-12.8 38.4-25.6 51.2-44.8zM620.8 416c-32 19.2-70.4 32-108.8 32-108.8 0-192-89.6-192-192 0-108.8 83.2-192 192-192 51.2 0 102.4 19.2 140.8 57.6 12.8 12.8 25.6 32 38.4 51.2 6.4 25.6 12.8 51.2 12.8 83.2 0 44.8-12.8 83.2-38.4 115.2-12.8 19.2-32 32-44.8 44.8zM774.4 582.4c-25.6-19.2-57.6-32-89.6-44.8-51.2-12.8-108.8-25.6-172.8-25.6-249.6 0-448 172.8-448 384v128h896v-128c0-128-76.8-243.2-185.6-313.6zM883.2 960h-742.4v-64c0-179.2 166.4-320 371.2-320 32 0 57.6 0 89.6 6.4s57.6 12.8 83.2 25.6c121.6 51.2 198.4 160 198.4 281.6v70.4z"],"isMulticolor":false,"isMulticolor2":false,"tags":["myaccount"],"defaultCode":59270,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":517,"order":13,"ligatures":"","prevSize":32,"code":59270,"name":"myaccount"},"setIdx":2,"setId":0,"iconIdx":516},{"icon":{"paths":["M949.497 453.228c25.735-25.735 12.867-70.771-25.735-77.205l-238.049-32.169c-12.867-6.434-25.735-12.867-32.169-25.735l-102.94-212.314c-12.867-32.169-64.338-32.169-77.205 0l-102.94 212.314c-6.434 12.867-19.301 25.735-32.169 25.735l-238.049 32.169c-38.603 6.434-51.47 45.036-25.735 70.771l167.278 167.278c12.867 12.868 12.867 25.735 12.867 38.602l-38.603 238.049c-6.434 32.169 32.169 64.338 64.338 45.036l212.314-109.374c12.867-6.434 25.735-6.434 38.603 0l212.314 109.374c32.169 19.301 70.771-6.434 64.338-45.036l-38.603-231.615c0-12.867 0-25.735 12.867-38.602l167.278-167.278z"],"isMulticolor":false,"isMulticolor2":false,"tags":["star-solid"],"defaultCode":59076,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":629,"order":17,"ligatures":"","prevSize":32,"code":59076,"name":"star-solid"},"setIdx":2,"setId":0,"iconIdx":628},{"icon":{"paths":["M948.002 455.681c25.525-25.525 12.763-70.195-25.525-76.576l-236.111-31.907c-12.763-6.381-25.525-12.763-31.907-25.525l-102.102-210.585c-12.763-31.907-63.814-31.907-76.576 0l-102.102 210.585c-6.381 12.763-19.144 25.525-31.907 25.525l-236.111 31.907c-38.288 6.381-51.051 44.67-25.525 70.195l165.916 165.916c12.763 12.763 12.763 25.525 12.763 38.288l-38.288 236.111c-6.381 31.907 31.907 63.814 63.814 44.67l210.585-108.483c12.763-6.381 25.525-6.381 38.288 0l210.585 108.483c31.907 19.144 70.195-6.381 63.814-44.67l-38.288-229.729c0-12.763 0-25.525 12.763-38.288l165.915-165.916zM737.417 576.927c-31.907 31.907-31.907 63.814-31.907 82.958v6.381l25.525 165.916c0 12.763-12.763 19.144-19.144 12.763l-153.153-76.576c-12.763-6.381-31.907-12.763-51.051-12.763s-31.907 6.381-44.67 12.763l-153.153 76.576c-12.763 6.381-25.525-6.381-19.144-12.763l31.907-172.297v-6.381c0-19.144 0-51.051-31.907-82.958l-114.865-114.865c-6.381-6.381 0-19.144 12.763-25.525l159.534-19.144c38.288 0 70.195-31.907 82.958-63.814l70.195-146.771c6.381-12.763 19.144-12.763 25.525 0l70.195 146.771c12.763 38.288 51.051 51.051 70.195 57.432 44.67 12.763 178.678 25.525 178.678 25.525 12.763 0 19.144 12.763 6.381 25.525l-114.865 121.246z"],"isMulticolor":false,"isMulticolor2":false,"tags":["star-hollow"],"defaultCode":59077,"grid":0,"attrs":[]},"attrs":[],"properties":{"id":630,"order":18,"ligatures":"","prevSize":32,"code":59077,"name":"star-hollow"},"setIdx":2,"setId":0,"iconIdx":629}],"height":1024,"metadata":{"name":"icomoon"},"preferences":{"showGlyphs":true,"showCodes":true,"showQuickUse":false,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"icon-","metadata":{"fontFamily":"icomoon"},"metrics":{"emSize":1024,"baseline":12.5},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"name":"icomoon","classSelector":".icon"},"historySize":50,"gridSize":16}} \ No newline at end of file diff --git a/ui/public/fonts/style.min.css b/ui/public/fonts/style.min.css new file mode 100644 index 0000000000..d9f53195a8 --- /dev/null +++ b/ui/public/fonts/style.min.css @@ -0,0 +1,72 @@ +@font-face { + font-family: 'icomoon'; + src: url('fonts/icomoon.eot?9lzn0r'); + src: url('fonts/icomoon.eot?9lzn0r#iefix') format('embedded-opentype'), + url('fonts/icomoon.ttf?9lzn0r') format('truetype'), + url('fonts/icomoon.woff?9lzn0r') format('woff'), + url('fonts/icomoon.svg?9lzn0r#icomoon') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="icon-"], [class*=" icon-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'icomoon' !important; + speak: none; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.icon-fullscreen:before { + content: "\e900"; +} +.icon-fullscreen-exit:before { + content: "\e901"; +} +.icon-dealrecord:before { + content: "\e885"; +} +.icon-auto-renew:before { + content: "\e84e"; +} +.icon-overview-instance:before { + content: "\e816"; +} +.icon-exit:before { + content: "\e735"; +} +.icon-zhangdanguanli:before { + content: "\e728"; +} +.icon-verify:before { + content: "\e7ac"; +} +.icon-verify-email:before { + content: "\e7ab"; +} +.icon-verify-mobile:before { + content: "\e7ad"; +} +.icon-iam-user:before { + content: "\e780"; +} +.icon-authinfo:before { + content: "\e769"; +} +.icon-myaccount:before { + content: "\e786"; +} +.icon-star-solid:before { + content: "\e6c4"; +} +.icon-star-hollow:before { + content: "\e6c5"; +} diff --git a/ui/public/img/background.png b/ui/public/img/background.png new file mode 100644 index 0000000000..5ab06ca68b Binary files /dev/null and b/ui/public/img/background.png differ diff --git a/ui/public/img/logo.png b/ui/public/img/logo.png new file mode 100644 index 0000000000..b5660e641d Binary files /dev/null and b/ui/public/img/logo.png differ diff --git a/ui/public/locales/en-us.json b/ui/public/locales/en-us.json new file mode 100644 index 0000000000..8f12e77beb --- /dev/null +++ b/ui/public/locales/en-us.json @@ -0,0 +1,48 @@ +{ + "username": "Username", + "password": "Password", + "signOut": "Sign out", + + "exitSuccessfully":"Exit successfully", + "editor": "Editor", + "format": "Format", + "clear": "Clear the editor", + "execute": "Execute", + "tableStructure": "Table Schema", + "dataPreview": "Data Preview", + "queryForm": "Query", + "runSuccessfully": "Run successfully", + "results": "Results", + "selectTheFile": "Select File", + "loadConfig": "Configurations", + "previous": "Previous", + "next": "Next", + "dataImport": "Data Import", + "table": "Table", + "delimiter": "Column Separator", + "importResults": "Import Results", + "errMsg":"Error request, please try again later", + "display10":"Display up to 10 lines", + "refresh":"Refresh", + "startingTime":"Starting Time", + "endTime":"End Time", + "currentDatabase":"Current Database", + "executionFailed":"Execution failed", + "uploadWarning":"Please upload files", + "upload": "Upload", + "delimiterWarning":"Please select a separator", + "uploadedFiles":"Uploaded Files", + "filePreview":"File Preview", + "database":"Database", + "notice":"Notice", + "login":"Login", + "loginExpirtMsg":"The login information has expired. Click OK to jump to the login page.", + "nextStep":"Next", + "previousStep":"Previous", + "pleaseEnter":"Please enter", + "cancelButton":"Cancel", + "loadButton":"Import", + "successfulOperation":"The operation was successful", + "tips":"Tips", + "fileSizeWarning": "File size cannot exceed 100M" +} diff --git a/ui/public/locales/zh-cn.json b/ui/public/locales/zh-cn.json new file mode 100644 index 0000000000..0b4faaf99f --- /dev/null +++ b/ui/public/locales/zh-cn.json @@ -0,0 +1,48 @@ +{ + "username": "用户名", + "password": "密码", + "exitSuccessfully": "退出成功", + + "editor": "编辑器", + "format": "格式化", + "clear": "清空编辑器", + "execute": "执行", + "tableStructure": "表结构", + "dataPreview": "数据预览", + "queryForm": "查询", + "runSuccessfully": "运行成功", + "results": "执行结果", + "selectTheFile": "文件选择", + "loadConfig": "导入配置", + "previous": "上一步", + "next": "下一步", + "cancel": "取消", + "dataImport": "数据导入", + "table": "表", + "delimiter": "列分隔符", + "importResults": "导入结果", + "errMsg": "请求出错,请稍后重试", + "display10": "最多显示10行", + "refresh": "刷新", + "startingTime": "开始时间", + "endTime":"结束时间", + "currentDatabase": "当前数据库", + "executionFailed": "上传失败", + "uploadWarning": "请上传文件", + "upload": "上传", + "delimiterWarning": "请选择分隔符", + "uploadedFiles": "已上传文件列表", + "filePreview": "文件预览", + "database": "数据库", + "notice": "注意", + "login": "登录", + "loginExpirtMsg": "登录信息已过期。点击确定跳转到登陆页面。", + "nextStep": "下一步", + "previousStep": "上一步", + "pleaseEnter": "请输入", + "cancelButton": "取消", + "loadButton": "导入", + "successfulOperation": "操作成功", + "tips": "提示", + "fileSizeWarning": "文件大小不能超过100m" +} diff --git a/ui/src/App.tsx b/ui/src/App.tsx new file mode 100644 index 0000000000..60f7f55ae5 --- /dev/null +++ b/ui/src/App.tsx @@ -0,0 +1,43 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +import React from 'react'; +import {BrowserRouter as Router, Switch, Redirect} from 'react-router-dom'; +// import {renderRoutes} from 'react-router-config'; +import routes from './router'; +import renderRoutes from './router/renderRouter'; +import 'antd/dist/antd.css'; + +function App() { + return ( + + + {renderRoutes(routes.routes)} + + + ); +}; + +export default App; + diff --git a/ui/src/components/codemirror-with-fullscreen/codemirror-with-fullscreen.less b/ui/src/components/codemirror-with-fullscreen/codemirror-with-fullscreen.less new file mode 100644 index 0000000000..99f0c64d9f --- /dev/null +++ b/ui/src/components/codemirror-with-fullscreen/codemirror-with-fullscreen.less @@ -0,0 +1,63 @@ +/* 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 '~antd/lib/style/themes/default.less'; + +.codemirror-with-fullscreen { + position: relative; + height: 100%; + .fullscreen { + position: absolute; + top: 20px; + right: 20px; + color: white; + z-index: 10; + } + &-divider { + margin: 20px 0; + height: 1px; + cursor: row-resize; + border-bottom: 1px solid #DCDCDC; + } + &-operator { + margin-top: 10px; + } +} + +.fullscreen { + cursor: pointer; + &:hover { + color: @primary-color; + } +} +.fullscreen-exit { + z-index: 10; + position: fixed; + right: 20px; + top: 20px; + color: white; + cursor: pointer; + &:hover { + color: @primary-color; + } +} +.codemirror { + height: 100%; + > div { + height: 100%; + } +} \ No newline at end of file diff --git a/ui/src/components/codemirror-with-fullscreen/codemirror-with-fullscreen.tsx b/ui/src/components/codemirror-with-fullscreen/codemirror-with-fullscreen.tsx new file mode 100644 index 0000000000..d99c395917 --- /dev/null +++ b/ui/src/components/codemirror-with-fullscreen/codemirror-with-fullscreen.tsx @@ -0,0 +1,62 @@ +/** + * 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 React, {useState} from 'react'; +import {IconFont} from 'Components/iconfont'; +import {Controlled as CodeMirror} from 'react-codemirror2'; +import styles from './codemirror-with-fullscreen.less'; +require('codemirror/lib/codemirror.css'); +require('codemirror/theme/material.css'); +require('./doris.css'); +require('codemirror/addon/hint/show-hint.css'); +require('codemirror/addon/display/fullscreen.css'); +require('codemirror/mode/sql/sql'); +require('codemirror/addon/hint/show-hint'); +require('codemirror/addon/hint/sql-hint'); +require('codemirror/addon/display/fullscreen'); +export function CodeMirrorWithFullscreen(props: any) { + const [options, setOptions] = useState(props.options); + const [iconType, setIconType] = useState('fullscreen'); + return ( +
+ { + setOptions({ + ...options, + fullScreen: !options.fullScreen, + }); + const iconType = options.fullScreen + ? 'fullscreen' + : 'fullscreen-exit'; + setIconType(iconType); + }} + type={iconType} + className={styles[iconType]} + /> + +
+ ); +} + diff --git a/ui/src/components/codemirror-with-fullscreen/doris.css b/ui/src/components/codemirror-with-fullscreen/doris.css new file mode 100644 index 0000000000..3e56e52caf --- /dev/null +++ b/ui/src/components/codemirror-with-fullscreen/doris.css @@ -0,0 +1,65 @@ +/* neo theme for codemirror */ + +/* Color scheme */ + +.cm-s-neo.CodeMirror { + background-color: #fff; + color: #2e383c; + line-height: 1.4375; + font-family: apercu-mono-regular, Menlo, Monaco, Consolas, "Courier New", monospace !important; + } +.cm-s-neo .cm-comment { + color: #75787b; +} +.cm-s-neo .cm-keyword, +.cm-s-neo .cm-property { + color: #02a0f9; +} +.cm-s-neo .cm-atom, +.cm-s-neo .cm-number { + color: #35ae91; +} +.cm-s-neo .cm-node, +.cm-s-neo .cm-tag { + color: #9c3328; +} +.cm-s-neo .cm-string { + color: #b35e14; +} +.cm-s-neo .cm-variable, +.cm-s-neo .cm-qualifier { + color: #75438a; +} + + + /* Editor styling */ + +.cm-s-neo pre { + padding: 0; +} + +.cm-s-neo .CodeMirror-gutters { + border: none; + border-right: 10px solid transparent; + background-color: transparent; +} + +.cm-s-neo .CodeMirror-linenumber { + padding: 0; + color: #e0e2e5; +} + +.cm-s-neo .CodeMirror-guttermarker { + color: #1d75b3; +} +.cm-s-neo .CodeMirror-guttermarker-subtle { + color: #e0e2e5; +} + +.cm-s-neo .CodeMirror-cursor { + width: auto; + border: 0; + background: rgba(155, 157, 162, 0.37); + z-index: 1; +} + \ No newline at end of file diff --git a/ui/src/components/flatbtn/flat-btn-group.less b/ui/src/components/flatbtn/flat-btn-group.less new file mode 100644 index 0000000000..764e3a627c --- /dev/null +++ b/ui/src/components/flatbtn/flat-btn-group.less @@ -0,0 +1,52 @@ +/* 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 '~antd/lib/style/themes/default.less'; +.flat-btn-group { + display: inline-block; + .anticon-down { + margin-left: 5px; + vertical-align: middle; + transition: 0.5s; + } + &:hover { + .anticon-down { + transform: rotate(180deg); + transition: 0.5s; + } + } + .flat-btn-more { + color: @primary-color; + font-size: 14px; + cursor: pointer; + } +} + +.flat-menu { + &.ant-dropdown-menu { + min-width: 100px; + .ant-dropdown-menu-item > a, .ant-dropdown-menu-submenu-title > a { + color: @primary-color; + } + .ant-dropdown-menu-item-disabled { + // background-color: #ddd; + .flat-btn--disabled { + color: rgba(0, 0, 0, 0.25); + } + } + } +} diff --git a/ui/src/components/flatbtn/flat-btn-group.tsx b/ui/src/components/flatbtn/flat-btn-group.tsx new file mode 100644 index 0000000000..72b5670700 --- /dev/null +++ b/ui/src/components/flatbtn/flat-btn-group.tsx @@ -0,0 +1,89 @@ +/** + * 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 React, { FunctionComponent, useRef } from 'react'; +import { DownOutlined } from '@ant-design/icons'; +import { Menu, Dropdown, Divider } from 'antd'; +import './flat-btn-group.less'; + +interface FlatItemProps { + children?: React.ReactNode[]; + showNum?: number; +} + +const FlatBtnGroup: FunctionComponent = ({showNum = 3, children = []}) => { + let childList: React.ReactNode[] = []; + if (showNum <= 1) { + showNum = 3; + } + if (!Array.isArray(children)) { + childList.push(children); + } else { + childList = children; + } + const validChildren = childList.filter(child => !!child).flat(Infinity); + const newList = validChildren.slice(0, showNum - 1); + const dropList = validChildren.slice(showNum - 1); + + const menu = ( + + {dropList.map((item: any, index) => { + return ( + + {item} + + ); + })} + + ) + + const wrap = useRef(null); + + return ( +
+ {newList.map((btn, key) => ( + + {btn} + {(key !== showNum - 1 && !(key < showNum && key === newList.length - 1)) || + dropList.length ? : <>} + + ))} + {dropList.length ? ( + { + const dom = wrap.current; + if (dom) { + return dom; + } + return document.body; + }}> + + 更多 + + + + ) : <>} +
+ ); +}; + +export default FlatBtnGroup; + diff --git a/ui/src/components/flatbtn/flat-btn.tsx b/ui/src/components/flatbtn/flat-btn.tsx new file mode 100644 index 0000000000..ff16ee6f43 --- /dev/null +++ b/ui/src/components/flatbtn/flat-btn.tsx @@ -0,0 +1,68 @@ +/** + * 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 React, {HTMLAttributes} from 'react'; +import classNames from 'classnames'; +import {Link} from 'react-router-dom'; +import './style.less'; + +interface FlatBtnProps extends HTMLAttributes { + to?: string; + type?: '' | 'danger' | 'warn'; + disabled?: boolean; + children?: string | JSX.Element; + className?: string; + default?: string; + key?: string | number; + href?: string; + [attr: string]: any; +} + +const FlatBtn = (props: FlatBtnProps) => { + if (props.to) { + return ( + + {props.children} + + ); + } + return ( + + {props.children} + + ); +}; + +export default FlatBtn; + diff --git a/ui/src/components/flatbtn/index.tsx b/ui/src/components/flatbtn/index.tsx new file mode 100644 index 0000000000..08b7df3423 --- /dev/null +++ b/ui/src/components/flatbtn/index.tsx @@ -0,0 +1,25 @@ +/** + * 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 FlatBtn from './flat-btn'; +import FlatBtnGroup from './flat-btn-group'; + +export { FlatBtn }; +export { FlatBtnGroup }; + diff --git a/ui/src/components/flatbtn/style.less b/ui/src/components/flatbtn/style.less new file mode 100644 index 0000000000..5c894ecd34 --- /dev/null +++ b/ui/src/components/flatbtn/style.less @@ -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. */ + +.flat-btn { + display: inline-block; + text-decoration: none !important; + + border: none; + background: none; + text-shadow: none; + box-shadow: none; + cursor: pointer; + outline: none; + padding-left: 7px; + padding-right: 7px; + border-right: 1px dashed #c4c4c4; + // &.flat-btn--disabled { + // cursor: inherit !important; + // color: gray !important; + // } + &.flat-btn--default { + color: #414D5F; + cursor: not-allowed; + } + &:first-child { + padding-left: 0; + } + &:last-child { + border-right: 0; + } + &.btn-danger { + color: red; + &:hover { + background-color: inherit !important; + } + } + &.btn-warning { + color: #f0b64b; + } + .glyphicon-triangle-bottom { + color: #666; + margin-left: 5px; + font-size: 12px; + } +} diff --git a/ui/src/components/iconfont/index.tsx b/ui/src/components/iconfont/index.tsx new file mode 100644 index 0000000000..5b02bd1769 --- /dev/null +++ b/ui/src/components/iconfont/index.tsx @@ -0,0 +1,45 @@ +/** + * 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 React from 'react'; +import { HTMLAttributes } from 'react'; + +interface IconProps extends HTMLAttributes { + type: string; +} + +export const IconFont = (props: IconProps) => { + const { className = '', type } = props; + return ; +}; + +interface SvgIconProps { + className?: string; + name: string; +} + +export const SvgIcon = (props: SvgIconProps) => { + const { className = '', name } = props; + return ( + + ); +}; + diff --git a/ui/src/components/loadingwrapper/loadingwrapper.tsx b/ui/src/components/loadingwrapper/loadingwrapper.tsx new file mode 100644 index 0000000000..16ea14547f --- /dev/null +++ b/ui/src/components/loadingwrapper/loadingwrapper.tsx @@ -0,0 +1,44 @@ +/** + * 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 React, { PropsWithChildren } from 'react'; +import { SpinProps } from 'antd/lib/spin'; +import { Spin } from 'antd'; +import { TABLE_DELAY } from 'Constants'; + +type SpinWithoutSpinningProp = Omit; + +interface LoadingWrapperProps extends SpinWithoutSpinningProp { + loading?: boolean; +} + +export function LoadingWrapper(props: PropsWithChildren) { + let loading = false; + if (props.loading) { + loading = true; + } + const spinProps = { ...props }; + delete spinProps.loading; + return ( + + {props.children} + + ); +} + diff --git a/ui/src/components/table/index.less b/ui/src/components/table/index.less new file mode 100644 index 0000000000..a417c7d0a2 --- /dev/null +++ b/ui/src/components/table/index.less @@ -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. */ + +:global(.systemTable){ + position: relative; + display: block; + :global(.search){ + color: #1890ff; + position: absolute; + top: 12px; + left: 98%; + z-index: 2; + } + :global(.searchSystem){ + top: -33px; + right: 91px; + z-index: 2; + color: #1890ff; + position: absolute; + } +} diff --git a/ui/src/components/table/index.tsx b/ui/src/components/table/index.tsx new file mode 100644 index 0000000000..cdccdb435e --- /dev/null +++ b/ui/src/components/table/index.tsx @@ -0,0 +1,72 @@ +/** + * 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 React, {useState,useEffect} from 'react'; +import {Table, Popover, Input} from 'antd'; +import {FilterFilled} from '@ant-design/icons'; +import {getColumns, filterTableData} from './table.utils.tsx'; +import './index.less'; + +export default function SortFilterTable(props: any) { + const {isFilter=false, isSort=false, allTableData, isInner, isSystem=false} = props; + const [tableData, setTableData] = useState([]); + const [localColumns, setColumns] = useState([]); + // function onChange(pagination, filters, sorter, extra) { + // console.log('params', pagination, filters, sorter, extra); + // } + function changeTableData(e){ + const localData = filterTableData(allTableData.rows,e.target.value); + setTableData(localData); + } + const content = ( + changeTableData(e)}/> + ); + useEffect(() => { + if(allTableData.rows&&allTableData.column_names){ + setColumns(getColumns(allTableData.column_names, isSort, isInner, allTableData.href_columns||allTableData.href_column)); + setTableData(allTableData.rows); + } + }, [allTableData]); + + return( + + {isFilter? + + :''} + `${range[0]}-${range[1]} of ${total} items`, + showSizeChanger:true, + showQuickJumper:true, + hideOnSinglePage:true, + }} + /> + + ); +} + + diff --git a/ui/src/components/table/table.utils.tsx b/ui/src/components/table/table.utils.tsx new file mode 100644 index 0000000000..e1e6a0e33b --- /dev/null +++ b/ui/src/components/table/table.utils.tsx @@ -0,0 +1,71 @@ +/** + * 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 React from 'react'; +import {Link} from 'react-router-dom'; +function sortItems(a: any,b: any, item: string) { + if(typeof a[item] === 'number' && typeof b[item] === 'number'){ + return a[item]-b[item]; + } + return a[item].localeCompare(b[item]); +} +function getLinkItem(text, record, index, isInner, item, hrefColumn){ + if (isInner && hrefColumn && (hrefColumn.indexOf(item) !== -1)&&record.__hrefPaths) { + if (record.__hrefPaths[hrefColumn.indexOf(item)].includes('http')) { + return {text}; + } + return {text}; + } + return text === '\\N' ? '-' : text; +} +export function getColumns(params: string[], isSort: boolean, isInner, hrefColumn) { + if(!params||params.length === 0){return [];} + let arr = params.map(item=> { + if (isSort) { + return { + title: item, + dataIndex: item, + className: 'pr-25', + sorter: (a,b)=>sortItems(a, b, item), + render:(text, record, index)=>getLinkItem(text,record, index, isInner, item, hrefColumn), + }; + } + return { + title: item, + dataIndex: item, + className: 'pr-25', + render:(text, record, index)=>getLinkItem(text, record, index, isInner, item, hrefColumn), + }; + }); + return arr; +} +export function filterTableData(data: any, target: string) { + const res: any[]=[]; + data.forEach(item=>{ + const flag: boolean = (Object.values(item)).some(value=>{ + if (typeof(value) === 'object') { return false }; + return (value+'').includes(target); + }); + if (flag) { + res.push(item); + } + }); + return res; +} + diff --git a/ui/src/components/text-with-icon/text-with-icon.tsx b/ui/src/components/text-with-icon/text-with-icon.tsx new file mode 100644 index 0000000000..8bc8d0937f --- /dev/null +++ b/ui/src/components/text-with-icon/text-with-icon.tsx @@ -0,0 +1,57 @@ +/** + * 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 { Row } from "antd"; +import React, { ComponentProps } from "react"; +import { FlatBtn } from "Components/flatbtn"; + +interface TextWithIconProps extends ComponentProps { + iconPosition?: "left" | "right"; + colorWithIcon?: boolean, + icon: any; + isButton?: boolean; + text: string; + color?: string; +} + +export function TextWithIcon(props: TextWithIconProps) { + const { iconPosition = "left", colorWithIcon = true, icon, text, color = "#1890ff", isButton = false, style, ...rest } = props; + const children = props.children ? props.children : text; + const styles = { alignItems: "center", ...style }; + if (colorWithIcon) { + styles.color = color; + } + return ( + + {iconPosition === "left" && {icon}} + {isButton ? ( + props.onClick(true)}>{text} + ) : ( + {children} + )} + {iconPosition === "right" && {icon}} + + ); + +} diff --git a/ui/src/constants.ts b/ui/src/constants.ts new file mode 100644 index 0000000000..5f8d20d198 --- /dev/null +++ b/ui/src/constants.ts @@ -0,0 +1,22 @@ +// 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 const API_BASE = '/api/meta/namespaces/'; + +export const PAGE_SIDE_DEFAULT_WIDTH = 240; + +export const TABLE_DELAY = 150; diff --git a/ui/src/favicon.ico b/ui/src/favicon.ico new file mode 100644 index 0000000000..c79ff458a2 Binary files /dev/null and b/ui/src/favicon.ico differ diff --git a/ui/src/global.d.ts b/ui/src/global.d.ts new file mode 100644 index 0000000000..87c80d1d06 --- /dev/null +++ b/ui/src/global.d.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. + +declare module '*.svg'; +declare module '*.png'; +declare module '*.jpg'; +declare module '*.less'; diff --git a/ui/src/i18n.tsx b/ui/src/i18n.tsx new file mode 100644 index 0000000000..006938b699 --- /dev/null +++ b/ui/src/i18n.tsx @@ -0,0 +1,47 @@ +/** + * 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 LanguageDetector from 'i18next-browser-languagedetector'; +import i18n from 'i18next'; +import enUsTrans from '../public/locales/en-us.json'; +import zhCnTrans from '../public/locales/zh-cn.json'; +import { + initReactI18next +} from 'react-i18next'; + +i18n.use(LanguageDetector) + .use(initReactI18next) + .init({ + resources: { + en: { + translation: enUsTrans + }, + zh: { + translation: zhCnTrans + } + }, + fallbackLng: 'en', + debug: false, + interpolation: { + escapeValue: false + } + }); + +export default i18n; + diff --git a/ui/src/index.html b/ui/src/index.html new file mode 100644 index 0000000000..a7a39a3255 --- /dev/null +++ b/ui/src/index.html @@ -0,0 +1,33 @@ + + + + + + + + Apache Doris + + + + + +
+ + diff --git a/ui/src/index.less b/ui/src/index.less new file mode 100644 index 0000000000..86c63e497e --- /dev/null +++ b/ui/src/index.less @@ -0,0 +1,226 @@ +/* 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 'less/variable'; +@import '~antd/lib/style/themes/default.less'; + +html, +body, +:global(#root) { + height: 100%; +} + +:global(input[readonly]:not(.ant-cascader-input)) { + border: none !important; +} + +:global(.url) { + word-wrap: break-word; + word-break: break-all; +} + +/* navbar */ +:global { + .ant-row { + display: flex; + } + .ant-table-cell { + .ant-legacy-form-item { + margin: 0 !important; + } + } + .ant-form-item { + margin-bottom: 24px !important; + } + .ant-form-item-explain, .ant-form-item-extra { + margin-top: 0 !important; + } + .ant-select-selection-search-input { + padding-right: 0 !important; + } + .ant-tree-node-content-wrapper { + display: flex !important; + } + .ant-layout-header{ + z-index: 5!important; + box-sizing: border-box; + border: 1px solid #f0f0f0; + line-height: 61px !important; + background-color: #fff !important; + } + .ant-form-item-label>label.ant-form-item-required:not(.ant-form-item-required-mark-optional):before{ + position: absolute; + left: 100%; + top: 10px; + } + // .ant-menu-inline .ant-menu-item-selected:after, .ant-menu-inline .ant-menu-selected:after { + // display: none !important; + // } + // .ant-menu-dark.ant-menu-horizontal > .ant-menu-item, + // .ant-menu-dark.ant-menu-horizontal > .ant-menu-submenu { + // top: -1px !important; + // border-bottom: 2px solid transparent !important; + // // border-bottom-width: 2px !important; + // } + // .ant-menu-dark.ant-menu-horizontal > .ant-menu-item-selected, + // .ant-menu-dark.ant-menu-horizontal > .ant-menu-submenu-selected { + // border-bottom: 2px solid @primary-color !important; + // background: transparent !important; + // color: #608bff !important; + // } + // .ant-menu-submenu-popup.ant-menu-dark .ant-menu-item-selected, + // .ant-menu.ant-menu-dark .ant-menu-item-selected { + // background: transparent !important; + // } + .react-resizable-handle { + background-image: none !important; + height: 1px !important; + padding: 0 !important; + width: 100% !important; + background-color: #DCDCDC; + cursor: row-resize !important; + } + .react-resizable-handle:after { + display: block; + content: ''; + height: 7px; + width: 30px; + margin-left: calc((100% - 30px) / 2); + margin-top: -3px; + border: 1px solid #DCDCDC; + border-left: none; + border-right: none; + cursor: row-resize !important; + } + .react-resizable-handle-w { + left: 0 !important; + top: 0 !important; + transform: rotate(0deg) !important; + height: 100% !important; + width: 1px !important; + &:after { + display: block; + content: ""; + height: 30px; + width: 7px; + margin-left: -3px; + margin-top: 144px; + border: 1px solid #dcdcdc; + border-top: none; + border-bottom: none; + cursor: col-resize !important; + } + } + .react-resizable-handle-e { + right: 0 !important; + top: 0 !important; + transform: rotate(0deg) !important; + height: 100% !important; + width: 1px !important; + position: relative; + &:after { + display: block; + position: absolute; + content: ""; + height: 30px; + width: 7px; + margin-left: -3px; + top: calc(50% - 15px); + border: 1px solid #dcdcdc; + border-top: none; + border-bottom: none; + cursor: col-resize !important; + } + } +} + +:global { + .ant-card-body { + padding: 24px 30px !important; + } +} + +/* dropdown ---START--- */ +:global(.ant-dropdown-menu) { + padding: @spacer 0 !important; + border-radius: 2px !important; + box-shadow: 0 2px 6px 0 rgba(0, 0, 0, 0.1) !important; +} +:global(.ant-dropdown-menu-item) { + padding: 0 2*@spacer !important; + margin: 0 !important; + line-height: 30px !important; + border-radius: 0 !important; + // text-align: right; + font-size: @font-size-sm !important; + > a { + border-radius: 0 !important; + margin: 0 !important; + padding: 0 !important; + } + &:hover { + background: #f9f9f9 !important; + } +} +/* dropdown ---END--- */ + +:global(.form-item-appendix) { + position: absolute; + right: -3*@spacer; +} + +:global(.page-wrapper) { + max-width: 1280px; + margin: 0 auto; + padding: 3*@spacer; +} + +:global(.page-list-top) { + display: flex; + justify-content: space-between; + margin: 2*@spacer 0 @spacer; +} + +:global(.list-selected-count) { + display: inline-block; + width: 2.5*@spacer; + text-align: center; +} + +:global(.CodeMirror) { + line-height: 20px; +} + +:global { + .ml { + margin-right: 10px; + } +} +a:-webkit-any-link { + text-decoration: none; +} +:global { + .iconfont { + font-size: 16px; + font-style: normal; + } +} +:global { + .ant-tree { + background-color: transparent !important; + } +} diff --git a/ui/src/index.tsx b/ui/src/index.tsx new file mode 100644 index 0000000000..9bfa6402cc --- /dev/null +++ b/ui/src/index.tsx @@ -0,0 +1,32 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +import React from 'react'; +import ReactDOM from 'react-dom'; +import './i18n'; +import App from './App'; +import './index.less'; + +ReactDOM.render(, document.getElementById('root')); + diff --git a/ui/src/interfaces/http.interface.ts b/ui/src/interfaces/http.interface.ts new file mode 100644 index 0000000000..25af6a0063 --- /dev/null +++ b/ui/src/interfaces/http.interface.ts @@ -0,0 +1,23 @@ +// 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 interface Result { + data: T; + code: number; + count: number; + msg: string; +} \ No newline at end of file diff --git a/ui/src/less/variable.less b/ui/src/less/variable.less new file mode 100644 index 0000000000..2076fa3682 --- /dev/null +++ b/ui/src/less/variable.less @@ -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. */ + + @spacer: 8px; + @padding-top: 3*@spacer; + @menu-item-padding-space: 4*@spacer; + + @sider-tree-padding: @padding-top 4*@spacer; + @tree-sider-width: 240px; + @layout-header-height: 60px; + @layout-sider-background: #fafafb; + + @content-padding: @padding-top 5*@spacer; + + @content-title-font-size: 16px; + @content-padding-left: 4*@spacer; + @content-padding-top: @padding-top; + + @table-header-font-weight: normal; + + @btn-font-size-default: 12px; + + @font-size-base: 14px; + @font-size-lg: @font-size-base + 2px; + @font-size-sm: @font-size-base - 2px; + + @navbar-bg: #1b1b1b; + diff --git a/ui/src/pages/backend/index.tsx b/ui/src/pages/backend/index.tsx new file mode 100644 index 0000000000..4445997292 --- /dev/null +++ b/ui/src/pages/backend/index.tsx @@ -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. + */ + +import React from 'react'; +import {Typography, Divider} from 'antd'; +const {Title, Paragraph, Text} = Typography; +export default function Backend(params: any) { + return( + + Known Backends(8) + + TODO + + + ); +} + diff --git a/ui/src/pages/configuration/index.tsx b/ui/src/pages/configuration/index.tsx new file mode 100644 index 0000000000..8c29fd7680 --- /dev/null +++ b/ui/src/pages/configuration/index.tsx @@ -0,0 +1,56 @@ +/** + * 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 React, {useState, useEffect} from 'react'; +import {Typography, Button, Row, Col} from 'antd'; +const {Title} = Typography; +import {getConfig} from 'Utils/api'; +import Table from 'Src/components/table'; +export default function Configuration(params: any) { + const [allTableData, setAllTableData] = useState({}); + const getConfigData = function(){ + getConfig({}).then(res=>{ + if (res && res.msg === 'success') { + setAllTableData(res.data); + } + }) + .catch(err=>{ + setAllTableData({ + column_names:[], + rows:[], + }); + }); + }; + useEffect(() => { + getConfigData(); + }, []); + + return( + + Configure Info +
+ + ); +} + diff --git a/ui/src/pages/ha/index.tsx b/ui/src/pages/ha/index.tsx new file mode 100644 index 0000000000..7f661e1619 --- /dev/null +++ b/ui/src/pages/ha/index.tsx @@ -0,0 +1,67 @@ +/** + * 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 React from 'react'; +import {Typography, Divider} from 'antd'; +import Table from 'Src/components/table'; +const {Title, Text, Paragraph} = Typography; +export default function Ha(params: any) { + return( + + Frontend Role + + MASTER + + Current Journal Id + + MASTER + + Can Read + + MASTER + + Electable nodes + + MASTER + + Observer nodes + + MASTER + + Checkpoint Info + + MASTER + + Database names + + MASTER + + Allowed Frontends + + MASTER + + Removed Frontends + + MASTER + + + ); +} + diff --git a/ui/src/pages/help/index.tsx b/ui/src/pages/help/index.tsx new file mode 100644 index 0000000000..c7c6f88c0d --- /dev/null +++ b/ui/src/pages/help/index.tsx @@ -0,0 +1,69 @@ +/** + * 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 React from 'react'; +import {Typography, Divider, Row, Col, Input, Button} from 'antd'; +const {Title, Paragraph, Text} = Typography; +import Table from 'Src/components/table'; + +export default function Home(params: any) { + return( + + Help Info + This page lists the help info, like 'help contents' in Mysql client. + + + console.log(value)} + style={{color: '#ddd'}} + /> + + + + + + Exact Matching Topic + +

Level: INFO

+

Verbose Names:

+
+ Fuzzy Matching Topic(By Keyword) + Find only one category, so show you the detail info below. +

Find 1 sub topics.

+ {/*
*/} + Find 2 sub categories. + {/*
*/} + + ); +} + diff --git a/ui/src/pages/home/index.tsx b/ui/src/pages/home/index.tsx new file mode 100644 index 0000000000..4ea95a060e --- /dev/null +++ b/ui/src/pages/home/index.tsx @@ -0,0 +1,77 @@ +/** + * 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 React, {useState, useEffect} from 'react'; +import {Typography, Divider, BackTop, Spin} from 'antd'; +const {Title, Paragraph, Text} = Typography; +import {getHardwareInfo} from 'Utils/api'; + +export default function Home(params: any) { + const [hardwareData , setHardwareData] = useState({}); + const getConfigData = function(){ + getHardwareInfo().then(res=>{ + if (res && res.msg === 'success') { + setHardwareData(res.data); + } + }) + .catch(err=>{ + setHardwareData({ + VersionInfo:{}, + HarewareInfo:{}, + }); + }); + }; + function getItems(data, flag){ + let arr = []; + for (const i in data) { + if (flag) { + arr.push( +

+ ) + } else { + arr.push( +

{i + ' : ' + data[i]}

+ ) + } + } + return arr; + } + function createMarkup(key,data) { + return {__html:key + ' : ' + String(data)}; + } + useEffect(() => { + getConfigData(); + }, []); + return( + + Version + + {...getItems(hardwareData.VersionInfo, false)} + + + Hardware Info + + {...getItems(hardwareData.HarewareInfo, true)} + + + {hardwareData.HarewareInfo?'':} + + ); +} + diff --git a/ui/src/pages/layout/index.css b/ui/src/pages/layout/index.css new file mode 100644 index 0000000000..9bb8cd8ce6 --- /dev/null +++ b/ui/src/pages/layout/index.css @@ -0,0 +1,34 @@ + +/* .ant-layout-header .logo { + width: 120px; + height: 31px; + margin: 16px 24px 16px 0; + float: left; +} */ +.ant-layout-header .logo img { + width: 120px; + height: 31px; +} +.site-layout .site-layout-background { + background: #fff; +} +.ant-menu-dark.ant-menu-horizontal { + height: 64px; +} +.userSet { + height: 56px; + float: right; +} + +.userSet > span { + display: inline-block; + height: 100%; + padding: 0 12px; + /* color: rgba(255, 255, 255, 0.65); */ +} + +.userSet .avatar { + width: 24px; + height: 24px; + margin-right: 7px; +} diff --git a/ui/src/pages/layout/index.less b/ui/src/pages/layout/index.less new file mode 100644 index 0000000000..d70f7a8e2a --- /dev/null +++ b/ui/src/pages/layout/index.less @@ -0,0 +1,27 @@ +/* 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. */ + +.logo{ + width: 143px; + height: 50px; + float: left; + margin-left: -23px; + margin-top: 6px; + cursor: pointer; + background-size: 143px 50px; + background-image: url(../../../public/img/logo.png); +} \ No newline at end of file diff --git a/ui/src/pages/layout/index.tsx b/ui/src/pages/layout/index.tsx new file mode 100644 index 0000000000..466cf95d40 --- /dev/null +++ b/ui/src/pages/layout/index.tsx @@ -0,0 +1,135 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +import React, {useState} from 'react'; +import {Layout, Menu, Dropdown, message} from 'antd'; +import { CaretDownOutlined, LogoutOutlined} from '@ant-design/icons'; +import {renderRoutes} from 'react-router-config'; +import {useHistory} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import routes from 'Src/router'; +import {logOut} from 'Utils/api'; +import './index.css'; +import styles from './index.less'; +const {Header, Content, Footer} = Layout; + +function Layouts(props: any) { + let { t } = useTranslation(); + const [route, setRoute] = useState(props.route.routes); + const [current, setCurrent] = useState(props.location.pathname); + const history = useHistory(); + //Jump page + function handleClick(e) { + setCurrent(e.key); + if (e.key === '/System' ) { + history.push(`${e.key}?path=/`); + return; + } + if (location.pathname === e.key) { + location.reload(); + } + history.push(e.key); + if(location.pathname.includes('Playground')){ + location.reload(); + } + } + function clearAllCookie() { + var keys = document.cookie.match(/[^ =;]+(?=\=)/g); + if(keys) { + for(var i = keys.length; i--;) + document.cookie = keys[i] + '=0;expires=' + new Date(0).toUTCString() + } + } + function onLogout(){ + logOut().then((res)=>{ + localStorage.setItem('username',''); + clearAllCookie(); + message.success(t('exitSuccessfully')) + history.push('/login'); + }) + } + const menu = ( + + + + {t('signOut')} + + + ); + return ( + +
+
{history.replace('/home');setCurrent('')}}>
+ + + + {/* */} + {localStorage.getItem('username')} + + + + + {routes?.routes[1]?.routes?.map(item => { + if (item.path !== '/login'&&item.path !== '/home') { + return ( + + {item.title} + + ); + } + })} + +
+ + +
+ {renderRoutes(route)} +
+
+ + {/*
xxx
*/} +
+ + ); +} + +export default Layouts;/** + * 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. + */ + diff --git a/ui/src/pages/login/cover.less b/ui/src/pages/login/cover.less new file mode 100644 index 0000000000..4c71831610 --- /dev/null +++ b/ui/src/pages/login/cover.less @@ -0,0 +1,39 @@ +/* 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. */ + +:global(.login) { + /deep/ :global(.ant-form-item-label){ + label{ + color: #999; + } + } + :global(.ant-btn){ + margin-top: 20px; + } + :global(.ant-input){ + border: none; + color: #999; + border-radius: 10px; + background: #f6f7fb; + } + :global(.ant-input-password){ + border: none; + color: #999; + border-radius: 10px; + background: #f6f7fb; + } +} \ No newline at end of file diff --git a/ui/src/pages/login/index.less b/ui/src/pages/login/index.less new file mode 100644 index 0000000000..24408158be --- /dev/null +++ b/ui/src/pages/login/index.less @@ -0,0 +1,43 @@ +/* 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. */ + +.background{ + width: 100%; + height: 100%; + display: flex; + align-items: center; + flex-direction: column; + justify-content: center; + background-image: url('../../../public/img/background.png'); + background-size: 100% 100%; +} +.login-form{ + width: 350px; + height: 330px; + border-radius: 10px; + margin-bottom: 15%; + padding: 40px 35px 15px; + background-color: #fff; + box-shadow: 0 0 20px 2px rgba(0,0,0,.1); +} +.logo{ + width: 150px; + height: 60px; + margin-bottom: 15px; + background-image: url('../../../public/img/logo.png'); + background-size: 100% 100%; +} \ No newline at end of file diff --git a/ui/src/pages/login/index.tsx b/ui/src/pages/login/index.tsx new file mode 100644 index 0000000000..402e38c441 --- /dev/null +++ b/ui/src/pages/login/index.tsx @@ -0,0 +1,107 @@ +/** + * 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 React,{useState} from 'react'; +import {Form, Input, Button, Checkbox} from 'antd'; +import request from 'Utils/request'; +import {useHistory} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import styles from './index.less'; +import './cover.less'; +function Login(){ + let { t } = useTranslation(); + const [username, setUsername] = useState(); + const history = useHistory(); + const layout = { + labelCol: {span: 8}, + wrapperCol: {span: 24}, + layout='vertical', + }; + const tailLayout = { + wrapperCol: {span: 24}, + }; + + interface Result { + content: T; + status: string; + partialResults: boolean; + msg: string; + code: number; + } + function login(data: any): Promise> { + return request('/rest/v1/login', { + method: 'POST', + headers:{Authorization: data.password?`Basic ${btoa(data.username+':'+data.password)}`:`Basic ${btoa(data.username+':')}`}, + }); + } + const onFinish = values => { + login(values).then(res=>{ + if(res.code===200){ + history.push('/home'); + localStorage.setItem('username', username) + } + }); + }; + + const onFinishFailed = errorInfo => { + console.log('Failed:', errorInfo); + }; + // 878CB1 + // 31395B + return ( +
+
+
+ + {setUsername(e.target.value)}} /> + + + + + + + + + + +
+ ); +} + +export default Login; + diff --git a/ui/src/pages/logs/index.tsx b/ui/src/pages/logs/index.tsx new file mode 100644 index 0000000000..a4024de8e2 --- /dev/null +++ b/ui/src/pages/logs/index.tsx @@ -0,0 +1,83 @@ +/** + * 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 React,{useState, useEffect, useRef} from 'react'; +import {Typography, Divider, Row, Col, Input, BackTop} from 'antd'; +const {Title, Paragraph, Text} = Typography; +import {getLog} from 'Src/utils/api'; +const {Search} = Input; +import {Result} from '@src/interfaces/http.interface'; +export default function Logs(params: any) { + const container = useRef(); + const [LogConfiguration, setLogConfiguration] = useState({}); + const [LogContents, setLogContents] = useState({}); + function getLogData(data){ + getLog(data).then(res=>{ + if(res.data){ + if(res.data.LogConfiguration){ + setLogConfiguration(res.data.LogConfiguration); + } + if(res.data.LogContents){ + container.current.innerHTML=res.data.LogContents.log; + setLogContents(res.data.LogContents); + } + } + }); + } + useEffect(() => { + getLogData({}); + }, []); + return( + + Log Configuration + +

Level: {LogConfiguration.VerboseNames}

+

Verbose Names:{LogConfiguration.VerboseNames}

+

Audit Names: {LogConfiguration.AuditNames}

+
+ +
+ getLogData({add_verbose:value})} + /> + + + getLogData({del_verbose:value})} + /> + + + + Log Contents + +

Log path is: {LogContents.logPath}

+

{LogContents.showingLast}

+
+
+ {/* {LogContents.log} */} +
+ + + ); +} + diff --git a/ui/src/pages/playground/adhoc.context.ts b/ui/src/pages/playground/adhoc.context.ts new file mode 100644 index 0000000000..ef589c7fc9 --- /dev/null +++ b/ui/src/pages/playground/adhoc.context.ts @@ -0,0 +1,20 @@ +// 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 React from 'react'; + +export const QueueContext = React.createContext({}); \ No newline at end of file diff --git a/ui/src/pages/playground/adhoc.data.ts b/ui/src/pages/playground/adhoc.data.ts new file mode 100644 index 0000000000..2b8d25f738 --- /dev/null +++ b/ui/src/pages/playground/adhoc.data.ts @@ -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. + + +export const CODEMIRROR_OPTIONS = { + mode: 'sql', + theme: 'material', + lineNumbers: true, + fullScreen: false, + matchBrackets: true, + indentUnit: 4, + autofocus: true, + extraKeys: { + 'Alt-/': 'autocomplete', + }, +}; + +export const QUERY_TABTYPE = { + key1: '11133sdfsd', + key2: '2222sdfsdd', + key3: '3321fdsfsd', +}; +export enum TabPaneType { + Result = 'result', + History = 'history', + Structure = 'structure', + Favorite = 'favorite' +} + +export enum AdhocContentRouteKeyEnum { + Result = 'result', + Structure = 'structure', + History = 'history', + Favorite = 'favorite', + Default = 'default' +} \ No newline at end of file diff --git a/ui/src/pages/playground/adhoc.subject.ts b/ui/src/pages/playground/adhoc.subject.ts new file mode 100644 index 0000000000..dd9b69135b --- /dev/null +++ b/ui/src/pages/playground/adhoc.subject.ts @@ -0,0 +1,24 @@ +// 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 {Subject} from 'rxjs'; + +// 什么时候在编辑器中打开语句 +export const openInEditorSubject = new Subject(); + +// Run SQL Success +export const runSQLSuccessSubject = new Subject(); diff --git a/ui/src/pages/playground/content/components/data-prev.tsx b/ui/src/pages/playground/content/components/data-prev.tsx new file mode 100644 index 0000000000..55a1aa52f8 --- /dev/null +++ b/ui/src/pages/playground/content/components/data-prev.tsx @@ -0,0 +1,100 @@ +/** + * 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 React,{useState,useEffect} from 'react'; +import {AdHocAPI} from 'Utils/api'; +import {getDbName} from 'Utils/utils'; +import {Row, Empty} from 'antd'; +import {FlatBtn} from 'Components/flatbtn'; +import {useTranslation} from 'react-i18next'; +export function DataPrev(props: any) { + let { t } = useTranslation(); + const {db_name,tbl_name} = getDbName(); + const [tableData,setTableData] = useState([]); + function toQuery(): void { + AdHocAPI.doQuery({ + db_name, + body:{stmt:`SELECT * FROM ${db_name}.${tbl_name} LIMIT 10`}, + }).then(res=>{ + if (res && res.data) { + setTableData(res.data); + } + }) + .catch(()=>{ + setTableData([]); + }); + } + useEffect(()=>{ + toQuery(); + },[location.pathname]); + return ( +
+ + {t('dataPreview')}({t('display10')}) + + {db_name}.{tbl_name} + + + toQuery() + } + > + {t('refresh')} + + +
+ {tableData?.meta?.length + ?
+
+
+ + + {tableData?.meta?.map(item => ( + + ))} + + + + {tableData.data?.map((item,index) => ( + + {item.map((tdData,index) => ( + + ))} + + ))} + +
+ {item.name} +
+ {tdData == '\\N'?'-':tdData} +
+ + :} + + + ); +} + + diff --git a/ui/src/pages/playground/content/components/history-query.tsx b/ui/src/pages/playground/content/components/history-query.tsx new file mode 100644 index 0000000000..89ceb2a971 --- /dev/null +++ b/ui/src/pages/playground/content/components/history-query.tsx @@ -0,0 +1,66 @@ +/** + * 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 React,{useState,SyntheticEvent} from 'react'; +import {ResizableBox} from 'react-resizable'; +// const ResizableBox = require('react-resizable').ResizableBox; +import styles from './index.less'; +require('react-resizable/css/styles.css'); +// import VirtualList from 'Components/VirtualList/index'; +export function HistoryQuery(props: any) { + // const { match } = props; + // const queueInfo = { ...useContext(QueueContext) }; + const [historyBoxWidth,setHistoryBoxWidth] = useState(300); + // const historyBoxWidth = +(localStorage.getItem('historyBoxWidth')||300); + + const onResize=function(e: SyntheticEvent, data: any) { + const width = data.size.width || 300; + setHistoryBoxWidth(width); + // localStorage.setItem('historyBoxWidth', width); + }; + const initData = { + recommendHouse: '名仕公馆', + recommendReason: '开发商租赁保障,价格低,位置优越', + }; + const array = []; + for(let i = 0; i < 999; i++){ + array.push(initData); + } + const resource = array; + const renderTable =(item,index)=>{return (
+ 推荐房源:{item.recommendHouse }{index} + 推荐理由:{item.recommendReason} +
);}; + return ( + + + + ); +} + diff --git a/ui/src/pages/playground/content/components/index.less b/ui/src/pages/playground/content/components/index.less new file mode 100644 index 0000000000..8cca8bd057 --- /dev/null +++ b/ui/src/pages/playground/content/components/index.less @@ -0,0 +1,20 @@ +/* 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. */ + +.importForm{ + padding: 30px; +} diff --git a/ui/src/pages/playground/content/content-result.tsx b/ui/src/pages/playground/content/content-result.tsx new file mode 100644 index 0000000000..68f806d756 --- /dev/null +++ b/ui/src/pages/playground/content/content-result.tsx @@ -0,0 +1,210 @@ +/** + * 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 React, {useEffect, useState} from 'react'; +import { + Tabs, + Row, + Card, + Col, + Progress, + Popover, + Pagination, +} from 'antd'; +import {TabPaneType} from '../adhoc.data'; +import { useLocation } from 'react-router-dom' +import {FlatBtn} from 'Components/flatbtn'; +import { + DownloadOutlined, + CloseCircleFilled, +} from '@ant-design/icons'; +import SyntaxHighlighter from 'react-syntax-highlighter'; +import {docco} from 'react-syntax-highlighter/dist/esm/styles/hljs'; +import {TextWithIcon} from 'Components/text-with-icon/text-with-icon'; +import {LoadingWrapper} from 'Components/loadingwrapper/loadingwrapper'; +import {useTranslation} from 'react-i18next'; + +export function AdhocContentResult(props) { + let { t } = useTranslation(); + let location = useLocation() + const jobId = props.match.params.jobId; + const [showSaveResultModal, setShowSaveResultModal] = useState(false); + const [tableData, setTableDate] = useState([]); + const [resStatus, setResStatus] = useState([]); + // const [pageSize, setPageSize] = useState(20); + const [total, setTotal] = useState([]); + const [runningQueryInfo, setRunningQueryInfo] = useState({}); + useEffect(() => { + const runningQueryInfo = location.state; + if (runningQueryInfo.msg=='success') { + runningQueryInfo.status = 'success'; + } else { + runningQueryInfo.status = 'failed'; + } + if (runningQueryInfo.data?.type === 'exec_status') { + setResStatus(runningQueryInfo.data.status) + } else { + const tableData = runningQueryInfo.data?(runningQueryInfo.data?.data).slice(0,20):[]; + setTableDate(tableData); + setTotal(runningQueryInfo.data?.data.length); + } + setRunningQueryInfo(runningQueryInfo); + },[location.state]); + + function renderRunStatus() { + return ( + <> + + + {t('runSuccessfully')} + + + ); + } + function getELe(data) { + let arr = [] + for (const i in data) { + arr.push( + + {i}: + {data[i]} + + ); + } + return arr; + } + function changeShowTableItem(page,size){ + const allTableData =runningQueryInfo.data?.data; + const tableData = allTableData.slice((page-1)*size,page*size); + setTableDate(tableData); + } + return ( + + + + {runningQueryInfo.status !== 'failed' ? ( + + + {renderRunStatus()} + + {/* {runningQueryInfo.status === 'success' && ( + setShowSaveResultModal(true)} + style={{cursor: 'pointer'}} + icon={} + isButton={true} + text="保存结果" + /> + )} */} + + ) : ( + } + text={"执行失败: "+runningQueryInfo.msg} + color="red" + style={{ + marginBottom: 10, + }} + /> + )} + + {runningQueryInfo.sqlCode?runningQueryInfo.sqlCode:''} + + + + {/* + {t('queryForm')}: + {runningQueryInfo.tbl_name} + */} + + {t('startingTime')}: + {runningQueryInfo.beginTime} + + + {t('endTime')}: + {runningQueryInfo.beginTime} + + { + ...getELe(resStatus) + } + {/* + 结果表: + queryResultTable()}> + 临时表 + + */} + +
+
+
+ + + + {runningQueryInfo.data?.meta?.map(item => ( + + ))} + + + + {tableData.map((item,index) => ( + + {item.map(tdData => ( + + ))} + + ))} + +
+ {item.name} +
+ {tdData == '\\N'?'-':tdData} +
+
+
+
+ `Total ${total} items`} + /> +
+
+
+ ); +} + diff --git a/ui/src/pages/playground/content/content-structure.tsx b/ui/src/pages/playground/content/content-structure.tsx new file mode 100644 index 0000000000..e2866c8282 --- /dev/null +++ b/ui/src/pages/playground/content/content-structure.tsx @@ -0,0 +1,104 @@ +/** + * 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 React, {useState,useEffect} from 'react'; +import {Tabs, Row, Table} from 'antd'; +import {TabPaneType,QUERY_TABTYPE} from '../adhoc.data'; +import React from 'react'; +import {FlatBtn} from 'Components/flatbtn'; +import {TABLE_DELAY} from 'Constants'; +import {useRequest} from '@umijs/hooks'; +import {AdHocAPI} from 'Utils/api'; +import {getDbName} from 'Utils/utils'; +import {Result} from '@src/interfaces/http.interface'; +import {DataPrev} from './components/data-prev'; +import getColumns from './getColumns'; +import {useHistory} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +export function ContentStructure(props: any) { + let { t } = useTranslation() + const {db_name,tbl_name}=getDbName(); + const [columns, setColumns] = useState([]) + const getTableInfoRequest = useRequest>( + () => { + const param = { + db_name, + tbl_name, + }; + return AdHocAPI.getDatabaseList(param); + }, + { + refreshDeps: [location.pathname], + }, + ); + const cols = getTableInfoRequest?.data?.data?.[tbl_name]?.schema; + useEffect(() => { + if (cols) { + setColumns(getColumns(cols[0], props, false)) + } else { + setColumns([]) + } + }, [cols]) + + const history = useHistory(); + return ( + {if (key ===QUERY_TABTYPE.key3) {history.push('/Playground/import/'+db_name+'-'+tbl_name);}}} + > + + + + {t('database')}: {db_name}      {t('table')}: {tbl_name} + + + props.queryTable(`${db_name}.${tbl_name}`) + } + > + {t('queryForm')} + + + + + + + + + {/* */} + + + ); +} + diff --git a/ui/src/pages/playground/content/getColumns.tsx b/ui/src/pages/playground/content/getColumns.tsx new file mode 100644 index 0000000000..8ea6dc3b41 --- /dev/null +++ b/ui/src/pages/playground/content/getColumns.tsx @@ -0,0 +1,72 @@ +/** + * 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 React from 'react'; + +export default function getColumns(data, fa, isAction){ + if(!data){ + return [] + } + let arr = [] + for(let i in data){ + if(i === 'key'){ + continue + } + if ( i === 'Field' && fa && !isAction ){ + arr.push({ + title: 'Field', + dataIndex: 'Field', + key: 'Field', + render: Field => ( + fa.handleNameClicked(Field)} + > + {Field} + + ), + }) + } else { + arr.push({ + title: i, + dataIndex: i, + key: i, + render: i => ( + + {i === '\t'?'\\t':i} + + ), + }) + } + } + if(isAction){ + arr.push( + { + title: 'Action', + dataIndex: '', + key: 'x', + render: (text, record, index) => { + return {fa(record, index, e)}}>Delete + }, + }, + ) + } + return arr; +} + diff --git a/ui/src/pages/playground/content/index.less b/ui/src/pages/playground/content/index.less new file mode 100644 index 0000000000..6a60d496d1 --- /dev/null +++ b/ui/src/pages/playground/content/index.less @@ -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 '~antd/lib/style/themes/default.less'; + +.adhoc-code { + height: 100%; + padding-bottom: 60px; + &-operator { + margin-top: 10px; + } +} + +.adhoc-content-favorite { + &-edit-icon { + margin-left: 4px; + cursor: pointer; + } + &-edit-icon:hover { + color: @primary-color; + } + &-code { + margin-top: 10px; + // > pre { + // margin-bottom: 0 !important; + // } + } +} \ No newline at end of file diff --git a/ui/src/pages/playground/content/index.tsx b/ui/src/pages/playground/content/index.tsx new file mode 100644 index 0000000000..61e4b28b32 --- /dev/null +++ b/ui/src/pages/playground/content/index.tsx @@ -0,0 +1,241 @@ +/** + * 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 React, {useState, useEffect, useContext, SyntheticEvent} from 'react'; +// import {Controlled as CodeMirror} from 'react-codemirror2'; +import styles from './index.less'; +import { + CODEMIRROR_OPTIONS, + AdhocContentRouteKeyEnum, +} from '../adhoc.data'; +import {Button, Row, Col, message} from 'antd'; +import {PlayCircleFilled} from '@ant-design/icons'; +import {Switch, Route, Redirect} from 'react-router'; +import {AdhocContentResult} from './content-result'; +import {useRequest} from '@umijs/hooks'; +import {AdHocAPI} from 'Utils/api'; +import {Result} from '@src/interfaces/http.interface'; +import {isSuccess, getDbName, getTimeNow} from 'Utils/utils'; +import {CodeMirrorWithFullscreen} from 'Components/codemirror-with-fullscreen/codemirror-with-fullscreen'; +import { + openInEditorSubject, + runSQLSuccessSubject, +} from '../adhoc.subject'; +import {FlatBtn} from 'Components/flatbtn'; +import {ContentStructure} from './content-structure'; +import sqlFormatter from 'sql-formatter'; +import {useTranslation} from 'react-i18next'; +import {ResizableBox} from 'react-resizable'; +require('react-resizable/css/styles.css'); +let editorInstance: any; +let isQueryTableClicked = false; +let isFieldNameInserted = false; + +export function AdHocContent(props: any) { + let { t } = useTranslation(); + const {match} = props; + const [loading, setLoading] = useState({ + favoriteConfirm: false, + }); + const [code, setCode] = useState(''); + const editorAreaHeight = +(localStorage.getItem('editorAreaHeight') || 300); + const beginTime = getTimeNow(); + const runQuery = useRequest>( + () => + AdHocAPI.doQuery({ + db_name:getDbName().db_name, + body:{stmt:code}, + }), + { + manual: true, + onSuccess: res => { + const endTime = getTimeNow(); + const {db_name, tbl_name} = getDbName(); + if (isSuccess(res)) { + res.sqlCode = code; + res = {...res, db_name, tbl_name, beginTime, endTime} + props.history.push({pathname:`/Playground/result/${db_name}-${tbl_name}`,state: res}); + runSQLSuccessSubject.next(true); + } else { + res.sqlCode = code; + res = {...res, db_name, tbl_name, beginTime, endTime} + props.history.push({pathname:`/Playground/result/${db_name}-${tbl_name}`,state: res}); + runSQLSuccessSubject.next(false); + } + }, + onError: () => { + message.error(t('errMsg')); + runSQLSuccessSubject.next(false); + }, + }, + ); + + useEffect(() => { + const subscription = openInEditorSubject.subscribe(code => { + setCode(code); + setEditorCursor(false); + }); + return () => subscription.unsubscribe(); + }, []); + + const handleChange = (_editor, _data, value) => { + setCode(value); + }; + + + function handleQueryTable(tableName: string) { + isQueryTableClicked = true; + setCode(`SELECT FROM ${tableName}`); + setEditorCursor(false); + } + + function setEditorCursor(insert = false) { + setTimeout(() => { + editorInstance.focus(); + editorInstance.setCursor({line: 0, ch: 7}); + isFieldNameInserted = insert; + }, 0); + } + + + function handleRunQuery() { + runQuery.run(); + } + + function handleNameClicked(fieldName: string) { + const currentPos = editorInstance.getCursor(); + const insertFieldName = isFieldNameInserted + ? `, ${fieldName}` + : `${fieldName}`; + editorInstance.replaceRange(insertFieldName, currentPos); + setTimeout(() => { + editorInstance.focus(); + isFieldNameInserted = true; + }, 0); + } + return ( +
+ +
{t('editor')}    + + + { + setCode(''); + editorInstance.focus(); + }}>{t('clear')} + + + {t('currentDatabase')}: {getDbName().db_name} + + { + const height = data.size.height || 300; + localStorage.setItem('editorAreaHeight', height); + }} + minConstraints={[Infinity, 125]} + maxConstraints={[Infinity, 400]} + axis="y" + > +
+ (editorInstance = editor)} + // onFocus={() => isFieldNameInserted = false} + onCursor={(editor: any, data: any) => { + if (data.xRel || isQueryTableClicked) { + isFieldNameInserted = false; + } + }} + isDoris={props.isDoris} + /> + +
+ + + {/* */} + + + + + ( + + )} + /> + ( +
+
+ + handleQueryTable(tableName) + } + handleNameClicked={fieldName => + handleNameClicked(fieldName) + } + isDoris={props.isDoris} + // tabChange={() =>console.log(11)} + /> +
+ {/*
+ + +
*/} +
+ )} + /> + {/* <>} + /> */} + {/* */} +
+ + ); +} + diff --git a/ui/src/pages/playground/data-import/import-func.tsx b/ui/src/pages/playground/data-import/import-func.tsx new file mode 100644 index 0000000000..58a3b26e33 --- /dev/null +++ b/ui/src/pages/playground/data-import/import-func.tsx @@ -0,0 +1,32 @@ +/** + * 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 Item from "antd/lib/list/Item"; + +export function getAllTableData(max, arr) { + if (!max || !arr) { + return []; + } + return arr.map((item,i)=>{ + item.length = max; + item.key = i; + return item; + }); +} + diff --git a/ui/src/pages/playground/data-import/import-result.tsx b/ui/src/pages/playground/data-import/import-result.tsx new file mode 100644 index 0000000000..ffef384394 --- /dev/null +++ b/ui/src/pages/playground/data-import/import-result.tsx @@ -0,0 +1,68 @@ +/** + * 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 React from 'react'; +import {Modal} from 'antd'; +export function ImportResult(data,callback) { + const arr = []; + for (const i in data) { + arr.push( + + + + + ); + } + Modal.info({ + title: 'Import Result', + width: 800, + content: ( +
+
+
+
+ {i} + + {(data[i] && data[i].includes('http'))?{data[i]}:data[i]} +
+ + {...arr} + +
+ + + + ), + onOk() { + callback(); + }, + }); +} + diff --git a/ui/src/pages/playground/data-import/index.less b/ui/src/pages/playground/data-import/index.less new file mode 100644 index 0000000000..151230517e --- /dev/null +++ b/ui/src/pages/playground/data-import/index.less @@ -0,0 +1,23 @@ +/* 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. */ + +:global(.clickRowStyle) { + background-color: #cce6d9; +} +:global(.ant-table-tbody)>:global(.clickRowStyle):hover>td{ + background-color: #cce6d9 !important; +} \ No newline at end of file diff --git a/ui/src/pages/playground/data-import/index.tsx b/ui/src/pages/playground/data-import/index.tsx new file mode 100644 index 0000000000..9a6dc566fe --- /dev/null +++ b/ui/src/pages/playground/data-import/index.tsx @@ -0,0 +1,389 @@ +/** + * 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 React,{useState, useEffect, useLayoutEffect} from 'react'; +import {AdHocAPI} from 'Utils/api'; +import {getDbName} from 'Utils/utils'; +import {Typography, Steps, Button, message, Form, Input, Select, Upload, Table, Empty} from 'antd'; +import {UploadOutlined} from '@ant-design/icons'; +import {AdHocAPI, doUp, getUploadData, deleteUploadData} from 'Utils/api'; +const {Step} = Steps; +const {Option} = Select; +import {getAllTableData} from './import-func'; +import {ImportResult} from './import-result'; +import {useHistory} from 'react-router-dom'; +import {useTranslation} from 'react-i18next'; +import 'antd/lib/style/themes/default.less'; +import getColumns from '../content/getColumns'; +import './index.less'; +export default function DataImport(props: any) { + let { t } = useTranslation(); + const history = useHistory(); + const [header, setHeader] = useState([]) + const [rowId, setRowId] = useState() + const [cols, setCols] = useState([]) + const [headerUploadData, setHeaderUploadData] = useState([]) + const [colsUploadData, setColsUploadData] = useState([]) + const [prevData, setPrevData] = useState([]); + const [prevBackData, setPrevBackData] = useState(); + const [labelV, setLabel] = useState(); + const [columnsV, setColumns] = useState(); + const [fileList, setFileList] = useState([]); + const [column_separator, setColumnSeparator] = useState('\t'); + const {db_name, tbl_name}=getDbName(); + const [form] = Form.useForm(); + const steps = [ + { + title: t('table'), + }, + { + title: t('selectTheFile'), + }, + { + title: t('loadConfig'), + }, + ]; + const [current, setCurrent] = useState(0); + const layout = { + labelCol: {span: 3}, + wrapperCol: {span: 6}, + labelAlign: 'left', + }; + const uploadData = { + name: 'file', + action: `/api/default_cluster/${db_name}/${tbl_name}/upload`, + data:{ + column_separator, + preview:'true', + }, + headers: { + authorization: 'authorization-text', + }, + fileList, + beforeUpload(file){ + if (file.size/1024/1024 > 100) { + message.error(t('fileSizeWarning')); + return false; + } + return true + }, + onChange(info) { + // if (info.file.status !== 'uploading') { + + // } + if (info.file.status === 'done') { + message.success(`${info.file.name} file uploaded successfully`); + getUploadList() + } else if (info.file.status === 'error') { + message.error(`${info.file.name} file upload failed.`); + setHeaderUploadData([]); + setColsUploadData([]) + } + if (info.fileList.length === 2) { + info.fileList = info.fileList.slice(-1); + setFileList(info.fileList); + } else if(info.fileList.length === 1) { + setFileList(info.fileList); + } + if(!info.file.status){ + setFileList([]); + } + }, + progress: { + strokeColor: { + '0%': '#108ee9', + '100%': '#87d068', + }, + strokeWidth: 3, + format: percent => `${parseFloat(percent.toFixed(2))}%`, + }, + }; + function next() { + const num = current + 1; + if (current === 1 && !prevBackData) { + message.error(t('uploadWarning')); + return; + } + setCurrent( num ); + } + + function prev() { + const num = current - 1; + setCurrent( num ); + } + + function getTableInfoRequest (){ + const param = { + db_name, + tbl_name, + }; + AdHocAPI.getDatabaseList(param).then( + res => { + // const endTime = getTimeNow(); + const {db_name, tbl_name} = getDbName(); + if (res) { + let cols = res.data[tbl_name]?.schema; + setCols(cols); + setHeader(getColumns(cols[0], false, false)) + } else { + setCols([]) + setHeader([]) + } + } + ).catch( + () => { + message.error(t('errMsg')); + } + ) + } + + function doUploadData() { + form.validateFields() + .then(value=>{ + const params = { + db_name, + tbl_name, + file_id: prevBackData.id, + file_uuid: prevBackData.uuid, + label: labelV, + columns: columnsV, + column_separator: prevBackData.columnSeparator + }; + doUp(params).then(res=>{ + if(res){ + if(res.data){ + message.success(`${res.msg}`); + ImportResult(res.data,()=>{ + history.push(`/Playground/structure/${db_name}-${tbl_name}`); + }); + } + } + }); + }) + .catch(errorInfo => { + message.error(`${errorInfo}`); + }); + } + useEffect(() => { + getTableInfoRequest(); + getUploadList(); + }, []); + function doBack(){ + history.push(`/Playground/structure/${db_name}-${tbl_name}`); + } + function getPrev(data){ + setRowId(data.id); + getUploadData({ + db_name, + tbl_name, + file_id:data.id, + file_uuid:data.uuid, + preview:true + }).then((res)=>{ + if (res.data) { + const data = res.data; + setPrevBackData(data); + setPrevData(getAllTableData(data.maxColNum , data.lines)); + } else { + setPrevBackData(''); + setPrevData([]); + } + }).catch(err=>{ + setPrevBackData(''); + setPrevData([]); + }) + } + function getUploadList(){ + getUploadData({ + db_name, + tbl_name, + }).then((res)=>{ + if(res.data){ + let data = res.data; + setHeaderUploadData(getColumns(data[0], deleteUpload, true)); + setColsUploadData(data) + } + }).catch(()=>{ + setHeaderUploadData([]); + setColsUploadData([]) + }) + } + function deleteUpload(data, i, e){ + e.stopPropagation(); + deleteUploadData({ + db_name, + tbl_name, + file_uuid:data.uuid, + file_id:data.id + }).then((res)=>{ + getUploadList(); + setPrevData([]); + setPrevData([]); + setPrevBackData(''); + setRowId(''); + }) + } + function setRowClassName(record){ + return record.id === rowId ? 'clickRowStyle' : ''; + } + return ( +
+ + {steps.map(item => ( + + ))} + +
+ {/* {steps[current].content} */} + {current === 0 + ? <> + +

{t('database')}: {db_name}

+

{t('table')}: {tbl_name}

+
+ {t('tableStructure')}: + + :'' + } + {current === 1 + ? <> + + + + + + + + + +

{t('uploadedFiles')}

+
{ + return { + onClick: event => {getPrev(record)}, + }; + }} + /> +

{t('filePreview')}({t('display10')})

+
+
+
+
+ + {prevData?.map((item,index) => ( + + {item.map(tdData => ( + + ))} + + ))} + +
+ {tdData == '\\N'?'-':tdData} +
+
+
+ + + :'' + } + {current === 2 + ? <> +
+ + setLabel(e.target.value)}/> + + + {setColumns(e.target.value);}}/> + +
+ :'' + } + +
+ {current > 0 && ( + + )} + {current < steps.length - 1 && ( + + )} + {current === steps.length - 1 && ( + + )} +   +
+ + ); +} + diff --git a/ui/src/pages/playground/index.less b/ui/src/pages/playground/index.less new file mode 100644 index 0000000000..e9f640bf92 --- /dev/null +++ b/ui/src/pages/playground/index.less @@ -0,0 +1,68 @@ +/* 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 '../../less/variable'; +@import '~antd/lib/style/themes/default.less'; +.content { + padding: 3*@spacer; + background-color: #fff; + // max-width: 1200px; +} +.adhoc { + display: flex; + height: 100%; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + flex-direction: row; +} + +.breadcrumb { + margin-bottom: 3*@spacer!important; +} + +.section { + margin-left: 30px; +} +:global { + .warning { + color: red; + } + .info { + font-size: 12px; + margin-left: 10px; + } +} +.wrap{ + /deep/ .react-resizable-handle{ + left: 0; + right: initial; + height: 100%!important; + width: 1px!important; + &:after{ + display: block; + content: ""; + height: 30px; + width: 7px; + margin-left: -3px; + margin-top: 144px; + border: 1px solid #dcdcdc; + border-top: none; + border-bottom: none; + cursor: col-resize!important; + } + } +} \ No newline at end of file diff --git a/ui/src/pages/playground/index.tsx b/ui/src/pages/playground/index.tsx new file mode 100644 index 0000000000..0d16808984 --- /dev/null +++ b/ui/src/pages/playground/index.tsx @@ -0,0 +1,45 @@ +/** + * 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 React from 'react'; +import {PageSide} from './page-side/index'; +import {AdHocTree} from './tree/index'; +import {Layout} from 'antd'; +import styles from './index.less'; +import {AdHocContent} from './content'; +const Content = Layout.Content; +function QueryContent(props) { + return ( + + + + + + + + + ); +} + +export default QueryContent; + diff --git a/ui/src/pages/playground/page-side/index.less b/ui/src/pages/playground/page-side/index.less new file mode 100644 index 0000000000..0a80f52056 --- /dev/null +++ b/ui/src/pages/playground/page-side/index.less @@ -0,0 +1,23 @@ +/* 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. */ + +.side{ + flex: 0 0 300px; + height: 100%; + background: #fff; + min-height: calc(100vh - 60px); +} \ No newline at end of file diff --git a/ui/src/pages/playground/page-side/index.tsx b/ui/src/pages/playground/page-side/index.tsx new file mode 100644 index 0000000000..570584b3d4 --- /dev/null +++ b/ui/src/pages/playground/page-side/index.tsx @@ -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. + */ + +import React, {useState, useEffect, SyntheticEvent} from 'react'; +import {ResizableBox} from 'react-resizable'; +require('react-resizable/css/styles.css'); +import styles from './index.less'; + +export function PageSide(props: any) { + const {children} = props; + const [sideBoxWidth,setSideBoxWidth] = useState(300); + + const onResize=function(e: SyntheticEvent, data: any) { + const width = data.size.width || 300; + setSideBoxWidth(width); + }; + return ( +
+ + {children} + +
+ + ); +} + diff --git a/ui/src/pages/playground/router.ts b/ui/src/pages/playground/router.ts new file mode 100644 index 0000000000..9d2190bb79 --- /dev/null +++ b/ui/src/pages/playground/router.ts @@ -0,0 +1,25 @@ +// 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 {renderRoutes} from 'react-router-config'; +function Playground(props) { + return ( + renderRoutes(props.route.routes) + ); +} + +export default Playground; diff --git a/ui/src/pages/playground/tree/index.tsx b/ui/src/pages/playground/tree/index.tsx new file mode 100644 index 0000000000..1baccbad45 --- /dev/null +++ b/ui/src/pages/playground/tree/index.tsx @@ -0,0 +1,125 @@ +/** + * 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 React, {useState,useEffect} from 'react'; +import {Tree, Spin, Space} from 'antd'; +import {TableOutlined, HddOutlined} from '@ant-design/icons'; +import {AdHocAPI} from 'Utils/api'; +import { + AdhocContentRouteKeyEnum, +} from '../adhoc.data'; +interface DataNode { + title: string; + key: string; + isLeaf?: boolean; + children?: DataNode[]; +} + +const initTreeDate: DataNode[] = []; + +function updateTreeData(list: DataNode[], key: React.Key, children: DataNode[]): DataNode[] { + return list.map(node => { + if (node.key === key) { + return { + ...node, + children, + }; + } else if (node.children) { + return { + ...node, + children: updateTreeData(node.children, key, children), + }; + } + return node; + }); +} +export function AdHocTree(props: any) { + + const [treeData, setTreeData] = useState(initTreeDate); + const [loading, setLoading] = useState(true); + useEffect(() => { + AdHocAPI.getDatabaseList().then(res=>{ + if (res.msg === 'success' && Array.isArray(res.data)) { + const treeData = res.data.map((item,index)=>{ + return { + title: item, + key: `1-${index}-${item}`, + icon: , + }; + }); + setTreeData(treeData); + } + setLoading(false); + }); + }, []); + function onLoadData({key, children}) { + const [storey, index, db_name, tbl_name] = key.split('-'); + const param = { + db_name, + tbl_name, + }; + return AdHocAPI.getDatabaseList(param).then(res=>{ + if (res.msg=='success' && Array.isArray(res.data)) { + const treeData = res.data.map((item,index)=>{ + if(storey==1){ + return { + title: item, + key: `2-${index}-${param.db_name}-${item}`, + icon: , + isLeaf: true, + }; + } + + }); + setTreeData(origin => + updateTreeData(origin, key, treeData), + ); + } + }); + + } + function handleTreeSelect( + keys: React.ReactText[], + info: any, + path: AdhocContentRouteKeyEnum = AdhocContentRouteKeyEnum.Result, + ) { + if (keys.length > 0) { + props.history.push(`/Playground/${path}/${keys[0].split(':')[1]}`); + } + } + return ( + <> + + + handleTreeSelect( + selectedKeys, + info, + AdhocContentRouteKeyEnum.Structure ) + } + /> + + ); + +} + diff --git a/ui/src/pages/query-profile/index.tsx b/ui/src/pages/query-profile/index.tsx new file mode 100644 index 0000000000..815dbc24ba --- /dev/null +++ b/ui/src/pages/query-profile/index.tsx @@ -0,0 +1,92 @@ +/** + * 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 React, {useState, useEffect, useRef} from 'react'; +import {Typography, Button, Row, Col} from 'antd'; +const {Text, Title, Paragraph} = Typography; +import {queryProfile} from 'Utils/api'; +import Table from 'Src/components/table'; +import {useHistory} from 'react-router-dom'; +export default function QueryProfile(params: any) { + // const [parentUrl, setParentUrl] = useState(''); + const container = useRef(); + const [allTableData, setAllTableData] = useState({}); + const [profile, setProfile] = useState(); + const history = useHistory(); + const doQueryProfile = function(){ + const param = { + path:location.pathname.slice(14), + }; + queryProfile(param).then(res=>{ + if (res && res.msg === 'success') { + if(!res.data.column_names){ + setProfile(res.data); + container.current.innerHTML=res.data; + }else{ + setProfile(''); + setAllTableData(res.data); + } + } else { + setAllTableData({ + column_names:[], + rows:[], + }); + } + }) + .catch(err=>{ + setAllTableData({ + column_names:[], + rows:[], + }); + }); + }; + useEffect(() => { + doQueryProfile(); + }, [location.pathname]); + function goPrev(){ + if (location.pathname === '/QueryProfile/') {return;} + history.push('/QueryProfile/'); + } + return( + + Finished Queries + + + This table lists the latest 100 queries + + {profile?:''} + + + { + profile + ?
+ {/* {profile} */} +
+ : + } + + + ); +} + diff --git a/ui/src/pages/session/index.tsx b/ui/src/pages/session/index.tsx new file mode 100644 index 0000000000..7b49775af0 --- /dev/null +++ b/ui/src/pages/session/index.tsx @@ -0,0 +1,63 @@ +/** + * 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 React, {useState, useEffect} from 'react'; +import {Typography, Button, Row, Col} from 'antd'; +const {Text, Title, Paragraph} = Typography; +import {getSession} from 'Utils/api'; +import Table from 'Src/components/table'; +// import {useHistory} from 'react-router-dom'; +export default function Session(params: any) { + // const [parentUrl, setParentUrl] = useState(''); + const [allTableData, setAllTableData] = useState({}); + // const history = useHistory(); + const getSessionData = function(){ + getSession({}).then(res=>{ + if (res && res.msg === 'success') { + setAllTableData(res.data); + } + }) + .catch(err=>{ + setAllTableData({ + column_names:[], + rows:[], + }); + }); + }; + useEffect(() => { + getSessionData(); + }, []); + return( + + Session Info + + + This page lists the session info, there are {allTableData?.rows?.length} active sessions. + +
+ + + ); +} + diff --git a/ui/src/pages/system/index.tsx b/ui/src/pages/system/index.tsx new file mode 100644 index 0000000000..6af60da5a9 --- /dev/null +++ b/ui/src/pages/system/index.tsx @@ -0,0 +1,89 @@ +/** + * 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 React, {useState, useEffect} from 'react'; +import {Typography, Button, Row, Col} from 'antd'; +const {Text, Title, Paragraph} = Typography; +import {getSystem} from 'Utils/api'; +import Table from 'Src/components/table'; +import {useHistory} from 'react-router-dom'; +export default function System(params: any) { + const [parentUrl, setParentUrl] = useState(''); + const [allTableData, setAllTableData] = useState({}); + + const history = useHistory(); + const getSystemData = function(){ + const param = { + path:location.search, + }; + getSystem(param).then(res=>{ + if (res && res.msg === 'success') { + setAllTableData(res.data); + setParentUrl(res.data.parent_url); + } else { + setAllTableData({ + column_names:[], + rows:[], + }); + } + }) + .catch(err=>{ + setAllTableData({ + column_names:[], + rows:[], + }); + }); + }; + useEffect(() => { + getSystemData(); + }, [location.search]); + function goPrev(){ + if (parentUrl === '/rest/v1/system') { + history.push('/System?path=/'); + return; + } + if (parentUrl) { + history.push(parentUrl.split('v1/')[1]); + } + } + return( + + System Info + This page lists the system info, like /proc in Linux. + + + + + Current path: {location.search.split('=')[1]} + + + + +
+ + ); +} + diff --git a/ui/src/router/index.ts b/ui/src/router/index.ts new file mode 100644 index 0000000000..c94d28bfb8 --- /dev/null +++ b/ui/src/router/index.ts @@ -0,0 +1,114 @@ +// 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 asyncComponent from '../utils/lazy'; +const Playground = asyncComponent(() => import('../pages/playground')); +const QueryRouter = asyncComponent(() => import('../pages/playground/router')); +const Login = asyncComponent(() => import('../pages/login')); +const Layout = asyncComponent(() => import('../pages/layout')); +const Home = asyncComponent(() => import('../pages/home')); +const System = asyncComponent(() => import('../pages/system')); +const Backend = asyncComponent(() => import('../pages/backend')); +const Logs = asyncComponent(() => import('../pages/logs')); +const QueryProfile = asyncComponent(() => import('../pages/query-profile')); +const Session = asyncComponent(() => import('../pages/session')); +const Configuration = asyncComponent(() => import('../pages/configuration')); +const Ha = asyncComponent(() => import('../pages/ha')); +const Help = asyncComponent(() => import('../pages/help')); +const DataImport = asyncComponent(() => import('../pages/playground/data-import')); + +export default { + routes: [ + { + path: '/login', + component: Login, + title: '登录', + }, + { + path: '/', + component: Layout, + routes: [ + { + path: '/home', + component: Home, + title: '首页', + }, + { + path: '/Playground', + component: QueryRouter, + title: 'Playground', + routes: [ + { + path: '/Playground/import', + component: DataImport, + }, + { + path: '/Playground', + component: Playground, + }, + ], + }, + { + path: '/System', + component: System, + title: 'System', + search: '?path=/', + }, + // { + // path: '/menu/backend', + // component: Backend, + // title: 'backend', + // }, + { + path: '/Log', + component: Logs, + title: 'Log', + }, + { + path: '/QueryProfile', + component: QueryProfile, + title: 'QueryProfile', + }, + { + path: '/Session', + component: Session, + title: 'Session', + }, + { + path: '/Configuration', + component: Configuration, + title: 'Configuration', + }, + // { + // path: '/ha', + // component: Ha, + // title: 'ha', + // }, + // { + // path: '/help', + // component: Help, + // title: 'help', + // }, + ], + }, + { + path: '/', + redirect: '/home', + component: Layout, + }, + ], +}; \ No newline at end of file diff --git a/ui/src/router/renderRouter.tsx b/ui/src/router/renderRouter.tsx new file mode 100644 index 0000000000..a3f8ad2e6e --- /dev/null +++ b/ui/src/router/renderRouter.tsx @@ -0,0 +1,58 @@ +/** + * 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 React from 'react'; +import { Route, Redirect, Switch } from 'react-router-dom'; +import router from '.'; +let isLogin = document.cookie; +const renderRoutes = (routes, authPath = '/login') => { + if(routes){ + return ( + + {routes.map((route, i) => ( + { + if(props.location.pathname === '/'){ + return ; + } + if (isLogin) { + return route.render ? ( + route.render({ ...props, route: route }) + ) : ( + + ) + } else { + isLogin = '1'; + return ; + }} + } + /> + ))} + + ) + } + return null +} + +export default renderRoutes; + diff --git a/ui/src/utils/api.ts b/ui/src/utils/api.ts new file mode 100644 index 0000000000..cf7c9a814f --- /dev/null +++ b/ui/src/utils/api.ts @@ -0,0 +1,127 @@ +// 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 {API_BASE} from 'Constants'; +import request from 'Utils/request'; +import {Result} from '@src/interfaces/http.interface'; +//logout +export function logOut(): Promise> { + return request(`/rest/v1/logout`,{ + method: 'POST', + }); +} +//home +export function getHardwareInfo(): Promise> { + return request(`/rest/v1/hardware_info/fe/`,{ + method: 'GET', + }); +} +//system +export function getSystem(data: any): Promise> { + return request(`/rest/v1/system${data.path}`,{ + method: 'GET', + }); +} +//log +export function getLog(data: any): Promise> { + let localUrl = '/rest/v1/log'; + if(data.add_verbose){ + localUrl = `/rest/v1/log?add_verbose=${data.add_verbose}`; + } + if (data.del_verbose) { + localUrl = `/rest/v1/log?del_verbose=${data.del_verbose}`; + } + // if (data.add_verbose && data.del_verbose) { + // localUrl += `/rest/v1/log?add_verbose=${data.add_verbose}&&del_verbose=${data.del_verbose}`; + // } + return request(localUrl,{ + method: (data.add_verbose || data.del_verbose)?'POST':'GET', + }); +} +//query_profile +export function queryProfile(data: any): Promise> { + let LocalUrl = '/rest/v1/query_profile/'; + if(data.path){ + LocalUrl = `/rest/v1/query_profile/${data.path}`; + } + return request(LocalUrl); +} +//session +export function getSession(data: any): Promise> { + return request('/rest/v1/session'); +} +//config +export function getConfig(data: any): Promise> { + return request('rest/v1/config/fe/'); +} +//query begin +export function getDatabaseList(data: any): Promise> { + let reURL = `${API_BASE}default_cluster/databases`; + if(data){ + if(data.db_name){ + reURL += `/${data.db_name}/tables`; + } + if(data.db_name&&data.tbl_name){ + reURL += `/${data.tbl_name}/schema`; + } + } + return request(reURL); +} +export function doQuery(data: any): Promise> { + return request(`/api/query/default_cluster/${data.db_name}`, { + method: 'POST',...data, + }); +} +export function doUp(data: any): Promise> { + let localHeader = { + label: data.label, + columns: data.columns, + column_separator: data. column_separator + } + if(!localHeader.columns){ + delete localHeader.columns + } + return request(`/api/default_cluster/${data.db_name}/${data.tbl_name}/upload?file_id=${data.file_id}&file_uuid=${data.file_uuid}`, { + method: 'PUT',headers:localHeader, + }); +} +export function getUploadData(data: any): Promise> { + let localUrl = `/api/default_cluster/${data.db_name}/${data.tbl_name}/upload` + if(data.preview){ + localUrl = `/api/default_cluster/${data.db_name}/${data.tbl_name}/upload?file_id=${data.file_id}&file_uuid=${data.file_uuid}&preview=true` + } + return request(localUrl,{ + method: 'GET', + }); +} +export function deleteUploadData(data: any): Promise> { + return request(`/api/default_cluster/${data.db_name}/${data.tbl_name}/upload?file_id=${data.file_id}&file_uuid=${data.file_uuid}`,{ + method: 'DELETE', + }); +} +//query end +export const AdHocAPI = { + getDatabaseList, + doQuery, + doUp, + getLog, + queryProfile, + logOut, + getHardwareInfo, + getUploadData, + deleteUploadData +}; \ No newline at end of file diff --git a/ui/src/utils/lazy.tsx b/ui/src/utils/lazy.tsx new file mode 100644 index 0000000000..df9d483c60 --- /dev/null +++ b/ui/src/utils/lazy.tsx @@ -0,0 +1,44 @@ +/** + * 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 React, {Component} from 'react'; + +const asyncComponent = importComponent => { + return class extends Component { + constructor(props) { + super(props); + this.state = { + component: null + }; + } + componentDidMount() { + importComponent() + .then(cmp => { + this.setState({component: cmp.default}); + }); + } + render() { + const C = this.state.component; + return C ? : null; + } + }; +}; + +export default asyncComponent; + diff --git a/ui/src/utils/request.tsx b/ui/src/utils/request.tsx new file mode 100644 index 0000000000..9a7cf7ba16 --- /dev/null +++ b/ui/src/utils/request.tsx @@ -0,0 +1,116 @@ +/** + * 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. + */ + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +import React from 'react'; +import {message, Modal} from 'antd'; +import {ExclamationCircleOutlined} from '@ant-design/icons'; +import {Trans} from 'react-i18next'; + +function checkStatus(response) { + if (response.status >= 200 && response.status < 300) { + return response; + } + if (response.status === 401) { + Modal.confirm({ + title: tips, + icon: , + content: loginExpirtMsg, + onOk() { + window.location.href = window.location.origin + '/login'; + }, + onCancel() { + // + } + }); + return; + } + const error = new Error(response.statusText); + error.response = response; + message.error(response.statusText); + throw error; +} + +/** + * Requests a URL, returning a promise. + * + * @param {string} url The URL we want to request + * @param {Object} options The parameters to be passed; options may not be passed when GET, the default is {} + * @param {string} options POST, DELETE, GET, PUT + * @param {Object} options Parameters that need to be passed to the backend, such as {id: 1} + * @param {boolean} options Whether to upload a file, if you upload a file, you do not need the default headers and convert the body to a string + * @param {boolean} tipSuccess false: Do not show successful update true: show successful update + * @param {boolean} tipError false: Don't prompt error true: display error message + * @param {boolean} fullResponse false: Whether to return all requested information + * @return {Object} + */ +export default async function request(url, options = {}, tipSuccess = false, tipError = true, fullResponse = false) { + const newOptions = {credentials: 'include', ...options}; + if (newOptions.method === 'POST' || newOptions.method === 'PUT') { + newOptions.headers = newOptions.isUpload + ? { + ...newOptions.headers + } + : { + 'Content-Type': 'application/json; charset=utf-8', + ...newOptions.headers + }; + } + if (typeof newOptions.body === 'object' && !newOptions.isUpload) { + newOptions.body = JSON.stringify(newOptions.body); + } + const response = await fetch(url, newOptions); + if ( + response.url.includes('dataIntegrationApi') + && ( + newOptions.method === 'PUT' + || newOptions.method === 'POST' + || newOptions.method === 'DELETE' + ) + ) { + return response; + } + checkStatus(response); + + if (options && options.download) { + return response.blob(); + } + const data = await response.json(); + if ('code' in data || 'msg' in data) { + const code = data.code; + const msg = data.msg; + if (msg === 'success' || code === 0 || code === 200) { + if (tipSuccess) { + message.success(successfulOperation, msg); + } + } else if (tipError && code !== 0 && msg !== 'success') { + message.error(msg); + } + } + + if (fullResponse) { + return {data, response}; + } + return data; +} + diff --git a/ui/src/utils/utils.ts b/ui/src/utils/utils.ts new file mode 100644 index 0000000000..3355540c64 --- /dev/null +++ b/ui/src/utils/utils.ts @@ -0,0 +1,78 @@ +// 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. + +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +function isSuccess(response) { + if (!response) { + return false; + } + + let status = response.status; + if (status == null) { + status = response.msg; + } + + if (isNaN(status)) { + return status === 'success'; + } + return status === 0; +} +function getDbName(params) { + const infoArr = location.pathname.split('-'); + const db_name = infoArr[0].split('/')[3]; + const tbl_name = infoArr[1]; + const res = {}; + res.db_name = db_name; + res.tbl_name = tbl_name; + return res; +} +function getTimeNow() { + let dateNow = new Date(); + let fmt = 'yyyy-MM-dd hh:mm:ss'; + var o = { + 'M+': dateNow.getMonth() + 1, + 'd+': dateNow.getDate(), + 'h+': dateNow.getHours(), + 'm+': dateNow.getMinutes(), + 's+': dateNow.getSeconds(), + 'q+': Math.floor((dateNow.getMonth() + 3) / 3), + 'S': dateNow.getMilliseconds() + }; + if (/(y+)/.test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + (dateNow.getFullYear() + '').substr(4 - RegExp.$1.length) + ); + } + for (var k in o) { + if (new RegExp('(' + k + ')').test(fmt)) { + fmt = fmt.replace( + RegExp.$1, + RegExp.$1.length === 1 + ? o[k] + : ('00' + o[k]).substr(('' + o[k]).length) + ); + } + } + return fmt; +} + +module.exports = {isSuccess, getDbName, getTimeNow}; \ No newline at end of file diff --git a/ui/tsconfig.json b/ui/tsconfig.json new file mode 100644 index 0000000000..0f07e067e8 --- /dev/null +++ b/ui/tsconfig.json @@ -0,0 +1,59 @@ +{ + "compilerOptions": { + "outDir": "dist", + "module": "commonjs", + "target": "es5", + "lib": ["es7", "dom", "es2015.iterable", "es2019"], + "sourceMap": true, + "allowJs": true, + "jsx": "react", + "moduleResolution": "node", + "forceConsistentCasingInFileNames": false, + "noImplicitReturns": true, + "noImplicitThis": true, + "noImplicitAny": false, + "strictNullChecks": true, + "esModuleInterop": true, + "suppressImplicitAnyIndexErrors": true, + "noUnusedLocals": true, + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "noImplicitUseStrict": true, + "alwaysStrict": false, + "downlevelIteration": true, + "baseUrl": "./", + "paths": { + "Components/*": ["src/components/*"], + "Src": ["src"], + "Utils/*": ["src/utils/*"], + "Models": ["src/models"], + "Services": ["src/services"], + "Constants": ["src/constants"], + "@hooks/*": ["src/hooks/*"], + "@src/*": ["src/*"] + }, + "typeRoots": [ + "./node_modules/@types", + "custom_typings", + "./typings/**/*.d.ts" + ] + }, + "exclude": [ + "build", + "scripts", + "webpack", + "jest", + "bin", + "runtime", + "view", + "public_gen", + "public", + "node_modules", + "coverage", + ".vscode" + ], + "includes": ["src/**/*.ts", "src/**/*.tsx", "global.d.ts"], + "types": ["typePatches"] + } + \ No newline at end of file diff --git a/ui/webpack.config.js b/ui/webpack.config.js new file mode 100644 index 0000000000..9b84a5dc72 --- /dev/null +++ b/ui/webpack.config.js @@ -0,0 +1,30 @@ +/** + * @file test cron + * @author lpx + * @since 2020/08/19 + */ +const devConfig = require('./config/webpack.dev'); +const prodConfig = require('./config/webpack.prod'); + +const SpeedMeasurePlugin = require('speed-measure-webpack-plugin'); + +const smp = new SpeedMeasurePlugin(); + +let config = devConfig; + +switch (process.env.NODE_ENV) { + case 'prod': + case 'production': + config = prodConfig; + break; + case 'dev': + case 'development': + config = devConfig; + break; + default: + config = devConfig; +} + +const webpackConfig = config; + +module.exports = smp.wrap(webpackConfig);