style: area chart
This commit is contained in:
@@ -0,0 +1,79 @@
|
|||||||
|
import { Area } from '@ant-design/plots';
|
||||||
|
import EmptyData from './empty-data';
|
||||||
|
|
||||||
|
interface LineChartProps {
|
||||||
|
title?: string;
|
||||||
|
height?: number;
|
||||||
|
data: any[];
|
||||||
|
color?: string[];
|
||||||
|
xField?: string;
|
||||||
|
yField?: string;
|
||||||
|
locale?: string;
|
||||||
|
labelFormatter?: (v: any) => string;
|
||||||
|
slider?: any;
|
||||||
|
}
|
||||||
|
const LineChart: React.FC<LineChartProps> = (props) => {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
title,
|
||||||
|
color,
|
||||||
|
xField,
|
||||||
|
locale,
|
||||||
|
yField,
|
||||||
|
slider,
|
||||||
|
height,
|
||||||
|
labelFormatter
|
||||||
|
} = props;
|
||||||
|
const config = {
|
||||||
|
title,
|
||||||
|
height,
|
||||||
|
xField: xField || 'time',
|
||||||
|
yField: yField || 'value',
|
||||||
|
autoFit: true,
|
||||||
|
slider,
|
||||||
|
shapeField: 'smooth',
|
||||||
|
axis: {
|
||||||
|
x: {
|
||||||
|
textStyle: {
|
||||||
|
autoRoate: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
tick: false,
|
||||||
|
// size: 14,
|
||||||
|
// title: '%',
|
||||||
|
titlePosition: 'top',
|
||||||
|
titleFontSize: 12,
|
||||||
|
labelFormatter
|
||||||
|
}
|
||||||
|
},
|
||||||
|
style: {
|
||||||
|
fill: 'rgba(84, 204, 152,0.8)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
color: {
|
||||||
|
layout: { justifyContent: 'center' }
|
||||||
|
},
|
||||||
|
|
||||||
|
size: {
|
||||||
|
itemLabelFontSize: 14,
|
||||||
|
itemLabelFontWeight: 500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
title: 'time',
|
||||||
|
items: [{ channel: 'y' }]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data.length === 0 ? (
|
||||||
|
<EmptyData height={height} title={title} />
|
||||||
|
) : (
|
||||||
|
<Area data={data} {...config} />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LineChart;
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ import { Empty } from 'antd';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
const EmptyData: React.FC<{
|
const EmptyData: React.FC<{
|
||||||
height: string | number;
|
height?: string | number;
|
||||||
title: React.ReactNode;
|
title?: React.ReactNode;
|
||||||
}> = ({ height, title }) => {
|
}> = ({ height, title }) => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -13,12 +13,14 @@ const EmptyData: React.FC<{
|
|||||||
}}
|
}}
|
||||||
className="flex-center flex-column "
|
className="flex-center flex-column "
|
||||||
>
|
>
|
||||||
<h3
|
{title && (
|
||||||
className="justify-center"
|
<h3
|
||||||
style={{ padding: '16px 0', marginBottom: 0 }}
|
className="justify-center"
|
||||||
>
|
style={{ padding: '16px 0', marginBottom: 0 }}
|
||||||
{title}
|
>
|
||||||
</h3>
|
{title}
|
||||||
|
</h3>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className="flex-center justify-center flex-column"
|
className="flex-center justify-center flex-column"
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%' }}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Line } from '@ant-design/plots';
|
import { Line } from '@ant-design/plots';
|
||||||
|
import EmptyData from './empty-data';
|
||||||
|
|
||||||
interface LineChartProps {
|
interface LineChartProps {
|
||||||
title?: string;
|
title?: string;
|
||||||
@@ -7,7 +8,7 @@ interface LineChartProps {
|
|||||||
color?: string[];
|
color?: string[];
|
||||||
xField?: string;
|
xField?: string;
|
||||||
yField?: string;
|
yField?: string;
|
||||||
locale: string;
|
locale?: string;
|
||||||
labelFormatter?: (v: any) => string;
|
labelFormatter?: (v: any) => string;
|
||||||
slider?: any;
|
slider?: any;
|
||||||
}
|
}
|
||||||
@@ -79,7 +80,11 @@ const LineChart: React.FC<LineChartProps> = (props) => {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Line data={data} {...config} />
|
{data.length === 0 ? (
|
||||||
|
<EmptyData height={height} title={title} />
|
||||||
|
) : (
|
||||||
|
<Line data={data} {...config} />
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChange = (e: any) => {
|
||||||
|
e.target.value = e.target.value?.trim?.();
|
||||||
props.onChange?.(e);
|
props.onChange?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -48,6 +49,7 @@ const SealInputNumber: React.FC<InputNumberProps & SealFormItemProps> = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnBlur = (e: any) => {
|
const handleOnBlur = (e: any) => {
|
||||||
|
e.target.value = e.target.value?.trim?.();
|
||||||
if (!inputRef.current?.value) {
|
if (!inputRef.current?.value) {
|
||||||
setIsFocus(false);
|
setIsFocus(false);
|
||||||
props.onBlur?.(e);
|
props.onBlur?.(e);
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ const SealPassword: React.FC<InputProps & SealFormItemProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChange = (e: any) => {
|
||||||
|
e.target.value = e.target.value?.trim?.();
|
||||||
props.onChange?.(e);
|
props.onChange?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ const SealPassword: React.FC<InputProps & SealFormItemProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnBlur = (e: any) => {
|
const handleOnBlur = (e: any) => {
|
||||||
|
e.target.value = e.target.value?.trim?.();
|
||||||
if (!inputRef.current?.input?.value) {
|
if (!inputRef.current?.input?.value) {
|
||||||
setIsFocus(false);
|
setIsFocus(false);
|
||||||
props.onBlur?.(e);
|
props.onBlur?.(e);
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleChange = (e: any) => {
|
const handleChange = (e: any) => {
|
||||||
|
e.target.value = e.target.value?.trim?.();
|
||||||
props.onChange?.(e);
|
props.onChange?.(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -51,6 +52,7 @@ const SealInput: React.FC<InputProps & SealFormItemProps> = (props) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOnBlur = (e: any) => {
|
const handleOnBlur = (e: any) => {
|
||||||
|
e.target.value = e.target.value?.trim?.();
|
||||||
if (!inputRef.current?.input?.value) {
|
if (!inputRef.current?.input?.value) {
|
||||||
setIsFocus(false);
|
setIsFocus(false);
|
||||||
props.onBlur?.(e);
|
props.onBlur?.(e);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import '../styles/row-children.less';
|
|||||||
|
|
||||||
const RowChildren = (props: any) => {
|
const RowChildren = (props: any) => {
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
return <div className="row-children">{children}</div>;
|
return <div className="row-children">{children}</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ const TableRow: React.FC<
|
|||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
const pollTimer = useRef<any>(null);
|
const pollTimer = useRef<any>(null);
|
||||||
const chunkRequestRef = useRef<any>(null);
|
const chunkRequestRef = useRef<any>(null);
|
||||||
|
const childrenRendered = useRef<any>(null);
|
||||||
|
|
||||||
const { updateChunkedList } = useUpdateChunkedList(childrenData, {
|
const { updateChunkedList } = useUpdateChunkedList(childrenData, {
|
||||||
setDataList: setChildrenData
|
setDataList: setChildrenData
|
||||||
@@ -58,6 +59,10 @@ const TableRow: React.FC<
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const renderChildrenData = () => {
|
||||||
|
return renderChildren?.(childrenData);
|
||||||
|
};
|
||||||
|
|
||||||
const handlePolling = async () => {
|
const handlePolling = async () => {
|
||||||
if (pollingChildren) {
|
if (pollingChildren) {
|
||||||
try {
|
try {
|
||||||
@@ -110,12 +115,12 @@ const TableRow: React.FC<
|
|||||||
setExpanded(!expanded);
|
setExpanded(!expanded);
|
||||||
onExpand?.(!expanded, record);
|
onExpand?.(!expanded, record);
|
||||||
|
|
||||||
|
chunkRequestRef.current?.current?.cancel?.();
|
||||||
if (pollTimer.current) {
|
if (pollTimer.current) {
|
||||||
clearInterval(pollTimer.current);
|
clearInterval(pollTimer.current);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expanded) {
|
if (expanded) {
|
||||||
chunkRequestRef.current?.current?.cancel?.();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,9 +129,6 @@ const TableRow: React.FC<
|
|||||||
pollTimer.current = setInterval(() => {
|
pollTimer.current = setInterval(() => {
|
||||||
handlePolling();
|
handlePolling();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else if (watchChildren) {
|
|
||||||
await handleLoadChildren();
|
|
||||||
createChunkRequest();
|
|
||||||
} else {
|
} else {
|
||||||
handleLoadChildren();
|
handleLoadChildren();
|
||||||
}
|
}
|
||||||
@@ -146,6 +148,12 @@ const TableRow: React.FC<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (childrenData.length) {
|
||||||
|
createChunkRequest();
|
||||||
|
}
|
||||||
|
}, [childrenData]);
|
||||||
|
|
||||||
const renderRowPrefix = () => {
|
const renderRowPrefix = () => {
|
||||||
if (expandable && rowSelection) {
|
if (expandable && rowSelection) {
|
||||||
return (
|
return (
|
||||||
@@ -220,11 +228,7 @@ const TableRow: React.FC<
|
|||||||
{expanded && (
|
{expanded && (
|
||||||
<div className="expanded-row">
|
<div className="expanded-row">
|
||||||
<Spin spinning={loading}>
|
<Spin spinning={loading}>
|
||||||
{childrenData.length ? (
|
{childrenData.length ? renderChildrenData() : <Empty></Empty>}
|
||||||
renderChildren?.(childrenData)
|
|
||||||
) : (
|
|
||||||
<Empty></Empty>
|
|
||||||
)}
|
|
||||||
</Spin>
|
</Spin>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -118,7 +118,11 @@ const SealTable: React.FC<SealTableProps> = (props) => {
|
|||||||
|
|
||||||
const renderContent = () => {
|
const renderContent = () => {
|
||||||
if (!props.dataSource.length) {
|
if (!props.dataSource.length) {
|
||||||
return <Empty></Empty>;
|
return (
|
||||||
|
<div className="empty-wrapper">
|
||||||
|
<Empty></Empty>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className="seal-table-content">
|
<div className="seal-table-content">
|
||||||
|
|||||||
@@ -74,4 +74,11 @@
|
|||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
min-height: 100px;
|
min-height: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-wrapper {
|
||||||
|
display: flex;
|
||||||
|
min-height: 160px;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export function useUpdateChunkedList(
|
|||||||
dataList,
|
dataList,
|
||||||
(sItem: any) => sItem.id === item.id
|
(sItem: any) => sItem.id === item.id
|
||||||
);
|
);
|
||||||
|
console.log('updateIndex===========', dataList, updateIndex, data);
|
||||||
if (updateIndex > -1) {
|
if (updateIndex > -1) {
|
||||||
const updateItem = _.cloneDeep(item);
|
const updateItem = _.cloneDeep(item);
|
||||||
dataList[updateIndex] = updateItem;
|
dataList[updateIndex] = updateItem;
|
||||||
|
|||||||
@@ -19,5 +19,10 @@ export default {
|
|||||||
'users.password.number': 'At least one number',
|
'users.password.number': 'At least one number',
|
||||||
'users.password.special': 'At least one special character',
|
'users.password.special': 'At least one special character',
|
||||||
'users.password.length': 'Length between 6 and 12 characters',
|
'users.password.length': 'Length between 6 and 12 characters',
|
||||||
'users.password.modify.title': 'Modify Password'
|
'users.password.modify.title': 'Modify Password',
|
||||||
|
'users.password.modify.description':
|
||||||
|
"For your account's security, please change your initial password.",
|
||||||
|
'users.password.confirm': 'Confirm New Password',
|
||||||
|
'users.password.confirm.empty': 'Please confirm the new password.',
|
||||||
|
'users.password.confirm.error': 'The two passwords entered do not match.'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,5 +19,9 @@ export default {
|
|||||||
'users.password.number': '至少包含一个数字',
|
'users.password.number': '至少包含一个数字',
|
||||||
'users.password.special': '至少包含一个特殊字符',
|
'users.password.special': '至少包含一个特殊字符',
|
||||||
'users.password.length': '长度在6至12个字符之间',
|
'users.password.length': '长度在6至12个字符之间',
|
||||||
'users.password.modify.title': '修改密码'
|
'users.password.modify.title': '修改密码',
|
||||||
|
'users.password.modify.description': '为了确保您的账户安全,请修改初始密码',
|
||||||
|
'users.password.confirm': '确认新密码',
|
||||||
|
'users.password.confirm.empty': '请确认新密码',
|
||||||
|
'users.password.confirm.error': '两次输入的密码不一致'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import CardWrapper from '@/components/card-wrapper';
|
import CardWrapper from '@/components/card-wrapper';
|
||||||
|
import AreaChart from '@/components/charts/area';
|
||||||
import ColumnBar from '@/components/charts/column-bar';
|
import ColumnBar from '@/components/charts/column-bar';
|
||||||
import HBar from '@/components/charts/h-bar';
|
import HBar from '@/components/charts/h-bar';
|
||||||
import PageTools from '@/components/page-tools';
|
import PageTools from '@/components/page-tools';
|
||||||
@@ -34,16 +35,16 @@ const projects = [
|
|||||||
|
|
||||||
const APIRequestData = generateRandomArray({
|
const APIRequestData = generateRandomArray({
|
||||||
length: times.length,
|
length: times.length,
|
||||||
max: 100,
|
max: 1000,
|
||||||
min: 0,
|
min: 10,
|
||||||
offset: 10
|
offset: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
const TokensData = generateRandomArray({
|
const TokensData = generateRandomArray({
|
||||||
length: times.length,
|
length: times.length,
|
||||||
max: 2000,
|
max: 2000,
|
||||||
min: 200,
|
min: 0,
|
||||||
offset: 200
|
offset: 500
|
||||||
});
|
});
|
||||||
|
|
||||||
const usersData = generateRandomArray({
|
const usersData = generateRandomArray({
|
||||||
@@ -189,7 +190,11 @@ const Usage = () => {
|
|||||||
</span>
|
</span>
|
||||||
}
|
}
|
||||||
right={
|
right={
|
||||||
<RangePicker onChange={handleSelectDate} style={{ width: 300 }} />
|
<RangePicker
|
||||||
|
onChange={handleSelectDate}
|
||||||
|
style={{ width: 300 }}
|
||||||
|
picker="month"
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<Row style={{ width: '100%' }} gutter={[0, 20]}>
|
<Row style={{ width: '100%' }} gutter={[0, 20]}>
|
||||||
@@ -204,13 +209,13 @@ const Usage = () => {
|
|||||||
<CardWrapper style={{ width: '100%' }}>
|
<CardWrapper style={{ width: '100%' }}>
|
||||||
<Row style={{ width: '100%' }}>
|
<Row style={{ width: '100%' }}>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<ColumnBar
|
<AreaChart
|
||||||
title={intl.formatMessage({ id: 'dashboard.apirequest' })}
|
title={intl.formatMessage({ id: 'dashboard.apirequest' })}
|
||||||
data={requestData}
|
data={requestData}
|
||||||
xField="time"
|
xField="time"
|
||||||
yField="value"
|
yField="value"
|
||||||
height={360}
|
height={360}
|
||||||
></ColumnBar>
|
></AreaChart>
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={12}>
|
<Col span={12}>
|
||||||
<ColumnBar
|
<ColumnBar
|
||||||
|
|||||||
@@ -371,7 +371,7 @@ const Models: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData();
|
// fetchData();
|
||||||
createModelsChunkRequest();
|
createModelsChunkRequest();
|
||||||
return () => {
|
return () => {
|
||||||
chunkRequedtRef.current?.current?.cancel?.();
|
chunkRequedtRef.current?.current?.cancel?.();
|
||||||
|
|||||||
@@ -47,8 +47,21 @@ const PasswordForm: React.FC = () => {
|
|||||||
style={{ width: '400px', margin: '0 auto', paddingTop: '5%' }}
|
style={{ width: '400px', margin: '0 auto', paddingTop: '5%' }}
|
||||||
onFinish={handleSubmit}
|
onFinish={handleSubmit}
|
||||||
>
|
>
|
||||||
<h2 className="justify-center m-b-20">
|
<h2 className="justify-center m-b-20 flex-column flex-center">
|
||||||
{intl.formatMessage({ id: 'users.password.modify.title' })}
|
<span>
|
||||||
|
{' '}
|
||||||
|
{intl.formatMessage({ id: 'users.password.modify.title' })}
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
style={{
|
||||||
|
padding: '6px 0',
|
||||||
|
fontSize: 'var(--font-size-base)',
|
||||||
|
fontWeight: 'var(--font-weight-normal)',
|
||||||
|
color: 'var(--ant-color-text-tertiary)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{intl.formatMessage({ id: 'users.password.modify.description' })}
|
||||||
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<Form.Item
|
<Form.Item
|
||||||
@@ -87,8 +100,42 @@ const PasswordForm: React.FC = () => {
|
|||||||
label={intl.formatMessage({ id: 'users.form.newpassword' })}
|
label={intl.formatMessage({ id: 'users.form.newpassword' })}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="confirm_password"
|
||||||
|
dependencies={['new_password']}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: intl.formatMessage({
|
||||||
|
id: 'users.password.confirm.empty'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
({ getFieldValue }) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (!value || getFieldValue('new_password') === value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(
|
||||||
|
new Error(
|
||||||
|
intl.formatMessage({ id: 'users.password.confirm.error' })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<SealInput.Password
|
||||||
|
prefix={<LockOutlined />}
|
||||||
|
label={intl.formatMessage({ id: 'users.password.confirm' })}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
<Button htmlType="submit" type="primary" block>
|
<Button
|
||||||
|
htmlType="submit"
|
||||||
|
type="primary"
|
||||||
|
block
|
||||||
|
style={{ marginTop: 20 }}
|
||||||
|
>
|
||||||
{intl.formatMessage({ id: 'common.button.submit' })}
|
{intl.formatMessage({ id: 'common.button.submit' })}
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
Reference in New Issue
Block a user