feat: add watch api in cluster
This commit is contained in:
@@ -14,11 +14,15 @@ const CardStyled = styled(Card)`
|
||||
}
|
||||
}
|
||||
.ant-card-head {
|
||||
cursor: pointer;
|
||||
background-color: var(--ant-color-fill-quaternary);
|
||||
border-bottom: none;
|
||||
border-radius: var(--ant-border-radius);
|
||||
&:hover {
|
||||
background-color: var(--ant-color-fill-secondary);
|
||||
.del-btn {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
@@ -28,12 +32,17 @@ const useStyles = createStyles(({ css, token }) => {
|
||||
title: css`
|
||||
font-weight: 400;
|
||||
height: 56px;
|
||||
font-size: ${token.fontSizeLG};
|
||||
font-size: var(--font-size-base);
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
`,
|
||||
expandIcon: css`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
`,
|
||||
subtitle: css`
|
||||
font-size: 12px;
|
||||
color: ${token.colorTextSecondary};
|
||||
@@ -48,6 +57,9 @@ const useStyles = createStyles(({ css, token }) => {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
.del-btn {
|
||||
display: none;
|
||||
}
|
||||
`
|
||||
};
|
||||
});
|
||||
@@ -56,6 +68,7 @@ export interface CollapsibleContainerProps {
|
||||
title?: React.ReactNode;
|
||||
subtitle?: React.ReactNode;
|
||||
right?: React.ReactNode;
|
||||
deleteBtn?: React.ReactNode;
|
||||
defaultOpen?: boolean;
|
||||
open?: boolean;
|
||||
collapsible?: boolean;
|
||||
@@ -70,6 +83,7 @@ export default function CollapsibleContainer({
|
||||
title,
|
||||
subtitle,
|
||||
right,
|
||||
deleteBtn,
|
||||
defaultOpen = true,
|
||||
open,
|
||||
onToggle,
|
||||
@@ -105,19 +119,22 @@ export default function CollapsibleContainer({
|
||||
return (
|
||||
<div className={styles.title} onClick={toggle}>
|
||||
<div className={styles.left}>
|
||||
{title && <div>{title}</div>}
|
||||
<div className={styles.expandIcon}>
|
||||
<IconFont
|
||||
rotate={isOpen ? 180 : 0}
|
||||
type="icon-down"
|
||||
style={{
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
fontSize: 12
|
||||
}}
|
||||
/>
|
||||
{title && <div>{title}</div>}
|
||||
</div>
|
||||
{subtitle && <div className={styles.subtitle}>{subtitle}</div>}
|
||||
</div>
|
||||
<div className={styles.right}>
|
||||
{right}
|
||||
<IconFont
|
||||
rotate={isOpen ? 180 : 0}
|
||||
type="icon-down"
|
||||
style={{
|
||||
cursor: disabled ? 'not-allowed' : 'pointer',
|
||||
fontSize: 12
|
||||
}}
|
||||
/>
|
||||
{right && <span>{right}</span>}
|
||||
{deleteBtn && <span className="del-btn">{deleteBtn}</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
import { CheckOutlined, FormOutlined, UndoOutlined } from '@ant-design/icons';
|
||||
import { useIntl } from '@umijs/max';
|
||||
import { Button, Input, InputNumber, Tooltip } from 'antd';
|
||||
import _ from 'lodash';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import RowContext from '../row-context';
|
||||
import { CellContentProps } from '../types';
|
||||
|
||||
const CellContentWrapper = styled.div`
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
`;
|
||||
|
||||
interface EditButtonsProps {
|
||||
isEditing: boolean;
|
||||
editable?: any;
|
||||
handleSubmit: () => void;
|
||||
handleUndo: () => void;
|
||||
handleEdit: () => void;
|
||||
}
|
||||
|
||||
interface ContentProps {
|
||||
isEditing: boolean;
|
||||
current: any;
|
||||
editable: any;
|
||||
onChange: (val: any) => void;
|
||||
row?: any;
|
||||
render?: (text: any, record: any) => React.ReactNode;
|
||||
}
|
||||
|
||||
const EditButtons: React.FC<EditButtonsProps> = (props) => {
|
||||
const intl = useIntl();
|
||||
const { isEditing, editable, handleSubmit, handleUndo, handleEdit } = props;
|
||||
if (!editable) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
return (
|
||||
<span className="flex-column">
|
||||
<Tooltip
|
||||
key="confirm"
|
||||
title={intl.formatMessage({ id: 'common.button.confirm' })}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="m-l-10"
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<CheckOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
title={intl.formatMessage({ id: 'common.button.cancel' })}
|
||||
key="undo"
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="m-l-10"
|
||||
onClick={handleUndo}
|
||||
>
|
||||
<UndoOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="flex-column">
|
||||
<Tooltip
|
||||
key="edit"
|
||||
title={
|
||||
_.isBoolean(editable) ? (
|
||||
intl.formatMessage({ id: 'common.button.edit' })
|
||||
) : (
|
||||
<span>{editable.title || ''}</span>
|
||||
)
|
||||
}
|
||||
>
|
||||
<Button
|
||||
type="text"
|
||||
size="small"
|
||||
className="m-l-10"
|
||||
onClick={handleEdit}
|
||||
>
|
||||
<FormOutlined />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
const Content: React.FC<ContentProps> = (props) => {
|
||||
const { editable, current, isEditing, row, render, onChange } = props;
|
||||
if (isEditing && editable) {
|
||||
const isNumType =
|
||||
typeof editable === 'object' && editable?.valueType === 'number';
|
||||
return isNumType ? (
|
||||
<InputNumber
|
||||
style={{ width: '80px' }}
|
||||
min={0}
|
||||
value={current}
|
||||
onChange={onChange}
|
||||
/>
|
||||
) : (
|
||||
<Input value={current} onChange={(e) => onChange(e.target.value)} />
|
||||
);
|
||||
}
|
||||
|
||||
if (render) {
|
||||
return render(current, row);
|
||||
}
|
||||
return current;
|
||||
};
|
||||
|
||||
const CellContent: React.FC<CellContentProps> = (props) => {
|
||||
const { row, onCell } = useContext(RowContext);
|
||||
const { dataIndex, render, editable } = props;
|
||||
const [isEditing, setIsEditing] = React.useState(false);
|
||||
const [current, setCurrent] = React.useState(row[dataIndex]);
|
||||
const cachedValue = React.useRef(null);
|
||||
|
||||
const handleEdit = () => {
|
||||
setIsEditing(true);
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
cachedValue.current = current;
|
||||
await onCell?.(
|
||||
{
|
||||
...row,
|
||||
[dataIndex]: current
|
||||
},
|
||||
dataIndex
|
||||
);
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
const handleUndo = () => {
|
||||
setCurrent(cachedValue.current);
|
||||
setIsEditing(false);
|
||||
};
|
||||
|
||||
const handleValueChange = (val: any) => {
|
||||
setCurrent(val);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
cachedValue.current = row[dataIndex];
|
||||
setCurrent(row[dataIndex]);
|
||||
}, [row[dataIndex]]);
|
||||
|
||||
return (
|
||||
<CellContentWrapper>
|
||||
<Content
|
||||
onChange={handleValueChange}
|
||||
isEditing={isEditing}
|
||||
editable={editable}
|
||||
current={current}
|
||||
row={row}
|
||||
render={render}
|
||||
></Content>
|
||||
<EditButtons
|
||||
editable={editable}
|
||||
isEditing={isEditing}
|
||||
handleEdit={handleEdit}
|
||||
handleSubmit={handleSubmit}
|
||||
handleUndo={handleUndo}
|
||||
></EditButtons>
|
||||
</CellContentWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default CellContent;
|
||||
@@ -18,6 +18,7 @@ const Header: React.FC<HeaderProps> = (props) => {
|
||||
title,
|
||||
dataIndex,
|
||||
align,
|
||||
width,
|
||||
span,
|
||||
headerStyle,
|
||||
sortOrder,
|
||||
@@ -31,6 +32,7 @@ const Header: React.FC<HeaderProps> = (props) => {
|
||||
sorter={sorter}
|
||||
dataIndex={dataIndex}
|
||||
sortOrder={sortOrder}
|
||||
width={width}
|
||||
defaultSortOrder={defaultSortOrder}
|
||||
title={title}
|
||||
style={headerStyle}
|
||||
|
||||
+43
-13
@@ -4,9 +4,34 @@ import { Button, Input, InputNumber, Tooltip } from 'antd';
|
||||
import classNames from 'classnames';
|
||||
import _ from 'lodash';
|
||||
import React, { useContext, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import RowContext from '../row-context';
|
||||
import '../styles/cell.less';
|
||||
import { SealColumnProps } from '../types';
|
||||
import CellContent from './cell-content';
|
||||
|
||||
const CellWrapper = styled.div`
|
||||
padding: var(--ant-table-cell-padding-block)
|
||||
var(--ant-table-cell-padding-inline);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-height: 68px;
|
||||
word-break: break-word;
|
||||
min-width: 20px;
|
||||
overflow: hidden;
|
||||
|
||||
&.left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&.center {
|
||||
justify-content: center;
|
||||
}
|
||||
`;
|
||||
|
||||
interface EditButtonsProps {
|
||||
isEditing: boolean;
|
||||
@@ -89,7 +114,7 @@ const EditButtons: React.FC<EditButtonsProps> = (props) => {
|
||||
);
|
||||
};
|
||||
|
||||
const CellContent: React.FC<CellContentProps> = (props) => {
|
||||
const Content: React.FC<CellContentProps> = (props) => {
|
||||
const { editable, current, isEditing, row, render, onChange } = props;
|
||||
if (isEditing && editable) {
|
||||
const isNumType =
|
||||
@@ -112,7 +137,7 @@ const CellContent: React.FC<CellContentProps> = (props) => {
|
||||
return current;
|
||||
};
|
||||
|
||||
const SealColumn: React.FC<SealColumnProps> = (props) => {
|
||||
const TableCell: React.FC<SealColumnProps> = (props) => {
|
||||
const { row, onCell } = useContext(RowContext);
|
||||
const { dataIndex, render, align, editable } = props;
|
||||
const [isEditing, setIsEditing] = React.useState(false);
|
||||
@@ -150,22 +175,22 @@ const SealColumn: React.FC<SealColumnProps> = (props) => {
|
||||
}, [row[dataIndex]]);
|
||||
|
||||
return (
|
||||
<div
|
||||
<CellWrapper
|
||||
className={classNames('cell', {
|
||||
'cell-left': align === 'left',
|
||||
'cell-center': align === 'center',
|
||||
'cell-right': align === 'right'
|
||||
left: align === 'left',
|
||||
center: align === 'center',
|
||||
right: align === 'right'
|
||||
})}
|
||||
>
|
||||
<span className="cell-content flex-center">
|
||||
<CellContent
|
||||
{/* <span className="cell-content flex-center">
|
||||
<Content
|
||||
onChange={handleValueChange}
|
||||
isEditing={isEditing}
|
||||
editable={editable}
|
||||
current={current}
|
||||
row={row}
|
||||
render={render}
|
||||
></CellContent>
|
||||
></Content>
|
||||
<EditButtons
|
||||
editable={editable}
|
||||
isEditing={isEditing}
|
||||
@@ -173,9 +198,14 @@ const SealColumn: React.FC<SealColumnProps> = (props) => {
|
||||
handleSubmit={handleSubmit}
|
||||
handleUndo={handleUndo}
|
||||
></EditButtons>
|
||||
</span>
|
||||
</div>
|
||||
</span> */}
|
||||
<CellContent
|
||||
dataIndex={dataIndex}
|
||||
render={render}
|
||||
editable={editable}
|
||||
></CellContent>
|
||||
</CellWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default SealColumn;
|
||||
export default TableCell;
|
||||
@@ -15,6 +15,7 @@ const TableHeader: React.FC<TableHeaderProps> = (props) => {
|
||||
sortOrder,
|
||||
onSort,
|
||||
sorter,
|
||||
width,
|
||||
dataIndex
|
||||
} = props;
|
||||
|
||||
@@ -27,7 +28,7 @@ const TableHeader: React.FC<TableHeaderProps> = (props) => {
|
||||
};
|
||||
return (
|
||||
<div
|
||||
style={{ ...style }}
|
||||
style={{ width, ...style }}
|
||||
className={classNames('table-header', {
|
||||
'table-header-left': align === 'left',
|
||||
'table-header-center': align === 'center',
|
||||
|
||||
@@ -8,7 +8,7 @@ import RowContext from '../row-context';
|
||||
import TableContext from '../table-context';
|
||||
import { RowContextProps, SealTableProps } from '../types';
|
||||
import RowPrefix from './row-prefix';
|
||||
import TableColumn from './seal-column';
|
||||
import TableCell from './table-cell';
|
||||
|
||||
const TableRow: React.FC<
|
||||
RowContextProps &
|
||||
@@ -209,7 +209,7 @@ const TableRow: React.FC<
|
||||
key={`${restProps.dataIndex}-${rowIndex}`}
|
||||
span={restProps.span}
|
||||
>
|
||||
<TableColumn {...restProps}></TableColumn>
|
||||
<TableCell {...restProps}></TableCell>
|
||||
</Col>
|
||||
);
|
||||
})}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import React from 'react';
|
||||
|
||||
const RowContext = React.createContext<any>({});
|
||||
interface RowContextType {
|
||||
row: Record<string, any>;
|
||||
onCell?: (record: any, dataIndex: string) => any;
|
||||
}
|
||||
|
||||
const RowContext = React.createContext<RowContextType>({} as RowContextType);
|
||||
|
||||
export default RowContext;
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
&-left {
|
||||
&.left {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
&-right {
|
||||
&.right {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
&-center {
|
||||
&.center {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
import React from 'react';
|
||||
|
||||
export interface CellContentProps {
|
||||
dataIndex: string;
|
||||
render?: (text: any, record: any) => React.ReactNode;
|
||||
editable?:
|
||||
| boolean
|
||||
| {
|
||||
valueType?: 'text' | 'number' | 'date' | 'datetime' | 'time';
|
||||
title?: React.ReactNode;
|
||||
};
|
||||
}
|
||||
|
||||
export interface SealColumnProps {
|
||||
title: React.ReactNode;
|
||||
render?: (text: any, record: any) => React.ReactNode;
|
||||
@@ -33,6 +44,7 @@ export interface TableHeaderProps {
|
||||
firstCell?: boolean;
|
||||
lastCell?: boolean;
|
||||
align?: 'left' | 'center' | 'right';
|
||||
width?: number | string;
|
||||
}
|
||||
|
||||
export interface RowSelectionProps {
|
||||
|
||||
@@ -20,6 +20,7 @@ export const StatusMaps = {
|
||||
};
|
||||
|
||||
type StatusTagProps = {
|
||||
style?: React.CSSProperties;
|
||||
statusValue: {
|
||||
status: StatusType;
|
||||
text: string;
|
||||
@@ -42,6 +43,7 @@ type StatusTagProps = {
|
||||
};
|
||||
|
||||
const StatusTag: React.FC<StatusTagProps> = ({
|
||||
style,
|
||||
statusValue,
|
||||
download,
|
||||
extra,
|
||||
@@ -149,7 +151,8 @@ const StatusTag: React.FC<StatusTagProps> = ({
|
||||
})}
|
||||
style={{
|
||||
color: statusColor?.text,
|
||||
border: `1px solid ${statusColor?.border || statusColor?.text}`
|
||||
border: `1px solid ${statusColor?.border || statusColor?.text}`,
|
||||
...style
|
||||
}}
|
||||
>
|
||||
{statusValue.message ? (
|
||||
|
||||
@@ -70,12 +70,12 @@ const Icon = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 16px;
|
||||
font-size: 46px;
|
||||
font-size: 32px;
|
||||
`;
|
||||
|
||||
const Header = styled.div`
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
font-size: var(--font-size-base);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
|
||||
Reference in New Issue
Block a user