From 60a2809509003f1c94de1dac3d98894a1f5a4d17 Mon Sep 17 00:00:00 2001 From: Zero Date: Sat, 11 Dec 2021 01:05:25 +0800 Subject: [PATCH 1/2] feat(common): add card list and refactor msp overview feat(common): add card list and refactor msp overview --- .../app/common/components/card-list/index.tsx | 114 +++++++++++++ shell/app/common/index.ts | 2 + .../msp/pages/micro-service/overview.tsx | 157 +++++++++--------- 3 files changed, 199 insertions(+), 74 deletions(-) create mode 100644 shell/app/common/components/card-list/index.tsx diff --git a/shell/app/common/components/card-list/index.tsx b/shell/app/common/components/card-list/index.tsx new file mode 100644 index 0000000000..91b4bba4ca --- /dev/null +++ b/shell/app/common/components/card-list/index.tsx @@ -0,0 +1,114 @@ +// Copyright (c) 2021 Terminus, Inc. +// +// This program is free software: you can use, redistribute, and/or modify +// it under the terms of the GNU Affero General Public License, version 3 +// or later ("AGPL"), as published by the Free Software Foundation. +// +// This program is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +import React from 'react'; +import { Col, Row, Spin } from 'antd'; +import { RowProps } from 'antd/es/row'; +import { ColProps } from 'antd/es/col'; +import classnames from 'classnames'; + +interface CardColumnsProps { + dataIndex: keyof T; + colProps?: ColProps; + render?: (text: any, record: T, index: number) => React.ReactNode; + children?: { + rowProps?: RowProps; + columns: CardColumnsProps[]; + }; +} + +interface IProps> { + size?: 'default' | 'small'; + loading?: boolean; + rowKey?: string | ((record: T) => string); + rowClick?: (record: T) => void; + dataSource: T[]; + slot?: React.ReactNode; + rowClassName?: string; + columns: CardColumnsProps[]; +} + +const renderChild = (record: T, columns: CardColumnsProps[], index: number) => { + return columns.map((column) => { + let nodes: React.ReactNode = record[column.dataIndex]; + if (column.render) { + nodes = column.render(nodes, record, index); + } + if (column.children?.columns.length) { + nodes = ( + + {renderChild(record, column.children.columns, index)} + + ); + } + return ( + + {nodes} + + ); + }); +}; + +const CardList = ({ + loading, + dataSource, + rowKey = 'key', + rowClassName, + columns, + rowClick, + slot, + size = 'default', +}: IProps) => { + return ( +
+
+
{slot}
+
+
+ + {dataSource.map((record, index) => { + let rowId; + if (typeof rowKey === 'function') { + rowId = rowKey(record); + } else { + rowId = record[rowKey]; + } + const rowClass = classnames( + 'card-shadow mb-2 mx-2 px-4 rounded-sm transition-all duration-300 hover:bg-grey', + { + 'py-8': size === 'default', + 'py-6': size === 'small', + [rowClassName as string]: !!rowClassName, + 'cursor-pointer': rowClick, + }, + ); + return ( + { + rowClick?.(record); + }} + key={rowId} + className={rowClass} + > + {renderChild(record, columns, index)} + + ); + })} + +
+
+ ); +}; + +export default CardList; +export { CardColumnsProps }; diff --git a/shell/app/common/index.ts b/shell/app/common/index.ts index 0c8b3f375f..954012e204 100644 --- a/shell/app/common/index.ts +++ b/shell/app/common/index.ts @@ -109,3 +109,5 @@ export { default as Intro } from './components/intro'; export { default as ErdaAlert } from './components/erda-alert'; export { default as Badge } from './components/badge'; export type { IBadgeProps } from './components/badge'; +export { default as CardList } from './components/card-list'; +export type { CardColumnsProps } from './components/card-list'; diff --git a/shell/app/modules/msp/pages/micro-service/overview.tsx b/shell/app/modules/msp/pages/micro-service/overview.tsx index ebd4ba97b4..34db25b3b1 100644 --- a/shell/app/modules/msp/pages/micro-service/overview.tsx +++ b/shell/app/modules/msp/pages/micro-service/overview.tsx @@ -12,10 +12,9 @@ // along with this program. If not, see . import React from 'react'; -import { Col, Input, Row, Spin, Tag } from 'antd'; +import { Input, Tag } from 'antd'; import { useUpdate } from 'common/use-hooks'; import { getMspProjectList } from 'msp/services'; -import EmptyHolder from 'common/components/empty-holder'; import ErdaIcon from 'common/components/erda-icon'; import { fromNow, goTo } from 'common/utils'; import { debounce, last } from 'lodash'; @@ -28,6 +27,7 @@ import middleImg from 'app/images/msp/microservice-governance-middle.svg'; import bottomImg from 'app/images/msp/microservice-governance-bottom.svg'; import backgroundImg from 'app/images/msp/microservice-governance-background.svg'; import decorationImg from 'app/images/msp/microservice-governance-decoration.svg'; +import CardList, { CardColumnsProps } from 'common/components/card-list'; import i18n from 'i18n'; import './overview.scss'; @@ -56,6 +56,29 @@ const iconMap: { }, }; +const metric: { dataIndex: keyof MS_INDEX.IMspProject; name: string; renderData: (data: any) => React.ReactNode }[] = [ + { + dataIndex: 'relationship', + name: i18n.t('env'), + renderData: (data: MS_INDEX.IMspRelationship[]) => data.length, + }, + { + dataIndex: 'serviceCount', + name: i18n.t('service'), + renderData: (data: number) => data ?? 0, + }, + { + dataIndex: 'last24hAlertCount', + name: i18n.t('msp:last 1 day alarm'), + renderData: (data: number) => data ?? 0, + }, + { + dataIndex: 'lastActiveTime', + name: i18n.t('msp:last active time'), + renderData: (data: number) => (data ? fromNow(data) : '-'), + }, +]; + const Overview = () => { const [{ data, loading, filterKey }, updater] = useUpdate({ data: [], @@ -95,6 +118,52 @@ const Overview = () => { return data.filter((item) => item.displayName.toLowerCase().includes(filterKey)); }, [data, filterKey]); + const columns: CardColumnsProps[] = [ + { + dataIndex: 'displayName', + colProps:{ + className: 'flex items-center', + }, + render: (displayName: string, { logo, desc, type }) => { + const { icon, color, tag } = iconMap[type]; + return ( + <> +
+ {logo ? : } +
+
+

