From c5b034acc48434288f8bf593b93a267bb139ebb7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=B9=8F=E7=BF=94?= Date: Thu, 5 Nov 2020 20:30:09 +0800 Subject: [PATCH] [FE UI] Fix some bugs about new FE UI (#4830) 1. Add a search boxer in the left tree view of Playground. 2. Fix some visual bugs of UI. 3. Fix bugs that link failed in QueryProfile view. 4. Fix bugs that cookie is always invalid. 5. Set cookie to HTTP_ONLY to make it more safe. --- .../httpv2/controller/BaseController.java | 2 + ui/public/locales/en-us.json | 4 +- ui/public/locales/zh-cn.json | 4 +- ui/src/components/table/index.tsx | 1 - ui/src/components/table/table.utils.tsx | 6 +- ui/src/pages/layout/index.tsx | 2 +- .../content/components/data-prev.tsx | 92 +++++++----- .../playground/content/content-result.tsx | 4 +- .../playground/content/content-structure.tsx | 1 + ui/src/pages/playground/content/index.less | 1 + ui/src/pages/playground/content/index.tsx | 2 +- ui/src/pages/playground/page-side/index.less | 2 +- ui/src/pages/playground/page-side/index.tsx | 3 +- ui/src/pages/playground/tree/index.css | 3 + ui/src/pages/playground/tree/index.tsx | 140 +++++++++++++++--- ui/src/pages/query-profile/index.tsx | 2 +- 16 files changed, 196 insertions(+), 73 deletions(-) create mode 100644 ui/src/pages/playground/tree/index.css diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java index 3bbea54f02..bd36e3c229 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/controller/BaseController.java @@ -106,6 +106,7 @@ public class BaseController { Cookie cookie = new Cookie(PALO_SESSION_ID, key); cookie.setMaxAge(PALO_SESSION_EXPIRED_TIME); cookie.setPath("/"); + cookie.setHttpOnly(true); response.addCookie(cookie); LOG.debug("add session cookie: {} {}", PALO_SESSION_ID, key); HttpAuthManager.getInstance().addSessionValue(key, value); @@ -170,6 +171,7 @@ public class BaseController { for (Cookie cookie : cookies) { if (cookie.getName() != null && cookie.getName().equals(cookieName)) { cookie.setMaxAge(age); + cookie.setPath("/"); response.addCookie(cookie); LOG.debug("get update cookie: {} {}", cookie.getName(), cookie.getValue()); } diff --git a/ui/public/locales/en-us.json b/ui/public/locales/en-us.json index ba4200138f..97f076d39b 100644 --- a/ui/public/locales/en-us.json +++ b/ui/public/locales/en-us.json @@ -47,5 +47,7 @@ "tips":"Tips", "fileSizeWarning": "File size cannot exceed 100M", "selectWarning": "Please select a table", - "executionTime": "Execution Time" + "executionTime": "Execution Time", + "search":"Search", + "executionFailed":"Execution failed" } diff --git a/ui/public/locales/zh-cn.json b/ui/public/locales/zh-cn.json index 24a7335735..ec1ccf41e1 100644 --- a/ui/public/locales/zh-cn.json +++ b/ui/public/locales/zh-cn.json @@ -47,5 +47,7 @@ "tips": "提示", "fileSizeWarning": "文件大小不能超过100m", "selectWarning": "请选择表", - "executionTime": "执行时间" + "executionTime": "执行时间", + "search":"查询", + "executionFailed": "执行失败" } diff --git a/ui/src/components/table/index.tsx b/ui/src/components/table/index.tsx index f775739e55..8c082b752f 100644 --- a/ui/src/components/table/index.tsx +++ b/ui/src/components/table/index.tsx @@ -50,7 +50,6 @@ export default function SortFilterTable(props: any) { :''} {text}; } - return {text}; + return {text}; } return text === '\\N' ? '-' : text; } export function getColumns(params: string[], isSort: boolean, isInner, hrefColumn, path) { if(!params||params.length === 0){return [];} - let arr = params.map(item=> { + let arr = params.map((item,idx)=> { if (isSort) { return { title: item, dataIndex: item, className: 'pr-25', + key: item+idx, sorter: (a,b)=>sortItems(a, b, item), render:(text, record, index)=>getLinkItem(text,record, index, isInner, item, hrefColumn, path), }; @@ -50,6 +51,7 @@ export function getColumns(params: string[], isSort: boolean, isInner, hrefColum title: item, dataIndex: item, className: 'pr-25', + key: item+idx, render:(text, record, index)=>getLinkItem(text, record, index, isInner, item, hrefColumn, path), }; }); diff --git a/ui/src/pages/layout/index.tsx b/ui/src/pages/layout/index.tsx index 8c14ac4b1a..0ea4ba6257 100644 --- a/ui/src/pages/layout/index.tsx +++ b/ui/src/pages/layout/index.tsx @@ -91,7 +91,7 @@ function Layouts(props: any) { ); return ( -
+
{history.replace('/home');setCurrent('')}}>
diff --git a/ui/src/pages/playground/content/components/data-prev.tsx b/ui/src/pages/playground/content/components/data-prev.tsx index 96ab1e0f29..d3856848ce 100644 --- a/ui/src/pages/playground/content/components/data-prev.tsx +++ b/ui/src/pages/playground/content/components/data-prev.tsx @@ -20,26 +20,68 @@ import React,{useState,useEffect} from 'react'; import {AdHocAPI} from 'Src/api/api'; import {getDbName} from 'Utils/utils'; -import {Row, Empty} from 'antd'; +import {Row, Empty, notification, Table} 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([]); + const [columns,setColumns] = useState([]); function toQuery(): void { + if (!tbl_name){ + notification.error({message: t('selectWarning')}); + return; + } AdHocAPI.doQuery({ db_name, body:{stmt:`SELECT * FROM ${db_name}.${tbl_name} LIMIT 10`}, }).then(res=>{ if (res && res.msg === 'success') { - setTableData(res.data); + console.log(getColumns(res.data?.meta),2222) + setColumns(getColumns(res.data?.meta)) + setTableData(getTabledata(res.data)); } }) .catch(()=>{ setTableData([]); }); } + function getColumns(params: string[]) { + console.log(params,2222) + if(!params||params.length === 0){return [];} + + let arr = params.map(item=> { + return { + title: item.name, + dataIndex: item.name, + key: item.name, + width: 150, + render:(text, record, index)=>{return text === '\\N' ? '-' : text} + }; + }); + return arr; + } + function getTabledata(data){ + let meta = data.meta; + let source = data.data; + let res = []; + if(!source||source.length === 0){return [];} + let metaArr = meta.map(item=>item.name) + for (let i=0;i{ + obj[item] = node[idx] + }) + obj['key'] = i + res.push(obj) + } + return res; + } useEffect(()=>{ toQuery(); },[location.pathname]); @@ -58,42 +100,16 @@ export function DataPrev(props: any) { {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/content-result.tsx b/ui/src/pages/playground/content/content-result.tsx index 627fa99370..4eea84e892 100644 --- a/ui/src/pages/playground/content/content-result.tsx +++ b/ui/src/pages/playground/content/content-result.tsx @@ -123,7 +123,7 @@ export function AdhocContentResult(props) { ) : ( } - text={"执行失败: "+runningQueryInfo.msg +' '+ runningQueryInfo.data} + text={`${t('executionFailed')}: `+runningQueryInfo.msg +' '+ runningQueryInfo.data} color="red" style={{ marginBottom: 10, @@ -141,7 +141,7 @@ export function AdhocContentResult(props) { */} {t('executionTime')}: - {runningQueryInfo.data?.time + ' ms'} + {(runningQueryInfo.data?.time?runningQueryInfo.data?.time:0) + ' ms'} {/* {t('endTime')}: diff --git a/ui/src/pages/playground/content/content-structure.tsx b/ui/src/pages/playground/content/content-structure.tsx index 703a68c449..8aedb22dc2 100644 --- a/ui/src/pages/playground/content/content-structure.tsx +++ b/ui/src/pages/playground/content/content-structure.tsx @@ -86,6 +86,7 @@ export function ContentStructure(props: any) { bordered rowKey='Field' columns={columns} + scroll={{ y: '36vh' }} loading={{ spinning: getTableInfoRequest.loading, delay: TABLE_DELAY, diff --git a/ui/src/pages/playground/content/index.less b/ui/src/pages/playground/content/index.less index 6a60d496d1..ae98a1f044 100644 --- a/ui/src/pages/playground/content/index.less +++ b/ui/src/pages/playground/content/index.less @@ -23,6 +23,7 @@ under the License. */ &-operator { margin-top: 10px; } + width: calc(100vw - 350px); } .adhoc-content-favorite { diff --git a/ui/src/pages/playground/content/index.tsx b/ui/src/pages/playground/content/index.tsx index bc0f035c9e..50c69e8b80 100644 --- a/ui/src/pages/playground/content/index.tsx +++ b/ui/src/pages/playground/content/index.tsx @@ -206,7 +206,7 @@ export function AdHocContent(props: any) { ( -
+
diff --git a/ui/src/pages/playground/page-side/index.less b/ui/src/pages/playground/page-side/index.less index 0a80f52056..e82972e1c8 100644 --- a/ui/src/pages/playground/page-side/index.less +++ b/ui/src/pages/playground/page-side/index.less @@ -19,5 +19,5 @@ under the License. */ flex: 0 0 300px; height: 100%; background: #fff; - min-height: calc(100vh - 60px); + min-height: calc(100vh - 64px); } \ 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 index 570584b3d4..10f3b4a9c4 100644 --- a/ui/src/pages/playground/page-side/index.tsx +++ b/ui/src/pages/playground/page-side/index.tsx @@ -34,7 +34,8 @@ export function PageSide(props: any) {
{ 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) { - + let { t } = useTranslation(); const [treeData, setTreeData] = useState(initTreeDate); + const [realTree, setRealTree] = useState(initTreeDate); const [loading, setLoading] = useState(true); + const [expandedKeys, setExpandedKeys] = useState([]); + const [searchValue, setSearchValue] = useState(''); + const [autoExpandParent, setAutoExpandParent] = useState(true); useEffect(() => { + initTreeData() + }, []); + function initTreeData(){ AdHocAPI.getDatabaseList().then(res=>{ if (res.msg === 'success' && Array.isArray(res.data)) { + const num = Math.random() const treeData = res.data.map((item,index)=>{ return { title: item, - key: `1-${index}-${item}`, + key: `${num}-1-${index}-${item}`, icon: , }; }); setTreeData(treeData); + getRealTree(treeData); } setLoading(false); }); - }, []); + } function onLoadData({key, children}) { - const [storey, index, db_name, tbl_name] = key.split('-'); + const [random, storey, index, db_name] = key.split('-'); const param = { db_name, - tbl_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){ + const children = res.data.map((item,index)=>{ + if (storey === '1'){ return { title: item, key: `2-${index}-${param.db_name}-${item}`, @@ -86,9 +91,9 @@ export function AdHocTree(props: any) { } }); - setTreeData(origin => - updateTreeData(origin, key, treeData), - ); + const trData = updateTreeData(treeData, key, children); + setTreeData(trData); + getRealTree(trData); } }); @@ -102,14 +107,103 @@ export function AdHocTree(props: any) { props.history.push(`/Playground/${path}/${keys[0].split(':')[1]}`); } } + function onSearch(e){ + const { value } = e.target; + const expandedKeys = treeData + .map((item, index) => { + if (getParentKey(value, treeData[index].children, index)) { + return item.key + } else { + return null; + } + }) + setExpandedKeys(expandedKeys); + setSearchValue(value); + setAutoExpandParent(true); + getRealTree(treeData, value); + }; + function onExpand(expandedKeys) { + setExpandedKeys(expandedKeys); + setAutoExpandParent(false); + }; + const getParentKey = (key, tree, idx) => { + if (!tree) { + return false; + } + for (let i = 0; i < tree.length; i++) { + const node = tree[i]; + if (node.title.includes(key)) { + return true + } else { + treeData[idx].children ? treeData[idx].children[i].title = node.title : '' + } + } + return false; + }; + function getRealTree(treeData, value){ + const realTree = inner(treeData); + function inner(treeData){ + return treeData.map(item => { + const search = value || ''; + const index = item.title.indexOf(search); + const beforeStr = item.title.substr(0, index); + const afterStr = item.title.substr(index + search.length); + const title = + index > -1 ? ( + + {beforeStr} + {search} + {afterStr} + + ) : ( + item.title + ); + if (item.children) { + return {...item, title, children: inner(item.children)}; + } + return { + ...item, + title + }; + }); + } + debounce(setRealTree(realTree),300); + } + function debounce(fn, wait) { + var timer = null; + return function () { + var context = this + var args = arguments + if (timer) { + clearTimeout(timer); + timer = null; + } + timer = setTimeout(function () { + fn.apply(context, args) + }, wait) + } + } return ( <> +
+ } + onSearch={initTreeData} + onChange={onSearch} /> +
+ handleTreeSelect( selectedKeys, diff --git a/ui/src/pages/query-profile/index.tsx b/ui/src/pages/query-profile/index.tsx index 7beb2aea16..2b296cd233 100644 --- a/ui/src/pages/query-profile/index.tsx +++ b/ui/src/pages/query-profile/index.tsx @@ -86,7 +86,7 @@ export default function QueryProfile(params: any) { :
}