{displayName}

+ + {tag} + +
{desc || i18n.t('no description yet')}
+
+ + ); + }, + }, + { + dataIndex: 'id', + colProps:{ + className: 'flex items-center', + }, + children: { + columns: metric.map((item) => ({ + dataIndex: item.dataIndex, + colProps:{ + span: 6 + }, + render: (text) => ( + <> +

{item.renderData(text)}

+

{item.name}

+ + ), + })), + }, + }, + ]; + return (
@@ -120,87 +189,27 @@ const Overview = () => {
-
-
+ + rowKey="id" + loading={loading} + columns={columns} + dataSource={list} + rowClick={({ relationship, id }) => { + handleClick(relationship, id); + }} + slot={ } bordered={false} allowClear placeholder={i18n.t('msp:search by project name')} - className="bg-hover-gray-bg mb-3 w-72" + className="bg-hover-gray-bg w-72" onChange={(e) => { handleSearch(e.target.value); }} /> -
-
- - {list.length ? ( - list.map( - ({ - type, - desc, - displayName, - id, - relationship, - serviceCount, - last24hAlertCount, - lastActiveTime, - logo, - }) => { - const { icon, color, tag } = iconMap[type]; - return ( - { - handleClick(relationship, id); - }} - > - -
- {logo ? : } -
-
-

{displayName}

- - {tag} - -
{desc || i18n.t('no description yet')}
-
- - - - -

{relationship.length}

-

{i18n.t('env')}

- - -

{serviceCount ?? 0}

-

{i18n.t('service')}

- - -

{last24hAlertCount ?? 0}

-

{i18n.t('msp:last 1 day alarm')}

- - -

- {lastActiveTime ? fromNow(lastActiveTime) : '-'} -

-

{i18n.t('msp:last active time')}

- -
- -
- ); - }, - ) - ) : ( - - )} -
-
-
+ } + /> ); }; From c0e6a0f110492adcac0e81963922b0d30b552dc1 Mon Sep 17 00:00:00 2001 From: Zero Date: Mon, 13 Dec 2021 21:38:36 +0800 Subject: [PATCH 2/2] feat(common): add empty holdere feat(common): adjust shadow color --- .../app/common/components/card-list/index.tsx | 64 ++++++++++--------- .../msp/pages/micro-service/overview.tsx | 9 +-- shell/app/styles/_color.scss | 2 +- shell/app/styles/util.scss | 4 +- 4 files changed, 43 insertions(+), 36 deletions(-) diff --git a/shell/app/common/components/card-list/index.tsx b/shell/app/common/components/card-list/index.tsx index 91b4bba4ca..9067031d2b 100644 --- a/shell/app/common/components/card-list/index.tsx +++ b/shell/app/common/components/card-list/index.tsx @@ -16,6 +16,7 @@ import { Col, Row, Spin } from 'antd'; import { RowProps } from 'antd/es/row'; import { ColProps } from 'antd/es/col'; import classnames from 'classnames'; +import EmptyHolder from 'common/components/empty-holder'; interface CardColumnsProps { dataIndex: keyof T; @@ -28,7 +29,7 @@ interface CardColumnsProps { } interface IProps> { - size?: 'default' | 'small'; + size?: 'default' | 'small' | 'large'; loading?: boolean; rowKey?: string | ((record: T) => string); rowClick?: (record: T) => void; @@ -36,6 +37,7 @@ interface IProps> { slot?: React.ReactNode; rowClassName?: string; columns: CardColumnsProps[]; + emptyHolder?: React.ReactNode; } const renderChild = (record: T, columns: CardColumnsProps[], index: number) => { @@ -68,6 +70,7 @@ const CardList = ({ rowClick, slot, size = 'default', + emptyHolder, }: IProps) => { return (
@@ -76,34 +79,37 @@ const CardList = ({
- {dataSource.map((record, index) => { - let rowId; - if (typeof rowKey === 'function') { - rowId = rowKey(record); - } else { - rowId = record[rowKey]; - } - const rowClass = classnames( - 'card-shadow mb-2 mx-2 px-4 rounded-sm transition-all duration-300 hover:bg-grey', - { - 'py-8': size === 'default', - 'py-6': size === 'small', - [rowClassName as string]: !!rowClassName, - 'cursor-pointer': rowClick, - }, - ); - return ( - { - rowClick?.(record); - }} - key={rowId} - className={rowClass} - > - {renderChild(record, columns, index)} - - ); - })} + {dataSource.length + ? dataSource.map((record, index) => { + let rowId; + if (typeof rowKey === 'function') { + rowId = rowKey(record); + } else { + rowId = record[rowKey]; + } + const rowClass = classnames( + 'card-shadow mb-4 mx-2 px-4 rounded-sm transition-all duration-300 hover:bg-grey', + { + 'py-8': size === 'large', + 'py-6': size === 'default', + 'py-4': size === 'small', + [rowClassName as string]: !!rowClassName, + 'cursor-pointer': rowClick, + }, + ); + return ( + { + rowClick?.(record); + }} + key={rowId} + className={rowClass} + > + {renderChild(record, columns, index)} + + ); + }) + : emptyHolder || }
diff --git a/shell/app/modules/msp/pages/micro-service/overview.tsx b/shell/app/modules/msp/pages/micro-service/overview.tsx index 34db25b3b1..cfec25dc5a 100644 --- a/shell/app/modules/msp/pages/micro-service/overview.tsx +++ b/shell/app/modules/msp/pages/micro-service/overview.tsx @@ -121,7 +121,7 @@ const Overview = () => { const columns: CardColumnsProps[] = [ { dataIndex: 'displayName', - colProps:{ + colProps: { className: 'flex items-center', }, render: (displayName: string, { logo, desc, type }) => { @@ -144,14 +144,14 @@ const Overview = () => { }, { dataIndex: 'id', - colProps:{ + colProps: { className: 'flex items-center', }, children: { columns: metric.map((item) => ({ dataIndex: item.dataIndex, - colProps:{ - span: 6 + colProps: { + span: 6, }, render: (text) => ( <> @@ -191,6 +191,7 @@ const Overview = () => { rowKey="id" + size="large" loading={loading} columns={columns} dataSource={list} diff --git a/shell/app/styles/_color.scss b/shell/app/styles/_color.scss index 20968030eb..0bb0cc822d 100644 --- a/shell/app/styles/_color.scss +++ b/shell/app/styles/_color.scss @@ -136,7 +136,7 @@ $color-input-bg: #efeef0; // color new list $color-box-shadow: rgba($color-text-sub, 0.1); -$color-card-shadow: rgba(85, 77, 104, 0.08); +$color-card-shadow: rgba(48, 38, 71, 0.16); $color-card-hover-shadow: rgba(85, 77, 104, 0.16); // new assistant color diff --git a/shell/app/styles/util.scss b/shell/app/styles/util.scss index 13c15e5c29..3f8b1be0c9 100644 --- a/shell/app/styles/util.scss +++ b/shell/app/styles/util.scss @@ -343,10 +343,10 @@ } .card-shadow { - box-shadow: 0 2px 4px 0 $color-card-shadow; + box-shadow: 0 1px 4px 0 $color-card-shadow; &:hover { - box-shadow: 0 2px 4px 0 $color-card-hover-shadow; + box-shadow: 0 2px 4px 0 $color-card-shadow; } }