Browse Source

css

pull/10914/head
期贤 3 years ago
parent
commit
440197a7bf
  1. 28
      src/pages/User/register-result/style.style.ts
  2. 28
      src/pages/User/register/style.style.ts
  3. 70
      src/pages/account/center/Center.style.ts
  4. 60
      src/pages/account/center/components/Applications/index.style.ts
  5. 47
      src/pages/account/center/components/Applications/index.tsx
  6. 14
      src/pages/account/center/components/ArticleListContent/index.style.ts
  7. 10
      src/pages/account/center/components/ArticleListContent/index.tsx
  8. 8
      src/pages/account/center/components/Articles/index.style.ts
  9. 22
      src/pages/account/center/components/Articles/index.tsx
  10. 32
      src/pages/account/center/components/AvatarList/index.style.ts
  11. 43
      src/pages/account/center/components/AvatarList/index.tsx
  12. 48
      src/pages/account/center/components/Projects/index.style.ts
  13. 29
      src/pages/account/center/components/Projects/index.tsx
  14. 70
      src/pages/account/center/index.tsx
  15. 42
      src/pages/account/settings/components/BaseView.style.ts
  16. 10
      src/pages/account/settings/components/PhoneView.tsx
  17. 56
      src/pages/account/settings/index.tsx
  18. 78
      src/pages/account/settings/style.style.ts
  19. 31
      src/pages/dashboard/analysis/components/Charts/Bar/index.tsx
  20. 82
      src/pages/dashboard/analysis/components/Charts/ChartCard/index.style.ts
  21. 14
      src/pages/dashboard/analysis/components/Charts/Field/index.style.ts
  22. 4
      src/pages/dashboard/analysis/components/Charts/Field/index.tsx
  23. 22
      src/pages/dashboard/analysis/components/Charts/MiniArea/index.tsx
  24. 31
      src/pages/dashboard/analysis/components/Charts/MiniBar/index.tsx
  25. 8
      src/pages/dashboard/analysis/components/Charts/MiniProgress/index.tsx
  26. 90
      src/pages/dashboard/analysis/components/Charts/Pie/index.tsx
  27. 47
      src/pages/dashboard/analysis/components/Charts/TagCloud/index.tsx
  28. 58
      src/pages/dashboard/analysis/components/Charts/TimelineChart/index.tsx
  29. 34
      src/pages/dashboard/analysis/components/Charts/WaterWave/index.tsx
  30. 26
      src/pages/dashboard/analysis/components/Charts/index.style.ts
  31. 55
      src/pages/dashboard/analysis/components/IntroduceRow.tsx
  32. 42
      src/pages/dashboard/analysis/components/NumberInfo/index.style.ts
  33. 23
      src/pages/dashboard/analysis/components/NumberInfo/index.tsx
  34. 25
      src/pages/dashboard/analysis/components/OfflineData.tsx
  35. 30
      src/pages/dashboard/analysis/components/ProportionSales.tsx
  36. 70
      src/pages/dashboard/analysis/components/SalesCard.tsx
  37. 18
      src/pages/dashboard/analysis/components/Trend/index.style.ts
  38. 20
      src/pages/dashboard/analysis/components/Trend/index.tsx
  39. 72
      src/pages/dashboard/analysis/index.tsx
  40. 136
      src/pages/dashboard/analysis/style.style.ts
  41. 2
      src/pages/dashboard/analysis/utils/utils.style.ts
  42. 67
      src/pages/dashboard/monitor/components/ActiveChart/index.style.ts
  43. 22
      src/pages/dashboard/monitor/components/Charts/MiniArea/index.tsx
  44. 91
      src/pages/dashboard/monitor/components/Charts/Pie/index.tsx
  45. 47
      src/pages/dashboard/monitor/components/Charts/TagCloud/index.tsx
  46. 34
      src/pages/dashboard/monitor/components/Charts/WaterWave/index.tsx
  47. 71
      src/pages/dashboard/monitor/index.tsx
  48. 10
      src/pages/dashboard/monitor/style.style.ts
  49. 16
      src/pages/dashboard/workplace/components/EditableLinkGroup/index.style.ts
  50. 14
      src/pages/dashboard/workplace/components/EditableLinkGroup/index.tsx
  51. 180
      src/pages/dashboard/workplace/components/Radar/index.tsx
  52. 79
      src/pages/dashboard/workplace/index.tsx
  53. 158
      src/pages/dashboard/workplace/style.style.ts
  54. 85
      src/pages/form/advanced-form/components/TableForm.tsx
  55. 196
      src/pages/form/advanced-form/index.tsx
  56. 52
      src/pages/form/advanced-form/style.style.ts
  57. 63
      src/pages/form/basic-form/index.tsx
  58. 4
      src/pages/form/basic-form/style.style.ts
  59. 62
      src/pages/form/step-form/index.tsx
  60. 10
      src/pages/form/step-form/style.style.ts
  61. 32
      src/pages/list/basic-list/components/OperationModal.tsx
  62. 74
      src/pages/list/basic-list/index.tsx
  63. 94
      src/pages/list/basic-list/style.style.ts
  64. 2
      src/pages/list/basic-list/utils/utils.style.ts
  65. 45
      src/pages/list/card-list/index.tsx
  66. 56
      src/pages/list/card-list/style.style.ts
  67. 2
      src/pages/list/card-list/utils/utils.style.ts
  68. 52
      src/pages/list/search/applications/components/StandardFormRow/index.style.ts
  69. 6
      src/pages/list/search/applications/components/StandardFormRow/index.tsx
  70. 38
      src/pages/list/search/applications/components/TagSelect/index.style.ts
  71. 47
      src/pages/list/search/applications/components/TagSelect/index.tsx
  72. 70
      src/pages/list/search/applications/index.tsx
  73. 58
      src/pages/list/search/applications/style.style.ts
  74. 2
      src/pages/list/search/applications/utils/utils.style.ts
  75. 14
      src/pages/list/search/articles/components/ArticleListContent/index.style.ts
  76. 10
      src/pages/list/search/articles/components/ArticleListContent/index.tsx
  77. 54
      src/pages/list/search/articles/components/StandardFormRow/index.style.ts
  78. 6
      src/pages/list/search/articles/components/StandardFormRow/index.tsx
  79. 38
      src/pages/list/search/articles/components/TagSelect/index.style.ts
  80. 47
      src/pages/list/search/articles/components/TagSelect/index.tsx
  81. 71
      src/pages/list/search/articles/index.tsx
  82. 12
      src/pages/list/search/articles/style.style.ts
  83. 32
      src/pages/list/search/projects/components/AvatarList/index.style.ts
  84. 43
      src/pages/list/search/projects/components/AvatarList/index.tsx
  85. 54
      src/pages/list/search/projects/components/StandardFormRow/index.style.ts
  86. 6
      src/pages/list/search/projects/components/StandardFormRow/index.tsx
  87. 38
      src/pages/list/search/projects/components/TagSelect/index.style.ts
  88. 47
      src/pages/list/search/projects/components/TagSelect/index.tsx
  89. 34
      src/pages/list/search/projects/index.tsx
  90. 48
      src/pages/list/search/projects/style.style.ts
  91. 2
      src/pages/list/search/projects/utils/utils.style.ts
  92. 34
      src/pages/profile/advanced/style.style.ts
  93. 100
      src/pages/profile/basic/index.tsx
  94. 8
      src/pages/profile/basic/style.style.ts
  95. 8
      src/pages/result/fail/index.style.ts
  96. 16
      src/pages/result/success/index.style.ts
  97. 10
      src/pages/user/register-result/index.tsx
  98. 100
      src/pages/user/register/index.tsx

28
src/pages/User/register-result/style.style.ts

@ -1,27 +1,27 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return { return {
registerResult: { registerResult: {
width: "800px", width: '800px',
minHeight: "400px", minHeight: '400px',
margin: "auto", margin: 'auto',
padding: "80px", padding: '80px',
background: "none", background: 'none',
}, },
".anticon": { '.anticon': {
fontSize: "64px", fontSize: '64px',
}, },
title: { title: {
marginTop: "32px", marginTop: '32px',
fontSize: "20px", fontSize: '20px',
lineHeight: "28px", lineHeight: '28px',
}, },
actions: { actions: {
marginTop: "40px", marginTop: '40px',
}, },
"a + a": { 'a + a': {
marginLeft: "8px", marginLeft: '8px',
}, },
}; };
}); });

28
src/pages/User/register/style.style.ts

@ -1,30 +1,30 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
main: { main: {
width: "368px", width: '368px',
margin: "0 auto", margin: '0 auto',
}, },
h3: { h3: {
marginBottom: "20px", marginBottom: '20px',
fontSize: "16px", fontSize: '16px',
}, },
password: { password: {
marginBottom: "24px", marginBottom: '24px',
}, },
".ant-form-item-explain": { '.ant-form-item-explain': {
display: "none", display: 'none',
}, },
getCaptcha: { getCaptcha: {
display: "block", display: 'block',
width: "100%", width: '100%',
}, },
submit: { submit: {
width: "50%", width: '50%',
}, },
login: { login: {
float: "right", float: 'right',
lineHeight: token.controlHeight, lineHeight: token.controlHeight,
}, },
success: { success: {
@ -36,8 +36,8 @@ const useStyles = createStyles(({ token }) => {
error: { error: {
color: token.colorError, color: token.colorError,
}, },
".progress-pass > .progress": {}, '.progress-pass > .progress': {},
".ant-progress-bg": { '.ant-progress-bg': {
backgroundColor: token.colorWarning, backgroundColor: token.colorWarning,
}, },
}; };

70
src/pages/account/center/Center.style.ts

@ -1,65 +1,65 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
avatarHolder: { avatarHolder: {
marginBottom: "24px", marginBottom: '24px',
textAlign: "center", textAlign: 'center',
"& > img": { width: "104px", height: "104px", marginBottom: "20px" }, '& > img': { width: '104px', height: '104px', marginBottom: '20px' },
}, },
name: { name: {
marginBottom: "4px", marginBottom: '4px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
fontSize: "20px", fontSize: '20px',
lineHeight: "28px", lineHeight: '28px',
}, },
detail: {}, detail: {},
p: { p: {
position: "relative", position: 'relative',
marginBottom: "8px", marginBottom: '8px',
paddingLeft: "26px", paddingLeft: '26px',
"&:last-child": { marginBottom: "0" }, '&:last-child': { marginBottom: '0' },
}, },
i: { i: {
position: "absolute", position: 'absolute',
top: "4px", top: '4px',
left: "0", left: '0',
width: "14px", width: '14px',
height: "14px", height: '14px',
}, },
tagsTitle: { tagsTitle: {
marginBottom: "12px", marginBottom: '12px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
}, },
teamTitle: { teamTitle: {
marginBottom: "12px", marginBottom: '12px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
}, },
tags: {}, tags: {},
".ant-tag": { '.ant-tag': {
marginBottom: "8px", marginBottom: '8px',
}, },
team: {}, team: {},
".ant-avatar": { '.ant-avatar': {
marginRight: "12px", marginRight: '12px',
}, },
a: { a: {
display: "block", display: 'block',
marginBottom: "24px", marginBottom: '24px',
overflow: "hidden", overflow: 'hidden',
color: token.colorText, color: token.colorText,
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
wordBreak: "break-all", wordBreak: 'break-all',
transition: "color 0.3s", transition: 'color 0.3s',
"&:hover": { color: token.colorPrimary }, '&:hover': { color: token.colorPrimary },
}, },
tabsCard: {}, tabsCard: {},
".ant-card-head": { '.ant-card-head': {
padding: "0 16px", padding: '0 16px',
}, },
}; };
}); });

60
src/pages/account/center/components/Applications/index.style.ts

@ -1,47 +1,47 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
filterCardList: { filterCardList: {
marginBottom: "-24px", marginBottom: '-24px',
}, },
".ant-card-meta-content": { '.ant-card-meta-content': {
marginTop: "0", marginTop: '0',
}, },
"// disabled white space .ant-card-meta-avatar": { '// disabled white space .ant-card-meta-avatar': {
fontSize: "0", fontSize: '0',
}, },
".ant-list .ant-list-item-content-single": { '.ant-list .ant-list-item-content-single': {
maxWidth: "100%", maxWidth: '100%',
}, },
cardInfo: { cardInfo: {
marginTop: "16px", marginTop: '16px',
marginLeft: "40px", marginLeft: '40px',
zoom: "1", zoom: '1',
"&::before, &::after": { display: "table", content: "' '" }, '&::before, &::after': { display: 'table', content: "' '" },
"&::after": { '&::after': {
clear: "both", clear: 'both',
height: "0", height: '0',
fontSize: "0", fontSize: '0',
visibility: "hidden", visibility: 'hidden',
}, },
"& > div": { '& > div': {
position: "relative", position: 'relative',
float: "left", float: 'left',
width: "50%", width: '50%',
textAlign: "left", textAlign: 'left',
}, },
}, },
p: { p: {
margin: "0", margin: '0',
fontSize: "24px", fontSize: '24px',
lineHeight: "32px", lineHeight: '32px',
}, },
"p:first-child": { 'p:first-child': {
marginBottom: "4px", marginBottom: '4px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: "12px", fontSize: '12px',
lineHeight: "20px", lineHeight: '20px',
}, },
}; };
}); });

47
src/pages/account/center/components/Applications/index.tsx

@ -3,17 +3,17 @@ import {
EditOutlined, EditOutlined,
EllipsisOutlined, EllipsisOutlined,
ShareAltOutlined, ShareAltOutlined,
} from "@ant-design/icons"; } from '@ant-design/icons';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import { Avatar, Card, Dropdown, List, Menu, Tooltip } from "antd"; import { Avatar, Card, Dropdown, List, Menu, Tooltip } from 'antd';
import React from "react"; import React from 'react';
import numeral from "numeral"; import numeral from 'numeral';
import type { ListItemDataType } from "../../data.d"; import type { ListItemDataType } from '../../data.d';
import { queryFakeList } from "../../service"; import { queryFakeList } from '../../service';
import useStyles from "./index.style"; import useStyles from './index.style';
export function formatWan(val: number) { export function formatWan(val: number) {
const v = val * 1; const v = val * 1;
if (!v || Number.isNaN(v)) return ""; if (!v || Number.isNaN(v)) return '';
let result: React.ReactNode = val; let result: React.ReactNode = val;
if (val > 10000) { if (val > 10000) {
result = ( result = (
@ -21,10 +21,10 @@ export function formatWan(val: number) {
{Math.floor(val / 10000)} {Math.floor(val / 10000)}
<span <span
style={{ style={{
position: "relative", position: 'relative',
top: -2, top: -2,
fontSize: 14, fontSize: 14,
fontStyle: "normal", fontStyle: 'normal',
marginLeft: 2, marginLeft: 2,
}} }}
> >
@ -46,29 +46,17 @@ const Applications: React.FC = () => {
const itemMenu = ( const itemMenu = (
<Menu> <Menu>
<Menu.Item> <Menu.Item>
<a <a target="_blank" rel="noopener noreferrer" href="https://www.alipay.com/">
target="_blank"
rel="noopener noreferrer"
href="https://www.alipay.com/"
>
1st menu item 1st menu item
</a> </a>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<a <a target="_blank" rel="noopener noreferrer" href="https://www.taobao.com/">
target="_blank"
rel="noopener noreferrer"
href="https://www.taobao.com/"
>
2nd menu item 2nd menu item
</a> </a>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<a <a target="_blank" rel="noopener noreferrer" href="https://www.tmall.com/">
target="_blank"
rel="noopener noreferrer"
href="https://www.tmall.com/"
>
3d menu item 3d menu item
</a> </a>
</Menu.Item> </Menu.Item>
@ -125,14 +113,11 @@ const Applications: React.FC = () => {
</Dropdown>, </Dropdown>,
]} ]}
> >
<Card.Meta <Card.Meta avatar={<Avatar size="small" src={item.avatar} />} title={item.title} />
avatar={<Avatar size="small" src={item.avatar} />}
title={item.title}
/>
<div className={stylesApplications.cardItemContent}> <div className={stylesApplications.cardItemContent}>
<CardInfo <CardInfo
activeUser={formatWan(item.activeUser)} activeUser={formatWan(item.activeUser)}
newUser={numeral(item.newUser).format("0,0")} newUser={numeral(item.newUser).format('0,0')}
/> />
</div> </div>
</Card> </Card>

14
src/pages/account/center/components/ArticleListContent/index.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
@ -6,15 +6,15 @@ const useStyles = createStyles(({ token }) => {
[`@media screen and (max-width: ${token.screenXS}px)`]: {}, [`@media screen and (max-width: ${token.screenXS}px)`]: {},
}, },
description: { description: {
maxWidth: "720px", maxWidth: '720px',
lineHeight: "22px", lineHeight: '22px',
}, },
extra: { extra: {
[`@media screen and (max-width: ${token.screenXS}px)`]: { [`@media screen and (max-width: ${token.screenXS}px)`]: {
"& > em": { '& > em': {
display: "block", display: 'block',
marginTop: "8px", marginTop: '8px',
marginLeft: "0", marginLeft: '0',
}, },
}, },
}, },

10
src/pages/account/center/components/ArticleListContent/index.tsx

@ -1,7 +1,7 @@
import { Avatar } from "antd"; import { Avatar } from 'antd';
import React from "react"; import React from 'react';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import useStyles from "./index.style"; import useStyles from './index.style';
export type ApplicationsProps = { export type ApplicationsProps = {
data: { data: {
content?: string; content?: string;
@ -21,7 +21,7 @@ const ArticleListContent: React.FC<ApplicationsProps> = ({
<div className={styles.extra}> <div className={styles.extra}>
<Avatar src={avatar} size="small" /> <Avatar src={avatar} size="small" />
<a href={href}>{owner}</a> <a href={href}>{href}</a> <a href={href}>{owner}</a> <a href={href}>{href}</a>
<em>{dayjs(updatedAt).format("YYYY-MM-DD HH:mm")}</em> <em>{dayjs(updatedAt).format('YYYY-MM-DD HH:mm')}</em>
</div> </div>
</div> </div>
); );

8
src/pages/account/center/components/Articles/index.style.ts

@ -1,12 +1,12 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
articleList: {}, articleList: {},
".ant-list-item:first-child": { '.ant-list-item:first-child': {
paddingTop: "0", paddingTop: '0',
}, },
"a.listItemMetaTitle": { 'a.listItemMetaTitle': {
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
}; };

22
src/pages/account/center/components/Articles/index.tsx

@ -1,11 +1,11 @@
import React from "react"; import React from 'react';
import { StarTwoTone, LikeOutlined, MessageFilled } from "@ant-design/icons"; import { StarTwoTone, LikeOutlined, MessageFilled } from '@ant-design/icons';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import { List, Tag } from "antd"; import { List, Tag } from 'antd';
import ArticleListContent from "../ArticleListContent"; import ArticleListContent from '../ArticleListContent';
import type { ListItemDataType } from "../../data.d"; import type { ListItemDataType } from '../../data.d';
import { queryFakeList } from "../../service"; import { queryFakeList } from '../../service';
import useStyles from "./index.style"; import useStyles from './index.style';
const Articles: React.FC = () => { const Articles: React.FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const IconText: React.FC<{ const IconText: React.FC<{
@ -36,11 +36,7 @@ const Articles: React.FC = () => {
actions={[ actions={[
<IconText key="star" icon={<StarTwoTone />} text={item.star} />, <IconText key="star" icon={<StarTwoTone />} text={item.star} />,
<IconText key="like" icon={<LikeOutlined />} text={item.like} />, <IconText key="like" icon={<LikeOutlined />} text={item.like} />,
<IconText <IconText key="message" icon={<MessageFilled />} text={item.message} />,
key="message"
icon={<MessageFilled />}
text={item.message}
/>,
]} ]}
> >
<List.Item.Meta <List.Item.Meta

32
src/pages/account/center/components/AvatarList/index.style.ts

@ -1,26 +1,26 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
avatarList: { avatarList: {
display: "inline-block", display: 'inline-block',
}, },
ul: { ul: {
display: "inline-block", display: 'inline-block',
marginLeft: "8px", marginLeft: '8px',
fontSize: "0", fontSize: '0',
}, },
avatarItem: { avatarItem: {
display: "inline-block", display: 'inline-block',
width: token.controlHeight, width: token.controlHeight,
height: token.controlHeight, height: token.controlHeight,
marginLeft: "-8px", marginLeft: '-8px',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
".ant-avatar": { '.ant-avatar': {
width: "20px", width: '20px',
height: "20px", height: '20px',
lineHeight: "20px", lineHeight: '20px',
}, },
avatarItemLarge: { avatarItemLarge: {
width: token.controlHeightLG, width: token.controlHeightLG,
@ -31,12 +31,12 @@ const useStyles = createStyles(({ token }) => {
height: token.controlHeightSM, height: token.controlHeightSM,
}, },
avatarItemMini: { avatarItemMini: {
width: "20px", width: '20px',
height: "20px", height: '20px',
}, },
".ant-avatar-string": { '.ant-avatar-string': {
fontSize: "12px", fontSize: '12px',
lineHeight: "18px", lineHeight: '18px',
}, },
}; };
}); });

43
src/pages/account/center/components/AvatarList/index.tsx

@ -1,8 +1,8 @@
import { Avatar, Tooltip } from "antd"; import { Avatar, Tooltip } from 'antd';
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
export declare type SizeType = number | "small" | "default" | "large"; export declare type SizeType = number | 'small' | 'default' | 'large';
export type AvatarItemProps = { export type AvatarItemProps = {
tips: React.ReactNode; tips: React.ReactNode;
src: string; src: string;
@ -16,22 +16,15 @@ export type AvatarListProps = {
maxLength?: number; maxLength?: number;
excessItemsStyle?: React.CSSProperties; excessItemsStyle?: React.CSSProperties;
style?: React.CSSProperties; style?: React.CSSProperties;
children: children: React.ReactElement<AvatarItemProps> | React.ReactElement<AvatarItemProps>[];
| React.ReactElement<AvatarItemProps>
| React.ReactElement<AvatarItemProps>[];
}; };
const avatarSizeToClassName = (size?: SizeType | "mini") => const avatarSizeToClassName = (size?: SizeType | 'mini') =>
classNames(styles.avatarItem, { classNames(styles.avatarItem, {
[styles.avatarItemLarge]: size === "large", [styles.avatarItemLarge]: size === 'large',
[styles.avatarItemSmall]: size === "small", [styles.avatarItemSmall]: size === 'small',
[styles.avatarItemMini]: size === "mini", [styles.avatarItemMini]: size === 'mini',
}); });
const Item: React.FC<AvatarItemProps> = ({ const Item: React.FC<AvatarItemProps> = ({ src, size, tips, onClick = () => {} }) => {
src,
size,
tips,
onClick = () => {},
}) => {
const cls = avatarSizeToClassName(size); const cls = avatarSizeToClassName(size);
return ( return (
<li className={cls} onClick={onClick}> <li className={cls} onClick={onClick}>
@ -41,7 +34,7 @@ const Item: React.FC<AvatarItemProps> = ({
src={src} src={src}
size={size} size={size}
style={{ style={{
cursor: "pointer", cursor: 'pointer',
}} }}
/> />
</Tooltip> </Tooltip>
@ -57,22 +50,18 @@ const AvatarList: React.FC<AvatarListProps> & {
const { styles } = useStyles(); const { styles } = useStyles();
const numOfChildren = React.Children.count(children); const numOfChildren = React.Children.count(children);
const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength; const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength;
const childrenArray = React.Children.toArray( const childrenArray = React.Children.toArray(children) as React.ReactElement<AvatarItemProps>[];
children
) as React.ReactElement<AvatarItemProps>[];
const childrenWithProps = childrenArray.slice(0, numToShow).map((child) => const childrenWithProps = childrenArray.slice(0, numToShow).map((child) =>
React.cloneElement(child, { React.cloneElement(child, {
size, size,
}) }),
); );
if (numToShow < numOfChildren) { if (numToShow < numOfChildren) {
const cls = avatarSizeToClassName(size); const cls = avatarSizeToClassName(size);
childrenWithProps.push( childrenWithProps.push(
<li key="exceed" className={cls}> <li key="exceed" className={cls}>
<Avatar size={size} style={excessItemsStyle}>{`+${ <Avatar size={size} style={excessItemsStyle}>{`+${numOfChildren - maxLength}`}</Avatar>
numOfChildren - maxLength </li>,
}`}</Avatar>
</li>
); );
} }
return ( return (

48
src/pages/account/center/components/Projects/index.style.ts

@ -1,47 +1,47 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
coverCardList: {}, coverCardList: {},
card: { card: {
"&:hover": {}, '&:hover': {},
}, },
".ant-card-meta-title": { '.ant-card-meta-title': {
marginBottom: "4px", marginBottom: '4px',
"& > a": { '& > a': {
display: "inline-block", display: 'inline-block',
maxWidth: "100%", maxWidth: '100%',
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
}, },
".ant-card-meta-description": { '.ant-card-meta-description': {
height: "44px", height: '44px',
overflow: "hidden", overflow: 'hidden',
lineHeight: "22px", lineHeight: '22px',
}, },
".ant-card-meta-title > a": { '.ant-card-meta-title > a': {
color: token.colorPrimary, color: token.colorPrimary,
}, },
cardItemContent: { cardItemContent: {
display: "flex", display: 'flex',
height: "20px", height: '20px',
marginTop: "16px", marginTop: '16px',
marginBottom: "-4px", marginBottom: '-4px',
lineHeight: "20px", lineHeight: '20px',
"& > span": { '& > span': {
flex: "1", flex: '1',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: "12px", fontSize: '12px',
}, },
}, },
avatarList: { avatarList: {
flex: "0 1 auto", flex: '0 1 auto',
}, },
cardList: { cardList: {
marginTop: "24px", marginTop: '24px',
}, },
".ant-list .ant-list-item-content-single": { '.ant-list .ant-list-item-content-single': {
maxWidth: "100%", maxWidth: '100%',
}, },
}; };
}); });

29
src/pages/account/center/components/Projects/index.tsx

@ -1,12 +1,12 @@
import { Card, List } from "antd"; import { Card, List } from 'antd';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import React from "react"; import React from 'react';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import { queryFakeList } from "../../service"; import { queryFakeList } from '../../service';
import AvatarList from "../AvatarList"; import AvatarList from '../AvatarList';
import type { ListItemDataType } from "../../data.d"; import type { ListItemDataType } from '../../data.d';
import useStyles from "./index.style"; import useStyles from './index.style';
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
const Projects: React.FC = () => { const Projects: React.FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
@ -32,15 +32,8 @@ const Projects: React.FC = () => {
dataSource={listData?.list || []} dataSource={listData?.list || []}
renderItem={(item) => ( renderItem={(item) => (
<List.Item> <List.Item>
<Card <Card className={styles.card} hoverable cover={<img alt={item.title} src={item.cover} />}>
className={styles.card} <Card.Meta title={<a>{item.title}</a>} description={item.subDescription} />
hoverable
cover={<img alt={item.title} src={item.cover} />}
>
<Card.Meta
title={<a>{item.title}</a>}
description={item.subDescription}
/>
<div className={styles.cardItemContent}> <div className={styles.cardItemContent}>
<span>{dayjs(item.updatedAt).fromNow()}</span> <span>{dayjs(item.updatedAt).fromNow()}</span>
<div className={styles.avatarList}> <div className={styles.avatarList}>

70
src/pages/account/center/index.tsx

@ -1,25 +1,20 @@
import { import { PlusOutlined, HomeOutlined, ContactsOutlined, ClusterOutlined } from '@ant-design/icons';
PlusOutlined, import { Avatar, Card, Col, Divider, Input, InputRef, Row, Tag } from 'antd';
HomeOutlined, import React, { useState, useRef } from 'react';
ContactsOutlined, import { GridContent } from '@ant-design/pro-components';
ClusterOutlined, import { Link, useRequest } from '@umijs/max';
} from "@ant-design/icons"; import Projects from './components/Projects';
import { Avatar, Card, Col, Divider, Input, InputRef, Row, Tag } from "antd"; import Articles from './components/Articles';
import React, { useState, useRef } from "react"; import Applications from './components/Applications';
import { GridContent } from "@ant-design/pro-components"; import type { CurrentUser, TagType, tabKeyType } from './data.d';
import { Link, useRequest } from "@umijs/max"; import { queryCurrent } from './service';
import Projects from "./components/Projects"; import useStyles from './Center.style';
import Articles from "./components/Articles";
import Applications from "./components/Applications";
import type { CurrentUser, TagType, tabKeyType } from "./data.d";
import { queryCurrent } from "./service";
import useStyles from "./Center.style";
const operationTabList = [ const operationTabList = [
{ {
key: "articles", key: 'articles',
tab: ( tab: (
<span> <span>
{" "} {' '}
<span <span
style={{ style={{
fontSize: 14, fontSize: 14,
@ -31,10 +26,10 @@ const operationTabList = [
), ),
}, },
{ {
key: "applications", key: 'applications',
tab: ( tab: (
<span> <span>
{" "} {' '}
<span <span
style={{ style={{
fontSize: 14, fontSize: 14,
@ -46,10 +41,10 @@ const operationTabList = [
), ),
}, },
{ {
key: "projects", key: 'projects',
tab: ( tab: (
<span> <span>
{" "} {' '}
<span <span
style={{ style={{
fontSize: 14, fontSize: 14,
@ -62,13 +57,13 @@ const operationTabList = [
}, },
]; ];
const TagList: React.FC<{ const TagList: React.FC<{
tags: CurrentUser["tags"]; tags: CurrentUser['tags'];
}> = ({ tags }) => { }> = ({ tags }) => {
const { styles } = useStyles(); const { styles } = useStyles();
const ref = useRef<InputRef | null>(null); const ref = useRef<InputRef | null>(null);
const [newTags, setNewTags] = useState<TagType[]>([]); const [newTags, setNewTags] = useState<TagType[]>([]);
const [inputVisible, setInputVisible] = useState<boolean>(false); const [inputVisible, setInputVisible] = useState<boolean>(false);
const [inputValue, setInputValue] = useState<string>(""); const [inputValue, setInputValue] = useState<string>('');
const showInput = () => { const showInput = () => {
setInputVisible(true); setInputVisible(true);
if (ref.current) { if (ref.current) {
@ -81,10 +76,7 @@ const TagList: React.FC<{
}; };
const handleInputConfirm = () => { const handleInputConfirm = () => {
let tempsTags = [...newTags]; let tempsTags = [...newTags];
if ( if (inputValue && tempsTags.filter((tag) => tag.label === inputValue).length === 0) {
inputValue &&
tempsTags.filter((tag) => tag.label === inputValue).length === 0
) {
tempsTags = [ tempsTags = [
...tempsTags, ...tempsTags,
{ {
@ -95,7 +87,7 @@ const TagList: React.FC<{
} }
setNewTags(tempsTags); setNewTags(tempsTags);
setInputVisible(false); setInputVisible(false);
setInputValue(""); setInputValue('');
}; };
return ( return (
<div className={styles.tags}> <div className={styles.tags}>
@ -121,7 +113,7 @@ const TagList: React.FC<{
<Tag <Tag
onClick={showInput} onClick={showInput}
style={{ style={{
borderStyle: "dashed", borderStyle: 'dashed',
}} }}
> >
<PlusOutlined /> <PlusOutlined />
@ -132,7 +124,7 @@ const TagList: React.FC<{
}; };
const Center: React.FC = () => { const Center: React.FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const [tabKey, setTabKey] = useState<tabKeyType>("articles"); const [tabKey, setTabKey] = useState<tabKeyType>('articles');
// 获取用户信息 // 获取用户信息
const { data: currentUser, loading } = useRequest(() => { const { data: currentUser, loading } = useRequest(() => {
@ -140,11 +132,7 @@ const Center: React.FC = () => {
}); });
// 渲染用户信息 // 渲染用户信息
const renderUserInfo = ({ const renderUserInfo = ({ title, group, geographic }: Partial<CurrentUser>) => {
title,
group,
geographic,
}: Partial<CurrentUser>) => {
return ( return (
<div className={styles.detail}> <div className={styles.detail}>
<p> <p>
@ -173,7 +161,7 @@ const Center: React.FC = () => {
( (
geographic || { geographic || {
province: { province: {
label: "", label: '',
}, },
} }
).province.label ).province.label
@ -182,7 +170,7 @@ const Center: React.FC = () => {
( (
geographic || { geographic || {
city: { city: {
label: "", label: '',
}, },
} }
).city.label ).city.label
@ -194,13 +182,13 @@ const Center: React.FC = () => {
// 渲染tab切换 // 渲染tab切换
const renderChildrenByTabKey = (tabValue: tabKeyType) => { const renderChildrenByTabKey = (tabValue: tabKeyType) => {
if (tabValue === "projects") { if (tabValue === 'projects') {
return <Projects />; return <Projects />;
} }
if (tabValue === "applications") { if (tabValue === 'applications') {
return <Applications />; return <Applications />;
} }
if (tabValue === "articles") { if (tabValue === 'articles') {
return <Articles />; return <Articles />;
} }
return null; return null;

42
src/pages/account/settings/components/BaseView.style.ts

@ -1,51 +1,51 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
baseView: { baseView: {
[`@media screen and (max-width: ${token.screenXL}px)`]: { [`@media screen and (max-width: ${token.screenXL}px)`]: {
flexDirection: "column-reverse", flexDirection: 'column-reverse',
}, },
}, },
".ant-legacy-form-item .ant-legacy-form-item-control-wrapper": { '.ant-legacy-form-item .ant-legacy-form-item-control-wrapper': {
width: "100%", width: '100%',
}, },
left: { left: {
minWidth: "224px", minWidth: '224px',
maxWidth: "448px", maxWidth: '448px',
}, },
right: { right: {
[`@media screen and (max-width: ${token.screenXL}px)`]: { [`@media screen and (max-width: ${token.screenXL}px)`]: {
display: "flex", display: 'flex',
flexDirection: "column", flexDirection: 'column',
alignItems: "center", alignItems: 'center',
maxWidth: "448px", maxWidth: '448px',
padding: "20px", padding: '20px',
}, },
}, },
avatar_title: { avatar_title: {
[`@media screen and (max-width: ${token.screenXL}px)`]: { [`@media screen and (max-width: ${token.screenXL}px)`]: {
display: "none", display: 'none',
}, },
}, },
avatar: { avatar: {
width: "144px", width: '144px',
height: "144px", height: '144px',
marginBottom: "12px", marginBottom: '12px',
overflow: "hidden", overflow: 'hidden',
}, },
img: { img: {
width: "100%", width: '100%',
}, },
button_view: { button_view: {
width: "144px", width: '144px',
textAlign: "center", textAlign: 'center',
}, },
area_code: { area_code: {
width: "72px", width: '72px',
}, },
phone_number: { phone_number: {
width: "214px", width: '214px',
}, },
}; };
}); });

10
src/pages/account/settings/components/PhoneView.tsx

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import { Input } from "antd"; import { Input } from 'antd';
import useStyles from "./PhoneView.style"; import useStyles from './PhoneView.style';
type PhoneViewProps = { type PhoneViewProps = {
value?: string; value?: string;
onChange?: (value: string) => void; onChange?: (value: string) => void;
@ -8,9 +8,9 @@ type PhoneViewProps = {
const PhoneView: React.FC<PhoneViewProps> = (props) => { const PhoneView: React.FC<PhoneViewProps> = (props) => {
const { styles } = useStyles(); const { styles } = useStyles();
const { value, onChange } = props; const { value, onChange } = props;
let values = ["", ""]; let values = ['', ''];
if (value) { if (value) {
values = value.split("-"); values = value.split('-');
} }
return ( return (
<> <>

56
src/pages/account/settings/index.tsx

@ -1,28 +1,28 @@
import React, { useState, useRef, useLayoutEffect } from "react"; import React, { useState, useRef, useLayoutEffect } from 'react';
import { GridContent } from "@ant-design/pro-components"; import { GridContent } from '@ant-design/pro-components';
import { Menu } from "antd"; import { Menu } from 'antd';
import BaseView from "./components/base"; import BaseView from './components/base';
import BindingView from "./components/binding"; import BindingView from './components/binding';
import NotificationView from "./components/notification"; import NotificationView from './components/notification';
import SecurityView from "./components/security"; import SecurityView from './components/security';
import useStyles from "./style.style"; import useStyles from './style.style';
const { Item } = Menu; const { Item } = Menu;
type SettingsStateKeys = "base" | "security" | "binding" | "notification"; type SettingsStateKeys = 'base' | 'security' | 'binding' | 'notification';
type SettingsState = { type SettingsState = {
mode: "inline" | "horizontal"; mode: 'inline' | 'horizontal';
selectKey: SettingsStateKeys; selectKey: SettingsStateKeys;
}; };
const Settings: React.FC = () => { const Settings: React.FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const menuMap: Record<string, React.ReactNode> = { const menuMap: Record<string, React.ReactNode> = {
base: "基本设置", base: '基本设置',
security: "安全设置", security: '安全设置',
binding: "账号绑定", binding: '账号绑定',
notification: "新消息通知", notification: '新消息通知',
}; };
const [initConfig, setInitConfig] = useState<SettingsState>({ const [initConfig, setInitConfig] = useState<SettingsState>({
mode: "inline", mode: 'inline',
selectKey: "base", selectKey: 'base',
}); });
const dom = useRef<HTMLDivElement>(); const dom = useRef<HTMLDivElement>();
const resize = () => { const resize = () => {
@ -30,44 +30,42 @@ const Settings: React.FC = () => {
if (!dom.current) { if (!dom.current) {
return; return;
} }
let mode: "inline" | "horizontal" = "inline"; let mode: 'inline' | 'horizontal' = 'inline';
const { offsetWidth } = dom.current; const { offsetWidth } = dom.current;
if (dom.current.offsetWidth < 641 && offsetWidth > 400) { if (dom.current.offsetWidth < 641 && offsetWidth > 400) {
mode = "horizontal"; mode = 'horizontal';
} }
if (window.innerWidth < 768 && offsetWidth > 400) { if (window.innerWidth < 768 && offsetWidth > 400) {
mode = "horizontal"; mode = 'horizontal';
} }
setInitConfig({ setInitConfig({
...initConfig, ...initConfig,
mode: mode as SettingsState["mode"], mode: mode as SettingsState['mode'],
}); });
}); });
}; };
useLayoutEffect(() => { useLayoutEffect(() => {
if (dom.current) { if (dom.current) {
window.addEventListener("resize", resize); window.addEventListener('resize', resize);
resize(); resize();
} }
return () => { return () => {
window.removeEventListener("resize", resize); window.removeEventListener('resize', resize);
}; };
}, [dom.current]); }, [dom.current]);
const getMenu = () => { const getMenu = () => {
return Object.keys(menuMap).map((item) => ( return Object.keys(menuMap).map((item) => <Item key={item}>{menuMap[item]}</Item>);
<Item key={item}>{menuMap[item]}</Item>
));
}; };
const renderChildren = () => { const renderChildren = () => {
const { selectKey } = initConfig; const { selectKey } = initConfig;
switch (selectKey) { switch (selectKey) {
case "base": case 'base':
return <BaseView />; return <BaseView />;
case "security": case 'security':
return <SecurityView />; return <SecurityView />;
case "binding": case 'binding':
return <BindingView />; return <BindingView />;
case "notification": case 'notification':
return <NotificationView />; return <NotificationView />;
default: default:
return null; return null;

78
src/pages/account/settings/style.style.ts

@ -1,73 +1,73 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
main: { main: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
flexDirection: "column", flexDirection: 'column',
}, },
}, },
leftMenu: { leftMenu: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
width: "100%", width: '100%',
border: "none", border: 'none',
}, },
}, },
".ant-menu-inline": { '.ant-menu-inline': {
border: "none", border: 'none',
}, },
".ant-menu-horizontal": { '.ant-menu-horizontal': {
fontWeight: "bold", fontWeight: 'bold',
}, },
right: { right: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
padding: "40px", padding: '40px',
}, },
}, },
title: { title: {
marginBottom: "12px", marginBottom: '12px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
fontSize: "20px", fontSize: '20px',
lineHeight: "28px", lineHeight: '28px',
}, },
".ant-list-split .ant-list-item:last-child": { '.ant-list-split .ant-list-item:last-child': {
borderBottom: "1px solid @border-color-split", borderBottom: '1px solid @border-color-split',
}, },
".ant-list-item": { '.ant-list-item': {
paddingTop: "14px", paddingTop: '14px',
paddingBottom: "14px", paddingBottom: '14px',
}, },
".ant-list-item-meta": {}, '.ant-list-item-meta': {},
"// 账号绑定图标 .taobao": { '// 账号绑定图标 .taobao': {
display: "block", display: 'block',
color: "#ff4000", color: '#ff4000',
fontSize: "48px", fontSize: '48px',
lineHeight: "48px", lineHeight: '48px',
borderRadius: token.borderRadius, borderRadius: token.borderRadius,
}, },
dingding: { dingding: {
margin: "2px", margin: '2px',
padding: "6px", padding: '6px',
color: "#fff", color: '#fff',
fontSize: "32px", fontSize: '32px',
lineHeight: "32px", lineHeight: '32px',
backgroundColor: "#2eabff", backgroundColor: '#2eabff',
borderRadius: token.borderRadius, borderRadius: token.borderRadius,
}, },
alipay: { alipay: {
color: "#2eabff", color: '#2eabff',
fontSize: "48px", fontSize: '48px',
lineHeight: "48px", lineHeight: '48px',
borderRadius: token.borderRadius, borderRadius: token.borderRadius,
}, },
"// 密码强度 font.strong": { '// 密码强度 font.strong': {
color: token.colorSuccess, color: token.colorSuccess,
}, },
"font.medium": { 'font.medium': {
color: token.colorWarning, color: token.colorWarning,
}, },
"font.weak": { 'font.weak': {
color: token.colorError, color: token.colorError,
}, },
}; };

31
src/pages/dashboard/analysis/components/Charts/Bar/index.tsx

@ -1,8 +1,8 @@
import { Axis, Chart, Geom, Tooltip } from "bizcharts"; import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import React, { Component } from "react"; import React, { Component } from 'react';
import Debounce from "lodash.debounce"; import Debounce from 'lodash.debounce';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "../index.style"; import useStyles from '../index.style';
export type BarProps = { export type BarProps = {
title: React.ReactNode; title: React.ReactNode;
color?: string; color?: string;
@ -51,12 +51,12 @@ class Bar extends Component<
} }
}, 500); }, 500);
componentDidMount() { componentDidMount() {
window.addEventListener("resize", this.resize, { window.addEventListener('resize', this.resize, {
passive: true, passive: true,
}); });
} }
componentWillUnmount() { componentWillUnmount() {
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
} }
handleRoot = (n: HTMLDivElement) => { handleRoot = (n: HTMLDivElement) => {
this.root = n; this.root = n;
@ -70,13 +70,13 @@ class Bar extends Component<
title, title,
forceFit = true, forceFit = true,
data, data,
color = "rgba(24, 144, 255, 0.85)", color = 'rgba(24, 144, 255, 0.85)',
padding, padding,
} = this.props; } = this.props;
const { autoHideXLabels } = this.state; const { autoHideXLabels } = this.state;
const scale = { const scale = {
x: { x: {
type: "cat", type: 'cat',
}, },
y: { y: {
min: 0, min: 0,
@ -87,9 +87,9 @@ class Bar extends Component<
(...args: any[]) => { (...args: any[]) => {
name?: string; name?: string;
value: string; value: string;
} },
] = [ ] = [
"x*y", 'x*y',
(x: string, y: string) => ({ (x: string, y: string) => ({
name: x, name: x,
value: y, value: y,
@ -118,7 +118,7 @@ class Bar extends Component<
height={title ? height - 41 : height} height={title ? height - 41 : height}
forceFit={forceFit} forceFit={forceFit}
data={data} data={data}
padding={padding || "auto"} padding={padding || 'auto'}
> >
<Axis <Axis
name="x" name="x"
@ -128,12 +128,7 @@ class Bar extends Component<
/> />
<Axis name="y" min={0} /> <Axis name="y" min={0} />
<Tooltip showTitle={false} crosshairs={false} /> <Tooltip showTitle={false} crosshairs={false} />
<Geom <Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
type="interval"
position="x*y"
color={color}
tooltip={tooltip}
/>
</Chart> </Chart>
</div> </div>
</div> </div>

82
src/pages/dashboard/analysis/components/Charts/ChartCard/index.style.ts

@ -1,77 +1,77 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
chartCard: { chartCard: {
position: "relative", position: 'relative',
}, },
chartTop: { chartTop: {
position: "relative", position: 'relative',
width: "100%", width: '100%',
overflow: "hidden", overflow: 'hidden',
}, },
chartTopMargin: { chartTopMargin: {
marginBottom: "12px", marginBottom: '12px',
}, },
chartTopHasMargin: { chartTopHasMargin: {
marginBottom: "20px", marginBottom: '20px',
}, },
metaWrap: { metaWrap: {
float: "left", float: 'left',
}, },
avatar: { avatar: {
position: "relative", position: 'relative',
top: "4px", top: '4px',
float: "left", float: 'left',
marginRight: "20px", marginRight: '20px',
}, },
img: { img: {
borderRadius: "100%", borderRadius: '100%',
}, },
meta: { meta: {
height: "22px", height: '22px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: token.fontSize, fontSize: token.fontSize,
lineHeight: "22px", lineHeight: '22px',
}, },
action: { action: {
position: "absolute", position: 'absolute',
top: "4px", top: '4px',
right: "0", right: '0',
lineHeight: "1", lineHeight: '1',
cursor: "pointer", cursor: 'pointer',
}, },
total: { total: {
height: "38px", height: '38px',
marginTop: "4px", marginTop: '4px',
marginBottom: "0", marginBottom: '0',
overflow: "hidden", overflow: 'hidden',
color: token.colorTextHeading, color: token.colorTextHeading,
fontSize: "30px", fontSize: '30px',
lineHeight: "38px", lineHeight: '38px',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
wordBreak: "break-all", wordBreak: 'break-all',
}, },
content: { content: {
position: "relative", position: 'relative',
width: "100%", width: '100%',
marginBottom: "12px", marginBottom: '12px',
}, },
contentFixed: { contentFixed: {
position: "absolute", position: 'absolute',
bottom: "0", bottom: '0',
left: "0", left: '0',
width: "100%", width: '100%',
}, },
footer: { footer: {
marginTop: "8px", marginTop: '8px',
paddingTop: "9px", paddingTop: '9px',
borderTop: "1px solid @border-color-split", borderTop: '1px solid @border-color-split',
"& > *": { position: "relative" }, '& > *': { position: 'relative' },
}, },
footerMargin: { footerMargin: {
marginTop: "20px", marginTop: '20px',
}, },
}; };
}); });

14
src/pages/dashboard/analysis/components/Charts/Field/index.style.ts

@ -1,19 +1,19 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
field: { field: {
margin: "0", margin: '0',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
}, },
label: { label: {
fontSize: token.fontSize, fontSize: token.fontSize,
lineHeight: "22px", lineHeight: '22px',
}, },
number: { number: {
marginLeft: "8px", marginLeft: '8px',
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
}; };

4
src/pages/dashboard/analysis/components/Charts/Field/index.tsx

@ -1,5 +1,5 @@
import React from "react"; import React from 'react';
import useStyles from "./index.style"; import useStyles from './index.style';
export type FieldProps = { export type FieldProps = {
label: React.ReactNode; label: React.ReactNode;
value: React.ReactNode; value: React.ReactNode;

22
src/pages/dashboard/analysis/components/Charts/MiniArea/index.tsx

@ -1,8 +1,8 @@
import type { AxisProps } from "bizcharts"; import type { AxisProps } from 'bizcharts';
import { Axis, Chart, Geom, Tooltip } from "bizcharts"; import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import React from "react"; import React from 'react';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "../index.style"; import useStyles from '../index.style';
export type MiniAreaProps = { export type MiniAreaProps = {
color?: string; color?: string;
height?: number; height?: number;
@ -32,8 +32,8 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
height = 1, height = 1,
data = [], data = [],
forceFit = true, forceFit = true,
color = "rgba(24, 144, 255, 0.2)", color = 'rgba(24, 144, 255, 0.2)',
borderColor = "#1089ff", borderColor = '#1089ff',
scale = { scale = {
x: {}, x: {},
y: {}, y: {},
@ -47,7 +47,7 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
const padding: [number, number, number, number] = [36, 5, 30, 5]; const padding: [number, number, number, number] = [36, 5, 30, 5];
const scaleProps = { const scaleProps = {
x: { x: {
type: "cat", type: 'cat',
range: [0, 1], range: [0, 1],
...scale.x, ...scale.x,
}, },
@ -61,9 +61,9 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
(...args: any[]) => { (...args: any[]) => {
name?: string; name?: string;
value: string; value: string;
} },
] = [ ] = [
"x*y", 'x*y',
(x: string, y: string) => ({ (x: string, y: string) => ({
name: x, name: x,
value: y, value: y,
@ -128,7 +128,7 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
) : ( ) : (
<span <span
style={{ style={{
display: "none", display: 'none',
}} }}
/> />
)} )}

31
src/pages/dashboard/analysis/components/Charts/MiniBar/index.tsx

@ -1,7 +1,7 @@
import { Chart, Geom, Tooltip } from "bizcharts"; import { Chart, Geom, Tooltip } from 'bizcharts';
import React from "react"; import React from 'react';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "../index.style"; import useStyles from '../index.style';
export type MiniBarProps = { export type MiniBarProps = {
color?: string; color?: string;
height?: number; height?: number;
@ -14,10 +14,10 @@ export type MiniBarProps = {
}; };
const MiniBar: React.FC<MiniBarProps> = (props) => { const MiniBar: React.FC<MiniBarProps> = (props) => {
const { styles } = useStyles(); const { styles } = useStyles();
const { height = 0, forceFit = true, color = "#1890FF", data = [] } = props; const { height = 0, forceFit = true, color = '#1890FF', data = [] } = props;
const scale = { const scale = {
x: { x: {
type: "cat", type: 'cat',
}, },
y: { y: {
min: 0, min: 0,
@ -29,9 +29,9 @@ const MiniBar: React.FC<MiniBarProps> = (props) => {
(...args: any[]) => { (...args: any[]) => {
name?: string; name?: string;
value: string; value: string;
} },
] = [ ] = [
"x*y", 'x*y',
(x: string, y: string) => ({ (x: string, y: string) => ({
name: x, name: x,
value: y, value: y,
@ -48,20 +48,9 @@ const MiniBar: React.FC<MiniBarProps> = (props) => {
}} }}
> >
<div className={styles.chartContent}> <div className={styles.chartContent}>
<Chart <Chart scale={scale} height={chartHeight} forceFit={forceFit} data={data} padding={padding}>
scale={scale}
height={chartHeight}
forceFit={forceFit}
data={data}
padding={padding}
>
<Tooltip showTitle={false} crosshairs={false} /> <Tooltip showTitle={false} crosshairs={false} />
<Geom <Geom type="interval" position="x*y" color={color} tooltip={tooltip} />
type="interval"
position="x*y"
color={color}
tooltip={tooltip}
/>
</Chart> </Chart>
</div> </div>
</div> </div>

8
src/pages/dashboard/analysis/components/Charts/MiniProgress/index.tsx

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import { Tooltip } from "antd"; import { Tooltip } from 'antd';
import useStyles from "./index.style"; import useStyles from './index.style';
export type MiniProgressProps = { export type MiniProgressProps = {
target: number; target: number;
targetLabel?: string; targetLabel?: string;
@ -12,7 +12,7 @@ export type MiniProgressProps = {
const MiniProgress: React.FC<MiniProgressProps> = ({ const MiniProgress: React.FC<MiniProgressProps> = ({
targetLabel, targetLabel,
target, target,
color = "rgb(19, 194, 194)", color = 'rgb(19, 194, 194)',
strokeWidth, strokeWidth,
percent, percent,
}) => { }) => {

90
src/pages/dashboard/analysis/components/Charts/Pie/index.tsx

@ -1,12 +1,13 @@
import { Chart, Coord, Geom, Tooltip } from "bizcharts"; import { Chart, Coord, Geom, Tooltip } from 'bizcharts';
import React, { Component } from "react"; import React, { Component } from 'react';
import { DataView } from "@antv/data-set"; import { DataView } from '@antv/data-set';
import Debounce from "lodash.debounce"; import Debounce from 'lodash.debounce';
import { Divider } from "antd"; import { Divider } from 'antd';
import ReactFitText from "react-fittext"; import ReactFitText from 'react-fittext';
import classNames from "classnames"; import classNames from 'classnames';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
export type PieProps = { export type PieProps = {
animate?: boolean; animate?: boolean;
color?: string; color?: string;
@ -56,7 +57,7 @@ class Pie extends Component<PieProps, PieState> {
const { hasLegend } = this.props; const { hasLegend } = this.props;
const { legendBlock } = this.state; const { legendBlock } = this.state;
if (!hasLegend || !this.root) { if (!hasLegend || !this.root) {
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
return; return;
} }
if ( if (
@ -77,13 +78,13 @@ class Pie extends Component<PieProps, PieState> {
}, 400); }, 400);
componentDidMount() { componentDidMount() {
window.addEventListener( window.addEventListener(
"resize", 'resize',
() => { () => {
this.requestRef = requestAnimationFrame(() => this.resize()); this.requestRef = requestAnimationFrame(() => this.resize());
}, },
{ {
passive: true, passive: true,
} },
); );
} }
componentDidUpdate(preProps: PieProps) { componentDidUpdate(preProps: PieProps) {
@ -98,7 +99,7 @@ class Pie extends Component<PieProps, PieState> {
if (this.requestRef) { if (this.requestRef) {
window.cancelAnimationFrame(this.requestRef); window.cancelAnimationFrame(this.requestRef);
} }
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
if (this.resize) { if (this.resize) {
(this.resize as any).cancel(); (this.resize as any).cancel();
} }
@ -116,21 +117,21 @@ class Pie extends Component<PieProps, PieState> {
if (!this.chart) return; if (!this.chart) return;
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形 const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return; if (!geom) return;
const items = (geom as any).get("dataArray") || []; // 获取图形对应的 const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map( const legendData = items.map(
( (
item: { item: {
color: any; color: any;
_origin: any; _origin: any;
}[] }[],
) => { ) => {
/* eslint no-underscore-dangle:0 */ /* eslint no-underscore-dangle:0 */
const origin = item[0]._origin; const origin = item[0]._origin;
origin.color = item[0].color; origin.color = item[0].color;
origin.checked = true; origin.checked = true;
return origin; return origin;
} },
); );
this.setState({ this.setState({
legendData, legendData,
@ -145,14 +146,9 @@ class Pie extends Component<PieProps, PieState> {
const { legendData } = this.state; const { legendData } = this.state;
const key = i as unknown as number; const key = i as unknown as number;
legendData[key] = newItem; legendData[key] = newItem;
const filteredLegendData = legendData const filteredLegendData = legendData.filter((l) => l.checked).map((l) => l.x);
.filter((l) => l.checked)
.map((l) => l.x);
if (this.chart) { if (this.chart) {
this.chart.filter( this.chart.filter('x', (val) => filteredLegendData.indexOf(`${val}`) > -1);
"x",
(val) => filteredLegendData.indexOf(`${val}`) > -1
);
} }
this.setState({ this.setState({
legendData, legendData,
@ -195,7 +191,7 @@ class Pie extends Component<PieProps, PieState> {
let formatColor; let formatColor;
const scale = { const scale = {
x: { x: {
type: "cat", type: 'cat',
range: [0, 1], range: [0, 1],
}, },
y: { y: {
@ -206,18 +202,18 @@ class Pie extends Component<PieProps, PieState> {
selected = false; selected = false;
tooltip = false; tooltip = false;
formatColor = (value: string) => { formatColor = (value: string) => {
if (value === "占比") { if (value === '占比') {
return color || "rgba(24, 144, 255, 0.85)"; return color || 'rgba(24, 144, 255, 0.85)';
} }
return "#F0F2F5"; return '#F0F2F5';
}; };
data = [ data = [
{ {
x: "占比", x: '占比',
y: parseFloat(`${percent}`), y: parseFloat(`${percent}`),
}, },
{ {
x: "反比", x: '反比',
y: 100 - parseFloat(`${percent}`), y: 100 - parseFloat(`${percent}`),
}, },
]; ];
@ -227,9 +223,9 @@ class Pie extends Component<PieProps, PieState> {
(...args: any[]) => { (...args: any[]) => {
name?: string; name?: string;
value: string; value: string;
} },
] = [ ] = [
"x*percent", 'x*percent',
(x: string, p: number) => ({ (x: string, p: number) => ({
name: x, name: x,
value: `${(p * 100).toFixed(2)}%`, value: `${(p * 100).toFixed(2)}%`,
@ -238,10 +234,10 @@ class Pie extends Component<PieProps, PieState> {
const padding = [12, 0, 12, 0] as [number, number, number, number]; const padding = [12, 0, 12, 0] as [number, number, number, number];
const dv = new DataView(); const dv = new DataView();
dv.source(data).transform({ dv.source(data).transform({
type: "percent", type: 'percent',
field: "y", field: 'y',
dimension: "x", dimension: 'x',
as: "percent", as: 'percent',
}); });
return ( return (
<div ref={this.handleRoot} className={pieClassName} style={style}> <div ref={this.handleRoot} className={pieClassName} style={style}>
@ -261,17 +257,12 @@ class Pie extends Component<PieProps, PieState> {
<Geom <Geom
style={{ style={{
lineWidth, lineWidth,
stroke: "#fff", stroke: '#fff',
}} }}
tooltip={tooltip ? tooltipFormat : undefined} tooltip={tooltip ? tooltipFormat : undefined}
type="intervalStack" type="intervalStack"
position="percent" position="percent"
color={ color={['x', percent || percent === 0 ? formatColor : defaultColors] as any}
[
"x",
percent || percent === 0 ? formatColor : defaultColors,
] as any
}
selected={selected} selected={selected}
/> />
</Chart> </Chart>
@ -281,9 +272,7 @@ class Pie extends Component<PieProps, PieState> {
{subTitle && <h4 className="pie-sub-title">{subTitle}</h4>} {subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
{total && ( {total && (
<div className="pie-stat"> <div className="pie-stat">{typeof total === 'function' ? total() : total}</div>
{typeof total === "function" ? total() : total}
</div>
)} )}
</div> </div>
)} )}
@ -297,20 +286,15 @@ class Pie extends Component<PieProps, PieState> {
<span <span
className={styles.dot} className={styles.dot}
style={{ style={{
backgroundColor: !item.checked ? "#aaa" : item.color, backgroundColor: !item.checked ? '#aaa' : item.color,
}} }}
/> />
<span className={styles.legendTitle}>{item.x}</span> <span className={styles.legendTitle}>{item.x}</span>
<Divider type="vertical" /> <Divider type="vertical" />
<span className={styles.percent}> <span className={styles.percent}>
{`${(Number.isNaN(item.percent) {`${(Number.isNaN(item.percent) ? 0 : item.percent * 100).toFixed(2)}%`}
? 0
: item.percent * 100
).toFixed(2)}%`}
</span>
<span className={styles.value}>
{valueFormat ? valueFormat(item.y) : item.y}
</span> </span>
<span className={styles.value}>{valueFormat ? valueFormat(item.y) : item.y}</span>
</li> </li>
))} ))}
</ul> </ul>

47
src/pages/dashboard/analysis/components/Charts/TagCloud/index.tsx

@ -1,16 +1,15 @@
import { Chart, Coord, Geom, Shape, Tooltip } from "bizcharts"; import { Chart, Coord, Geom, Shape, Tooltip } from 'bizcharts';
import React, { Component } from "react"; import React, { Component } from 'react';
import DataSet from "@antv/data-set"; import DataSet from '@antv/data-set';
import Debounce from "lodash.debounce"; import Debounce from 'lodash.debounce';
import classNames from "classnames"; import classNames from 'classnames';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
/* eslint no-underscore-dangle: 0 */ /* eslint no-underscore-dangle: 0 */
/* eslint no-param-reassign: 0 */ /* eslint no-param-reassign: 0 */
const imgUrl = const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
"https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png";
export type TagCloudProps = { export type TagCloudProps = {
data: { data: {
name: string; name: string;
@ -40,7 +39,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
this.initTagCloud(); this.initTagCloud();
this.renderChart(this.props); this.renderChart(this.props);
}); });
window.addEventListener("resize", this.resize, { window.addEventListener('resize', this.resize, {
passive: true, passive: true,
}); });
} }
@ -53,7 +52,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
componentWillUnmount() { componentWillUnmount() {
this.isUnmount = true; this.isUnmount = true;
window.cancelAnimationFrame(this.requestRef); window.cancelAnimationFrame(this.requestRef);
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
} }
resize = () => { resize = () => {
this.requestRef = requestAnimationFrame(() => { this.requestRef = requestAnimationFrame(() => {
@ -78,13 +77,13 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
fontSize: cfg.origin._origin.size, fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate, rotate: cfg.origin._origin.rotate,
text: cfg.origin._origin.text, text: cfg.origin._origin.text,
textAlign: "center", textAlign: 'center',
fontFamily: cfg.origin._origin.font, fontFamily: cfg.origin._origin.font,
fill: cfg.color, fill: cfg.color,
textBaseline: "Alphabetic", textBaseline: 'Alphabetic',
}; };
} }
(Shape as any).registerShape("point", "cloud", { (Shape as any).registerShape('point', 'cloud', {
drawShape( drawShape(
cfg: { cfg: {
x: any; x: any;
@ -95,12 +94,12 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
arg0: string, arg0: string,
arg1: { arg1: {
attrs: any; attrs: any;
} },
) => void; ) => void;
} },
) { ) {
const attrs = getTextAttrs(cfg); const attrs = getTextAttrs(cfg);
return container.addShape("text", { return container.addShape('text', {
attrs: { attrs: {
...attrs, ...attrs,
x: cfg.x, x: cfg.x,
@ -120,13 +119,13 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
const w = this.root.offsetWidth; const w = this.root.offsetWidth;
const onload = () => { const onload = () => {
const dv = new DataSet.View().source(data); const dv = new DataSet.View().source(data);
const range = dv.range("value"); const range = dv.range('value');
const [min, max] = range; const [min, max] = range;
dv.transform({ dv.transform({
type: "tag-cloud", type: 'tag-cloud',
fields: ["name", "value"], fields: ['name', 'value'],
imageMask: this.imageMask, imageMask: this.imageMask,
font: "Verdana", font: 'Verdana',
size: [w, h], size: [w, h],
// 宽高设置最好根据 imageMask 做调整 // 宽高设置最好根据 imageMask 做调整
padding: 0, padding: 0,
@ -151,7 +150,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
}; };
if (!this.imageMask) { if (!this.imageMask) {
this.imageMask = new Image(); this.imageMask = new Image();
this.imageMask.crossOrigin = ""; this.imageMask.crossOrigin = '';
this.imageMask.src = imgUrl; this.imageMask.src = imgUrl;
this.imageMask.onload = onload; this.imageMask.onload = onload;
} else { } else {
@ -165,7 +164,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
<div <div
className={classNames(styles.tagCloud, className)} className={classNames(styles.tagCloud, className)}
style={{ style={{
width: "100%", width: '100%',
height, height,
}} }}
ref={this.saveRootRef} ref={this.saveRootRef}
@ -193,7 +192,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
color="text" color="text"
shape="cloud" shape="cloud"
tooltip={[ tooltip={[
"text*value", 'text*value',
function trans(text, value) { function trans(text, value) {
return { return {
name: text, name: text,

58
src/pages/dashboard/analysis/components/Charts/TimelineChart/index.tsx

@ -1,9 +1,9 @@
import { Axis, Chart, Geom, Legend, Tooltip } from "bizcharts"; import { Axis, Chart, Geom, Legend, Tooltip } from 'bizcharts';
import DataSet from "@antv/data-set"; import DataSet from '@antv/data-set';
import React from "react"; import React from 'react';
import Slider from "bizcharts-plugin-slider"; import Slider from 'bizcharts-plugin-slider';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
export type TimelineChartProps = { export type TimelineChartProps = {
data: { data: {
x: number; x: number;
@ -27,8 +27,8 @@ const TimelineChart: React.FC<TimelineChartProps> = (props) => {
height = 400, height = 400,
padding = [60, 20, 40, 40] as [number, number, number, number], padding = [60, 20, 40, 40] as [number, number, number, number],
titleMap = { titleMap = {
y1: "y1", y1: 'y1',
y2: "y2", y2: 'y2',
}, },
borderWidth = 2, borderWidth = 2,
data: sourceData, data: sourceData,
@ -47,7 +47,7 @@ const TimelineChart: React.FC<TimelineChartProps> = (props) => {
if (data[0] && data[0].y1 && data[0].y2) { if (data[0] && data[0].y1 && data[0].y2) {
max = Math.max( max = Math.max(
[...data].sort((a, b) => b.y1 - a.y1)[0].y1, [...data].sort((a, b) => b.y1 - a.y1)[0].y1,
[...data].sort((a, b) => b.y2 - a.y2)[0].y2 [...data].sort((a, b) => b.y2 - a.y2)[0].y2,
); );
} }
const ds = new DataSet({ const ds = new DataSet({
@ -59,36 +59,36 @@ const TimelineChart: React.FC<TimelineChartProps> = (props) => {
const dv = ds.createView(); const dv = ds.createView();
dv.source(data) dv.source(data)
.transform({ .transform({
type: "filter", type: 'filter',
callback: (obj: { x: string }) => { callback: (obj: { x: string }) => {
const date = obj.x; const date = obj.x;
return date <= ds.state.end && date >= ds.state.start; return date <= ds.state.end && date >= ds.state.start;
}, },
}) })
.transform({ .transform({
type: "map", type: 'map',
callback(row: { y1: string; y2: string }) { callback(row: { y1: string; y2: string }) {
const newRow = { const newRow = {
...row, ...row,
}; };
newRow[titleMap.y1 as "y1"] = row.y1; newRow[titleMap.y1 as 'y1'] = row.y1;
newRow[titleMap.y2 as "y2"] = row.y2; newRow[titleMap.y2 as 'y2'] = row.y2;
return newRow; return newRow;
}, },
}) })
.transform({ .transform({
type: "fold", type: 'fold',
fields: [titleMap.y1, titleMap.y2], fields: [titleMap.y1, titleMap.y2],
// 展开字段集 // 展开字段集
key: "key", key: 'key',
// key字段 // key字段
value: "value", // value字段 value: 'value', // value字段
}); });
const timeScale = { const timeScale = {
type: "time", type: 'time',
tickInterval: 60 * 60 * 1000, tickInterval: 60 * 60 * 1000,
mask: "HH:mm", mask: 'HH:mm',
range: [0, 1], range: [0, 1],
}; };
const cols = { const cols = {
@ -112,17 +112,11 @@ const TimelineChart: React.FC<TimelineChartProps> = (props) => {
start={ds.state.start} start={ds.state.start}
end={ds.state.end} end={ds.state.end}
backgroundChart={{ backgroundChart={{
type: "line", type: 'line',
}} }}
onChange={({ onChange={({ startValue, endValue }: { startValue: string; endValue: string }) => {
startValue, ds.setState('start', startValue);
endValue, ds.setState('end', endValue);
}: {
startValue: string;
endValue: string;
}) => {
ds.setState("start", startValue);
ds.setState("end", endValue);
}} }}
/> />
); );
@ -135,13 +129,7 @@ const TimelineChart: React.FC<TimelineChartProps> = (props) => {
> >
<div> <div>
{title && <h4>{title}</h4>} {title && <h4>{title}</h4>}
<Chart <Chart height={height} padding={padding} data={dv} scale={cols} forceFit>
height={height}
padding={padding}
data={dv}
scale={cols}
forceFit
>
<Axis name="x" /> <Axis name="x" />
<Tooltip /> <Tooltip />
<Legend name="key" position="top" /> <Legend name="key" position="top" />

34
src/pages/dashboard/analysis/components/Charts/WaterWave/index.tsx

@ -1,6 +1,6 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
/* eslint no-return-assign: 0 */ /* eslint no-return-assign: 0 */
/* eslint no-mixed-operators: 0 */ /* eslint no-mixed-operators: 0 */
@ -24,28 +24,28 @@ class WaterWave extends Component<WaterWaveProps> {
this.renderChart(); this.renderChart();
this.resize(); this.resize();
window.addEventListener( window.addEventListener(
"resize", 'resize',
() => { () => {
requestAnimationFrame(() => this.resize()); requestAnimationFrame(() => this.resize());
}, },
{ {
passive: true, passive: true,
} },
); );
} }
componentDidUpdate(props: WaterWaveProps) { componentDidUpdate(props: WaterWaveProps) {
const { percent } = this.props; const { percent } = this.props;
if (props.percent !== percent) { if (props.percent !== percent) {
// 不加这个会造成绘制缓慢 // 不加这个会造成绘制缓慢
this.renderChart("update"); this.renderChart('update');
} }
} }
componentWillUnmount() { componentWillUnmount() {
cancelAnimationFrame(this.timer); cancelAnimationFrame(this.timer);
if (this.node) { if (this.node) {
this.node.innerHTML = ""; this.node.innerHTML = '';
} }
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
} }
resize = () => { resize = () => {
if (this.root) { if (this.root) {
@ -57,14 +57,14 @@ class WaterWave extends Component<WaterWaveProps> {
} }
}; };
renderChart(type?: string) { renderChart(type?: string) {
const { percent, color = "#1890FF" } = this.props; const { percent, color = '#1890FF' } = this.props;
const data = percent / 100; const data = percent / 100;
cancelAnimationFrame(this.timer); cancelAnimationFrame(this.timer);
if (!this.node || (data !== 0 && !data)) { if (!this.node || (data !== 0 && !data)) {
return; return;
} }
const canvas = this.node; const canvas = this.node;
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext('2d');
if (!ctx) { if (!ctx) {
return; return;
} }
@ -88,11 +88,7 @@ class WaterWave extends Component<WaterWaveProps> {
const bR = radius - lineWidth; const bR = radius - lineWidth;
const circleOffset = -(Math.PI / 2); const circleOffset = -(Math.PI / 2);
let circleLock = true; let circleLock = true;
for ( for (let i = circleOffset; i < circleOffset + 2 * Math.PI; i += 1 / (8 * Math.PI)) {
let i = circleOffset;
i < circleOffset + 2 * Math.PI;
i += 1 / (8 * Math.PI)
) {
arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]); arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]);
} }
const cStartPoint = arcStack.shift() as number[]; const cStartPoint = arcStack.shift() as number[];
@ -118,7 +114,7 @@ class WaterWave extends Component<WaterWaveProps> {
ctx.lineTo(xOffset, canvasHeight); ctx.lineTo(xOffset, canvasHeight);
ctx.lineTo(startPoint[0], startPoint[1]); ctx.lineTo(startPoint[0], startPoint[1]);
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight); const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
gradient.addColorStop(0, "#ffffff"); gradient.addColorStop(0, '#ffffff');
gradient.addColorStop(1, color); gradient.addColorStop(1, color);
ctx.fillStyle = gradient; ctx.fillStyle = gradient;
ctx.fill(); ctx.fill();
@ -129,7 +125,7 @@ class WaterWave extends Component<WaterWaveProps> {
return; return;
} }
ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.clearRect(0, 0, canvasWidth, canvasHeight);
if (circleLock && type !== "update") { if (circleLock && type !== 'update') {
if (arcStack.length) { if (arcStack.length) {
const temp = arcStack.shift() as number[]; const temp = arcStack.shift() as number[];
ctx.lineTo(temp[0], temp[1]); ctx.lineTo(temp[0], temp[1]);
@ -139,7 +135,7 @@ class WaterWave extends Component<WaterWaveProps> {
ctx.lineTo(cStartPoint[0], cStartPoint[1]); ctx.lineTo(cStartPoint[0], cStartPoint[1]);
ctx.stroke(); ctx.stroke();
arcStack = []; arcStack = [];
ctx.globalCompositeOperation = "destination-over"; ctx.globalCompositeOperation = 'destination-over';
ctx.beginPath(); ctx.beginPath();
ctx.lineWidth = lineWidth; ctx.lineWidth = lineWidth;
ctx.arc(radius, radius, bR, 0, 2 * Math.PI, true); ctx.arc(radius, radius, bR, 0, 2 * Math.PI, true);
@ -199,7 +195,7 @@ class WaterWave extends Component<WaterWaveProps> {
style={{ style={{
width: height, width: height,
height, height,
overflow: "hidden", overflow: 'hidden',
}} }}
> >
<canvas <canvas

26
src/pages/dashboard/analysis/components/Charts/index.style.ts

@ -1,25 +1,25 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return { return {
miniChart: { miniChart: {
position: "relative", position: 'relative',
width: "100%", width: '100%',
}, },
chartContent: { chartContent: {
position: "absolute", position: 'absolute',
bottom: "-28px", bottom: '-28px',
width: "100%", width: '100%',
}, },
"> div": { '> div': {
margin: "0 -5px", margin: '0 -5px',
overflow: "hidden", overflow: 'hidden',
}, },
chartLoading: { chartLoading: {
position: "absolute", position: 'absolute',
top: "16px", top: '16px',
left: "50%", left: '50%',
marginLeft: "-7px", marginLeft: '-7px',
}, },
}; };
}); });

55
src/pages/dashboard/analysis/components/IntroduceRow.tsx

@ -1,12 +1,12 @@
import { InfoCircleOutlined } from "@ant-design/icons"; import { InfoCircleOutlined } from '@ant-design/icons';
import { TinyArea, TinyColumn, Progress } from "@ant-design/charts"; import { TinyArea, TinyColumn, Progress } from '@ant-design/charts';
import { Col, Row, Tooltip } from "antd"; import { Col, Row, Tooltip } from 'antd';
import numeral from "numeral"; import numeral from 'numeral';
import { ChartCard, Field } from "./Charts"; import { ChartCard, Field } from './Charts';
import type { DataItem } from "../data.d"; import type { DataItem } from '../data.d';
import Trend from "./Trend"; import Trend from './Trend';
import Yuan from "../utils/Yuan"; import Yuan from '../utils/Yuan';
import useStyles from "../style.style"; import useStyles from '../style.style';
const topColResponsiveProps = { const topColResponsiveProps = {
xs: 24, xs: 24,
sm: 12, sm: 12,
@ -17,13 +17,7 @@ const topColResponsiveProps = {
marginBottom: 24, marginBottom: 24,
}, },
}; };
const IntroduceRow = ({ const IntroduceRow = ({ loading, visitData }: { loading: boolean; visitData: DataItem[] }) => {
loading,
visitData,
}: {
loading: boolean;
visitData: DataItem[];
}) => {
const { styles } = useStyles(); const { styles } = useStyles();
return ( return (
<Row gutter={24}> <Row gutter={24}>
@ -38,12 +32,7 @@ const IntroduceRow = ({
} }
loading={loading} loading={loading}
total={() => <Yuan>126560</Yuan>} total={() => <Yuan>126560</Yuan>}
footer={ footer={<Field label="日销售额" value={`${numeral(12423).format('0,0')}`} />}
<Field
label="日销售额"
value={`${numeral(12423).format("0,0")}`}
/>
}
contentHeight={46} contentHeight={46}
> >
<Trend <Trend
@ -72,10 +61,8 @@ const IntroduceRow = ({
<InfoCircleOutlined /> <InfoCircleOutlined />
</Tooltip> </Tooltip>
} }
total={numeral(8846).format("0,0")} total={numeral(8846).format('0,0')}
footer={ footer={<Field label="日访问量" value={numeral(1234).format('0,0')} />}
<Field label="日访问量" value={numeral(1234).format("0,0")} />
}
contentHeight={46} contentHeight={46}
> >
<TinyArea <TinyArea
@ -99,17 +86,11 @@ const IntroduceRow = ({
<InfoCircleOutlined /> <InfoCircleOutlined />
</Tooltip> </Tooltip>
} }
total={numeral(6560).format("0,0")} total={numeral(6560).format('0,0')}
footer={<Field label="转化率" value="60%" />} footer={<Field label="转化率" value="60%" />}
contentHeight={46} contentHeight={46}
> >
<TinyColumn <TinyColumn xField="x" height={46} forceFit yField="y" data={visitData} />
xField="x"
height={46}
forceFit
yField="y"
data={visitData}
/>
</ChartCard> </ChartCard>
</Col> </Col>
<Col {...topColResponsiveProps}> <Col {...topColResponsiveProps}>
@ -126,8 +107,8 @@ const IntroduceRow = ({
footer={ footer={
<div <div
style={{ style={{
whiteSpace: "nowrap", whiteSpace: 'nowrap',
overflow: "hidden", overflow: 'hidden',
}} }}
> >
<Trend <Trend
@ -157,7 +138,7 @@ const IntroduceRow = ({
{ {
value: 0.8, value: 0.8,
style: { style: {
stroke: "#13C2C2", stroke: '#13C2C2',
}, },
}, },
]} ]}

42
src/pages/dashboard/analysis/components/NumberInfo/index.style.ts

@ -1,48 +1,48 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
numberInfo: {}, numberInfo: {},
suffix: { suffix: {
marginLeft: "4px", marginLeft: '4px',
color: token.colorText, color: token.colorText,
fontSize: "16px", fontSize: '16px',
fontStyle: "normal", fontStyle: 'normal',
}, },
numberInfoTitle: { numberInfoTitle: {
marginBottom: "16px", marginBottom: '16px',
color: token.colorText, color: token.colorText,
fontSize: token.fontSizeLg, fontSize: token.fontSizeLg,
transition: "all 0.3s", transition: 'all 0.3s',
}, },
numberInfoSubTitle: { numberInfoSubTitle: {
height: "22px", height: '22px',
overflow: "hidden", overflow: 'hidden',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: token.fontSize, fontSize: token.fontSize,
lineHeight: "22px", lineHeight: '22px',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
wordBreak: "break-all", wordBreak: 'break-all',
}, },
numberInfoValue: { numberInfoValue: {
"& > span": { color: token.colorText }, '& > span': { color: token.colorText },
}, },
subTotal: { subTotal: {
marginRight: "0", marginRight: '0',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: token.fontSizeLg, fontSize: token.fontSizeLg,
verticalAlign: "top", verticalAlign: 'top',
}, },
".anticon": { '.anticon': {
marginLeft: "4px", marginLeft: '4px',
fontSize: "12px", fontSize: '12px',
transform: "scale(0.82)", transform: 'scale(0.82)',
}, },
".anticon-caret-up": { '.anticon-caret-up': {
color: token.red6, color: token.red6,
}, },
".anticon-caret-down": { '.anticon-caret-down': {
color: token.green6, color: token.green6,
}, },
numberInfolight: {}, numberInfolight: {},

23
src/pages/dashboard/analysis/components/NumberInfo/index.tsx

@ -1,12 +1,12 @@
import { CaretUpOutlined, CaretDownOutlined } from "@ant-design/icons"; import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
export type NumberInfoProps = { export type NumberInfoProps = {
title?: React.ReactNode | string; title?: React.ReactNode | string;
subTitle?: React.ReactNode | string; subTitle?: React.ReactNode | string;
total?: React.ReactNode | string; total?: React.ReactNode | string;
status?: "up" | "down"; status?: 'up' | 'down';
theme?: string; theme?: string;
gap?: number; gap?: number;
subTotal?: number; subTotal?: number;
@ -33,17 +33,14 @@ const NumberInfo: React.FC<NumberInfoProps> = ({
{...rest} {...rest}
> >
{title && ( {title && (
<div <div className={styles.numberInfoTitle} title={typeof title === 'string' ? title : ''}>
className={styles.numberInfoTitle}
title={typeof title === "string" ? title : ""}
>
{title} {title}
</div> </div>
)} )}
{subTitle && ( {subTitle && (
<div <div
className={styles.numberInfoSubTitle} className={styles.numberInfoSubTitle}
title={typeof subTitle === "string" ? subTitle : ""} title={typeof subTitle === 'string' ? subTitle : ''}
> >
{subTitle} {subTitle}
</div> </div>
@ -65,11 +62,7 @@ const NumberInfo: React.FC<NumberInfoProps> = ({
{(status || subTotal) && ( {(status || subTotal) && (
<span className={styles.subTotal}> <span className={styles.subTotal}>
{subTotal} {subTotal}
{status && status === "up" ? ( {status && status === 'up' ? <CaretUpOutlined /> : <CaretDownOutlined />}
<CaretUpOutlined />
) : (
<CaretDownOutlined />
)}
</span> </span>
)} )}
</div> </div>

25
src/pages/dashboard/analysis/components/OfflineData.tsx

@ -1,8 +1,8 @@
import { Card, Col, Row, Tabs } from "antd"; import { Card, Col, Row, Tabs } from 'antd';
import { RingProgress, Line } from "@ant-design/charts"; import { RingProgress, Line } from '@ant-design/charts';
import type { OfflineDataType, DataItem } from "../data.d"; import type { OfflineDataType, DataItem } from '../data.d';
import NumberInfo from "./NumberInfo"; import NumberInfo from './NumberInfo';
import useStyles from "../style.style"; import useStyles from '../style.style';
const CustomTab = ({ const CustomTab = ({
data, data,
currentTabKey: currentKey, currentTabKey: currentKey,
@ -14,7 +14,7 @@ const CustomTab = ({
gutter={8} gutter={8}
style={{ style={{
width: 138, width: 138,
margin: "8px 0", margin: '8px 0',
}} }}
> >
<Col span={12}> <Col span={12}>
@ -23,7 +23,7 @@ const CustomTab = ({
subTitle="转化率" subTitle="转化率"
gap={2} gap={2}
total={`${data.cvr * 100}%`} total={`${data.cvr * 100}%`}
theme={currentKey !== data.name ? "light" : undefined} theme={currentKey !== data.name ? 'light' : undefined}
/> />
</Col> </Col>
<Col <Col
@ -62,13 +62,10 @@ const OfflineData = ({
> >
<Tabs activeKey={activeKey} onChange={handleTabChange}> <Tabs activeKey={activeKey} onChange={handleTabChange}>
{offlineData.map((shop) => ( {offlineData.map((shop) => (
<TabPane <TabPane tab={<CustomTab data={shop} currentTabKey={activeKey} />} key={shop.name}>
tab={<CustomTab data={shop} currentTabKey={activeKey} />}
key={shop.name}
>
<div <div
style={{ style={{
padding: "0 24px", padding: '0 24px',
}} }}
> >
<Line <Line
@ -81,12 +78,12 @@ const OfflineData = ({
seriesField="type" seriesField="type"
interactions={[ interactions={[
{ {
type: "slider", type: 'slider',
cfg: {}, cfg: {},
}, },
]} ]}
legend={{ legend={{
position: "top-center", position: 'top-center',
}} }}
/> />
</div> </div>

30
src/pages/dashboard/analysis/components/ProportionSales.tsx

@ -1,11 +1,11 @@
import { Card, Radio, Typography } from "antd"; import { Card, Radio, Typography } from 'antd';
import numeral from "numeral"; import numeral from 'numeral';
import type { RadioChangeEvent } from "antd/es/radio"; import type { RadioChangeEvent } from 'antd/es/radio';
import { Donut } from "@ant-design/charts"; import { Donut } from '@ant-design/charts';
import type { DonutConfig } from "@ant-design/charts/es/donut"; import type { DonutConfig } from '@ant-design/charts/es/donut';
import React from "react"; import React from 'react';
import type { DataItem } from "../data.d"; import type { DataItem } from '../data.d';
import useStyles from "../style.style"; import useStyles from '../style.style';
const { Text } = Typography; const { Text } = Typography;
const ProportionSales = ({ const ProportionSales = ({
dropdownGroup, dropdownGroup,
@ -16,7 +16,7 @@ const ProportionSales = ({
}: { }: {
loading: boolean; loading: boolean;
dropdownGroup: React.ReactNode; dropdownGroup: React.ReactNode;
salesType: "all" | "online" | "stores"; salesType: 'all' | 'online' | 'stores';
salesPieData: DataItem[]; salesPieData: DataItem[];
handleChangeSalesType?: (e: RadioChangeEvent) => void; handleChangeSalesType?: (e: RadioChangeEvent) => void;
}) => { }) => {
@ -28,7 +28,7 @@ const ProportionSales = ({
bordered={false} bordered={false}
title="销售额类别占比" title="销售额类别占比"
style={{ style={{
height: "100%", height: '100%',
}} }}
extra={ extra={
<div className={styles.salesCardExtra}> <div className={styles.salesCardExtra}>
@ -57,18 +57,16 @@ const ProportionSales = ({
}} }}
label={{ label={{
visible: true, visible: true,
type: "spider", type: 'spider',
formatter: (text, item) => { formatter: (text, item) => {
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
return `${item._origin.x}: ${numeral(item._origin.y).format( return `${item._origin.x}: ${numeral(item._origin.y).format('0,0')}`;
"0,0"
)}`;
}, },
}} }}
statistic={ statistic={
{ {
totalLabel: "销售额", totalLabel: '销售额',
} as DonutConfig["statistic"] } as DonutConfig['statistic']
} }
/> />
</div> </div>

70
src/pages/dashboard/analysis/components/SalesCard.tsx

@ -1,11 +1,11 @@
import { Card, Col, DatePicker, Row, Tabs } from "antd"; import { Card, Col, DatePicker, Row, Tabs } from 'antd';
import type { RangePickerProps } from "antd/es/date-picker/generatePicker"; import type { RangePickerProps } from 'antd/es/date-picker/generatePicker';
import type dayjs from "dayjs"; import type dayjs from 'dayjs';
import { Column } from "@ant-design/charts"; import { Column } from '@ant-design/charts';
import numeral from "numeral"; import numeral from 'numeral';
import type { DataItem } from "../data.d"; import type { DataItem } from '../data.d';
import useStyles from "../style.style"; import useStyles from '../style.style';
export type TimeType = "today" | "week" | "month" | "year"; export type TimeType = 'today' | 'week' | 'month' | 'year';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const { TabPane } = Tabs; const { TabPane } = Tabs;
const rankingListData: { const rankingListData: {
@ -26,11 +26,11 @@ const SalesCard = ({
loading, loading,
selectDate, selectDate,
}: { }: {
rangePickerValue: RangePickerProps<dayjs.Dayjs>["value"]; rangePickerValue: RangePickerProps<dayjs.Dayjs>['value'];
isActive: (key: TimeType) => string; isActive: (key: TimeType) => string;
salesData: DataItem[]; salesData: DataItem[];
loading: boolean; loading: boolean;
handleRangePickerChange: RangePickerProps<dayjs.Dayjs>["onChange"]; handleRangePickerChange: RangePickerProps<dayjs.Dayjs>['onChange'];
selectDate: (key: TimeType) => void; selectDate: (key: TimeType) => void;
}) => { }) => {
const { styles } = useStyles(); const { styles } = useStyles();
@ -47,28 +47,16 @@ const SalesCard = ({
tabBarExtraContent={ tabBarExtraContent={
<div className={styles.salesExtraWrap}> <div className={styles.salesExtraWrap}>
<div className={styles.salesExtra}> <div className={styles.salesExtra}>
<a <a className={isActive('today')} onClick={() => selectDate('today')}>
className={isActive("today")}
onClick={() => selectDate("today")}
>
</a> </a>
<a <a className={isActive('week')} onClick={() => selectDate('week')}>
className={isActive("week")}
onClick={() => selectDate("week")}
>
</a> </a>
<a <a className={isActive('month')} onClick={() => selectDate('month')}>
className={isActive("month")}
onClick={() => selectDate("month")}
>
</a> </a>
<a <a className={isActive('year')} onClick={() => selectDate('year')}>
className={isActive("year")}
onClick={() => selectDate("year")}
>
</a> </a>
</div> </div>
@ -110,14 +98,14 @@ const SalesCard = ({
}} }}
title={{ title={{
visible: true, visible: true,
text: "销售趋势", text: '销售趋势',
style: { style: {
fontSize: 14, fontSize: 14,
}, },
}} }}
meta={{ meta={{
y: { y: {
alias: "销售量", alias: '销售量',
}, },
}} }}
/> />
@ -130,20 +118,15 @@ const SalesCard = ({
{rankingListData.map((item, i) => ( {rankingListData.map((item, i) => (
<li key={item.title}> <li key={item.title}>
<span <span
className={`${styles.rankingItemNumber} ${ className={`${styles.rankingItemNumber} ${i < 3 ? styles.active : ''}`}
i < 3 ? styles.active : ""
}`}
> >
{i + 1} {i + 1}
</span> </span>
<span <span className={styles.rankingItemTitle} title={item.title}>
className={styles.rankingItemTitle}
title={item.title}
>
{item.title} {item.title}
</span> </span>
<span className={styles.rankingItemValue}> <span className={styles.rankingItemValue}>
{numeral(item.total).format("0,0")} {numeral(item.total).format('0,0')}
</span> </span>
</li> </li>
))} ))}
@ -176,14 +159,14 @@ const SalesCard = ({
}} }}
title={{ title={{
visible: true, visible: true,
text: "访问量趋势", text: '访问量趋势',
style: { style: {
fontSize: 14, fontSize: 14,
}, },
}} }}
meta={{ meta={{
y: { y: {
alias: "访问量", alias: '访问量',
}, },
}} }}
/> />
@ -196,19 +179,14 @@ const SalesCard = ({
{rankingListData.map((item, i) => ( {rankingListData.map((item, i) => (
<li key={item.title}> <li key={item.title}>
<span <span
className={`${styles.rankingItemNumber} ${ className={`${styles.rankingItemNumber} ${i < 3 ? styles.active : ''}`}
i < 3 ? styles.active : ""
}`}
> >
{i + 1} {i + 1}
</span> </span>
<span <span className={styles.rankingItemTitle} title={item.title}>
className={styles.rankingItemTitle}
title={item.title}
>
{item.title} {item.title}
</span> </span>
<span>{numeral(item.total).format("0,0")}</span> <span>{numeral(item.total).format('0,0')}</span>
</li> </li>
))} ))}
</ul> </ul>

18
src/pages/dashboard/analysis/components/Trend/index.style.ts

@ -1,30 +1,30 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
trendItem: { trendItem: {
display: "inline-block", display: 'inline-block',
fontSize: token.fontSize, fontSize: token.fontSize,
lineHeight: "22px", lineHeight: '22px',
}, },
up: { up: {
color: token.red6, color: token.red6,
}, },
down: { down: {
top: "-1px", top: '-1px',
color: token.green6, color: token.green6,
}, },
span: { span: {
fontSize: "12px", fontSize: '12px',
transform: "scale(0.83)", transform: 'scale(0.83)',
}, },
"trendItemGrey .up, trendItemGrey .down": { 'trendItemGrey .up, trendItemGrey .down': {
color: token.colorText, color: token.colorText,
}, },
"reverseColor .up": { 'reverseColor .up': {
color: token.green6, color: token.green6,
}, },
"reverseColor .down": { 'reverseColor .down': {
color: token.red6, color: token.red6,
}, },
}; };

20
src/pages/dashboard/analysis/components/Trend/index.tsx

@ -1,10 +1,10 @@
import { CaretUpOutlined, CaretDownOutlined } from "@ant-design/icons"; import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons';
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
export type TrendProps = { export type TrendProps = {
colorful?: boolean; colorful?: boolean;
flag: "up" | "down"; flag: 'up' | 'down';
style?: React.CSSProperties; style?: React.CSSProperties;
reverseColor?: boolean; reverseColor?: boolean;
className?: string; className?: string;
@ -25,18 +25,14 @@ const Trend: React.FC<TrendProps> = ({
[styles.trendItemGrey]: !colorful, [styles.trendItemGrey]: !colorful,
[styles.reverseColor]: reverseColor && colorful, [styles.reverseColor]: reverseColor && colorful,
}, },
className className,
); );
return ( return (
<div <div {...rest} className={classString} title={typeof children === 'string' ? children : ''}>
{...rest}
className={classString}
title={typeof children === "string" ? children : ""}
>
<span>{children}</span> <span>{children}</span>
{flag && ( {flag && (
<span className={styles[flag]}> <span className={styles[flag]}>
{flag === "up" ? <CaretUpOutlined /> : <CaretDownOutlined />} {flag === 'up' ? <CaretUpOutlined /> : <CaretDownOutlined />}
</span> </span>
)} )}
</div> </div>

72
src/pages/dashboard/analysis/index.tsx

@ -1,35 +1,35 @@
import type { FC } from "react"; import type { FC } from 'react';
import { Suspense, useState } from "react"; import { Suspense, useState } from 'react';
import { EllipsisOutlined } from "@ant-design/icons"; import { EllipsisOutlined } from '@ant-design/icons';
import { Col, Dropdown, Menu, Row } from "antd"; import { Col, Dropdown, Menu, Row } from 'antd';
import { GridContent } from "@ant-design/pro-components"; import { GridContent } from '@ant-design/pro-components';
import type { RadioChangeEvent } from "antd/es/radio"; import type { RadioChangeEvent } from 'antd/es/radio';
import type { RangePickerProps } from "antd/es/date-picker/generatePicker"; import type { RangePickerProps } from 'antd/es/date-picker/generatePicker';
import type dayjs from "dayjs"; import type dayjs from 'dayjs';
import IntroduceRow from "./components/IntroduceRow"; import IntroduceRow from './components/IntroduceRow';
import SalesCard from "./components/SalesCard"; import SalesCard from './components/SalesCard';
import TopSearch from "./components/TopSearch"; import TopSearch from './components/TopSearch';
import ProportionSales from "./components/ProportionSales"; import ProportionSales from './components/ProportionSales';
import OfflineData from "./components/OfflineData"; import OfflineData from './components/OfflineData';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import { fakeChartData } from "./service"; import { fakeChartData } from './service';
import PageLoading from "./components/PageLoading"; import PageLoading from './components/PageLoading';
import type { TimeType } from "./components/SalesCard"; import type { TimeType } from './components/SalesCard';
import { getTimeDistance } from "./utils/utils"; import { getTimeDistance } from './utils/utils';
import type { AnalysisData } from "./data.d"; import type { AnalysisData } from './data.d';
import useStyles from "./style.style"; import useStyles from './style.style';
type RangePickerValue = RangePickerProps<dayjs.Dayjs>["value"]; type RangePickerValue = RangePickerProps<dayjs.Dayjs>['value'];
type AnalysisProps = { type AnalysisProps = {
dashboardAndanalysis: AnalysisData; dashboardAndanalysis: AnalysisData;
loading: boolean; loading: boolean;
}; };
type SalesType = "all" | "online" | "stores"; type SalesType = 'all' | 'online' | 'stores';
const Analysis: FC<AnalysisProps> = () => { const Analysis: FC<AnalysisProps> = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const [salesType, setSalesType] = useState<SalesType>("all"); const [salesType, setSalesType] = useState<SalesType>('all');
const [currentTabKey, setCurrentTabKey] = useState<string>(""); const [currentTabKey, setCurrentTabKey] = useState<string>('');
const [rangePickerValue, setRangePickerValue] = useState<RangePickerValue>( const [rangePickerValue, setRangePickerValue] = useState<RangePickerValue>(
getTimeDistance("year") getTimeDistance('year'),
); );
const { loading, data } = useRequest(fakeChartData); const { loading, data } = useRequest(fakeChartData);
const selectDate = (type: TimeType) => { const selectDate = (type: TimeType) => {
@ -40,31 +40,28 @@ const Analysis: FC<AnalysisProps> = () => {
}; };
const isActive = (type: TimeType) => { const isActive = (type: TimeType) => {
if (!rangePickerValue) { if (!rangePickerValue) {
return ""; return '';
} }
const value = getTimeDistance(type); const value = getTimeDistance(type);
if (!value) { if (!value) {
return ""; return '';
} }
if (!rangePickerValue[0] || !rangePickerValue[1]) { if (!rangePickerValue[0] || !rangePickerValue[1]) {
return ""; return '';
} }
if ( if (
rangePickerValue[0].isSame(value[0] as dayjs.Dayjs, "day") && rangePickerValue[0].isSame(value[0] as dayjs.Dayjs, 'day') &&
rangePickerValue[1].isSame(value[1] as dayjs.Dayjs, "day") rangePickerValue[1].isSame(value[1] as dayjs.Dayjs, 'day')
) { ) {
return styles.currentDate; return styles.currentDate;
} }
return ""; return '';
}; };
let salesPieData; let salesPieData;
if (salesType === "all") { if (salesType === 'all') {
salesPieData = data?.salesTypeData; salesPieData = data?.salesTypeData;
} else { } else {
salesPieData = salesPieData = salesType === 'online' ? data?.salesTypeDataOnline : data?.salesTypeDataOffline;
salesType === "online"
? data?.salesTypeDataOnline
: data?.salesTypeDataOffline;
} }
const menu = ( const menu = (
<Menu> <Menu>
@ -85,8 +82,7 @@ const Analysis: FC<AnalysisProps> = () => {
const handleTabChange = (key: string) => { const handleTabChange = (key: string) => {
setCurrentTabKey(key); setCurrentTabKey(key);
}; };
const activeKey = const activeKey = currentTabKey || (data?.offlineData[0] && data?.offlineData[0].name) || '';
currentTabKey || (data?.offlineData[0] && data?.offlineData[0].name) || "";
return ( return (
<GridContent> <GridContent>
<> <>

136
src/pages/dashboard/analysis/style.style.ts

@ -1,14 +1,14 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
iconGroup: {}, iconGroup: {},
"span.anticon": { 'span.anticon': {
marginLeft: "16px", marginLeft: '16px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
cursor: "pointer", cursor: 'pointer',
transition: "color 0.32s", transition: 'color 0.32s',
"&:hover": { color: token.colorText }, '&:hover': { color: token.colorText },
}, },
rankingList: { rankingList: {
[`@media screen and (max-width: ${token.screenLG}px)`]: {}, [`@media screen and (max-width: ${token.screenLG}px)`]: {},
@ -18,42 +18,42 @@ const useStyles = createStyles(({ token }) => {
}, },
span: { span: {
color: token.colorText, color: token.colorText,
fontSize: "14px", fontSize: '14px',
lineHeight: "22px", lineHeight: '22px',
}, },
rankingItemNumber: { rankingItemNumber: {
display: "inline-block", display: 'inline-block',
width: "20px", width: '20px',
height: "20px", height: '20px',
marginTop: "1.5px", marginTop: '1.5px',
marginRight: "16px", marginRight: '16px',
fontWeight: "600", fontWeight: '600',
fontSize: "12px", fontSize: '12px',
lineHeight: "20px", lineHeight: '20px',
textAlign: "center", textAlign: 'center',
backgroundColor: token.tagDefaultBg, backgroundColor: token.tagDefaultBg,
borderRadius: "20px", borderRadius: '20px',
}, },
active: { active: {
color: "#fff", color: '#fff',
backgroundColor: "#314659", backgroundColor: '#314659',
}, },
rankingItemTitle: { rankingItemTitle: {
flex: "1", flex: '1',
marginRight: "8px", marginRight: '8px',
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
}, },
salesExtra: { salesExtra: {
[`@media screen and (max-width: ${token.screenLG}px)`]: { [`@media screen and (max-width: ${token.screenLG}px)`]: {
display: "none", display: 'none',
}, },
}, },
a: { a: {
marginLeft: "24px", marginLeft: '24px',
color: token.colorText, color: token.colorText,
"&:hover": { color: token.colorPrimary }, '&:hover': { color: token.colorPrimary },
}, },
currentDate: { currentDate: {
color: token.colorPrimary, color: token.colorPrimary,
@ -63,82 +63,82 @@ const useStyles = createStyles(({ token }) => {
}, },
salesBar: { salesBar: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
padding: "16px", padding: '16px',
}, },
}, },
salesRank: { salesRank: {
padding: "0 32px 32px 72px", padding: '0 32px 32px 72px',
}, },
".ant-tabs-bar, .ant-tabs-nav-wrap": { '.ant-tabs-bar, .ant-tabs-nav-wrap': {
paddingLeft: "16px", paddingLeft: '16px',
}, },
".ant-tabs-nav .ant-tabs-tab": { '.ant-tabs-nav .ant-tabs-tab': {
paddingTop: "16px", paddingTop: '16px',
paddingBottom: "14px", paddingBottom: '14px',
lineHeight: "24px", lineHeight: '24px',
}, },
".ant-tabs-extra-content": { '.ant-tabs-extra-content': {
paddingRight: "24px", paddingRight: '24px',
lineHeight: "55px", lineHeight: '55px',
}, },
".ant-card-head": { '.ant-card-head': {
position: "relative", position: 'relative',
}, },
".ant-card-head-title": { '.ant-card-head-title': {
alignItems: "normal", alignItems: 'normal',
}, },
salesCardExtra: { salesCardExtra: {
height: "inherit", height: 'inherit',
}, },
salesTypeRadio: { salesTypeRadio: {
position: "absolute", position: 'absolute',
right: "54px", right: '54px',
bottom: "12px", bottom: '12px',
}, },
offlineCard: {}, offlineCard: {},
".ant-tabs-ink-bar": { '.ant-tabs-ink-bar': {
bottom: "auto", bottom: 'auto',
}, },
".ant-tabs-bar": { '.ant-tabs-bar': {
borderBottom: "none", borderBottom: 'none',
}, },
".ant-tabs-nav-container-scrolling": { '.ant-tabs-nav-container-scrolling': {
paddingRight: "40px", paddingRight: '40px',
paddingLeft: "40px", paddingLeft: '40px',
}, },
".ant-tabs-tab-prev-icon::before": { '.ant-tabs-tab-prev-icon::before': {
position: "relative", position: 'relative',
left: "6px", left: '6px',
}, },
".ant-tabs-tab-next-icon::before": { '.ant-tabs-tab-next-icon::before': {
position: "relative", position: 'relative',
right: "6px", right: '6px',
}, },
".ant-tabs-tab-active h4": { '.ant-tabs-tab-active h4': {
color: token.colorPrimary, color: token.colorPrimary,
}, },
trendText: { trendText: {
marginLeft: "8px", marginLeft: '8px',
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
"span:first-child": { 'span:first-child': {
[`@media screen and (max-width: ${token.screenLG}px)`]: { [`@media screen and (max-width: ${token.screenLG}px)`]: {
marginRight: "8px", marginRight: '8px',
}, },
}, },
rankingTitle: { rankingTitle: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
marginTop: "16px", marginTop: '16px',
}, },
}, },
salesExtraWrap: { salesExtraWrap: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
display: "none", display: 'none',
}, },
}, },
".ant-tabs-content": { '.ant-tabs-content': {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
paddingTop: "30px", paddingTop: '30px',
}, },
}, },
}; };

2
src/pages/dashboard/analysis/utils/utils.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return {}; return {};

67
src/pages/dashboard/monitor/components/ActiveChart/index.style.ts

@ -1,55 +1,54 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return { return {
activeChart: { activeChart: {
position: "relative", position: 'relative',
}, },
activeChartGrid: {}, activeChartGrid: {},
p: { p: {
position: "absolute", position: 'absolute',
top: "80px", top: '80px',
}, },
"p:last-child": { 'p:last-child': {
top: "115px", top: '115px',
}, },
activeChartLegend: { activeChartLegend: {
position: "relative", position: 'relative',
height: "20px", height: '20px',
marginTop: "8px", marginTop: '8px',
fontSize: "0", fontSize: '0',
lineHeight: "20px", lineHeight: '20px',
}, },
span: { span: {
display: "inline-block", display: 'inline-block',
width: "33.33%", width: '33.33%',
fontSize: "12px", fontSize: '12px',
textAlign: "center", textAlign: 'center',
}, },
"span:first-child": { 'span:first-child': {
textAlign: "left", textAlign: 'left',
}, },
"span:last-child": { 'span:last-child': {
textAlign: "right", textAlign: 'right',
}, },
dashedLine: { dashedLine: {
position: "relative", position: 'relative',
top: "-70px", top: '-70px',
left: "-3px", left: '-3px',
height: "1px", height: '1px',
}, },
line: { line: {
position: "absolute", position: 'absolute',
top: "0", top: '0',
left: "0", left: '0',
width: "100%", width: '100%',
height: "100%", height: '100%',
backgroundImage: backgroundImage: 'linear-gradient(to right, transparent 50%, #e9e9e9 50%)',
"linear-gradient(to right, transparent 50%, #e9e9e9 50%)", backgroundSize: '6px',
backgroundSize: "6px", },
}, 'dashedLine:last-child': {
"dashedLine:last-child": { top: '-36px',
top: "-36px",
}, },
}; };
}); });

22
src/pages/dashboard/monitor/components/Charts/MiniArea/index.tsx

@ -1,8 +1,8 @@
import type { AxisProps } from "bizcharts"; import type { AxisProps } from 'bizcharts';
import { Axis, Chart, Geom, Tooltip } from "bizcharts"; import { Axis, Chart, Geom, Tooltip } from 'bizcharts';
import React from "react"; import React from 'react';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "../index.style"; import useStyles from '../index.style';
export type MiniAreaProps = { export type MiniAreaProps = {
color?: string; color?: string;
height?: number; height?: number;
@ -32,8 +32,8 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
height = 1, height = 1,
data = [], data = [],
forceFit = true, forceFit = true,
color = "rgba(24, 144, 255, 0.2)", color = 'rgba(24, 144, 255, 0.2)',
borderColor = "#1089ff", borderColor = '#1089ff',
scale = { scale = {
x: {}, x: {},
y: {}, y: {},
@ -47,7 +47,7 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
const padding: [number, number, number, number] = [36, 5, 30, 5]; const padding: [number, number, number, number] = [36, 5, 30, 5];
const scaleProps = { const scaleProps = {
x: { x: {
type: "cat", type: 'cat',
range: [0, 1], range: [0, 1],
...scale.x, ...scale.x,
}, },
@ -61,9 +61,9 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
(...args: any[]) => { (...args: any[]) => {
name?: string; name?: string;
value: string; value: string;
} },
] = [ ] = [
"x*y", 'x*y',
(x: string, y: string) => ({ (x: string, y: string) => ({
name: x, name: x,
value: y, value: y,
@ -128,7 +128,7 @@ const MiniArea: React.FC<MiniAreaProps> = (props) => {
) : ( ) : (
<span <span
style={{ style={{
display: "none", display: 'none',
}} }}
/> />
)} )}

91
src/pages/dashboard/monitor/components/Charts/Pie/index.tsx

@ -1,12 +1,12 @@
import { Chart, Coord, Geom, Tooltip } from "bizcharts"; import { Chart, Coord, Geom, Tooltip } from 'bizcharts';
import React, { Component } from "react"; import React, { Component } from 'react';
import { DataView } from "@antv/data-set"; import { DataView } from '@antv/data-set';
import Debounce from "lodash.debounce"; import Debounce from 'lodash.debounce';
import { Divider } from "antd"; import { Divider } from 'antd';
import ReactFitText from "react-fittext"; import ReactFitText from 'react-fittext';
import classNames from "classnames"; import classNames from 'classnames';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
export type PieProps = { export type PieProps = {
animate?: boolean; animate?: boolean;
color?: string; color?: string;
@ -56,7 +56,7 @@ class Pie extends Component<PieProps, PieState> {
const { hasLegend } = this.props; const { hasLegend } = this.props;
const { legendBlock } = this.state; const { legendBlock } = this.state;
if (!hasLegend || !this.root) { if (!hasLegend || !this.root) {
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
return; return;
} }
if ( if (
@ -77,13 +77,13 @@ class Pie extends Component<PieProps, PieState> {
}, 300); }, 300);
componentDidMount() { componentDidMount() {
window.addEventListener( window.addEventListener(
"resize", 'resize',
() => { () => {
this.requestRef = requestAnimationFrame(() => this.resize()); this.requestRef = requestAnimationFrame(() => this.resize());
}, },
{ {
passive: true, passive: true,
} },
); );
} }
componentDidUpdate(preProps: PieProps) { componentDidUpdate(preProps: PieProps) {
@ -98,7 +98,7 @@ class Pie extends Component<PieProps, PieState> {
if (this.requestRef) { if (this.requestRef) {
window.cancelAnimationFrame(this.requestRef); window.cancelAnimationFrame(this.requestRef);
} }
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
if (this.resize) { if (this.resize) {
(this.resize as any).cancel(); (this.resize as any).cancel();
} }
@ -117,21 +117,21 @@ class Pie extends Component<PieProps, PieState> {
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形 const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形
if (!geom) return; if (!geom) return;
// g2 的类型有问题 // g2 的类型有问题
const items = (geom as any).get("dataArray") || []; // 获取图形对应的 const items = (geom as any).get('dataArray') || []; // 获取图形对应的
const legendData = items.map( const legendData = items.map(
( (
item: { item: {
color: any; color: any;
_origin: any; _origin: any;
}[] }[],
) => { ) => {
/* eslint no-underscore-dangle:0 */ /* eslint no-underscore-dangle:0 */
const origin = item[0]._origin; const origin = item[0]._origin;
origin.color = item[0].color; origin.color = item[0].color;
origin.checked = true; origin.checked = true;
return origin; return origin;
} },
); );
this.setState({ this.setState({
legendData, legendData,
@ -144,7 +144,7 @@ class Pie extends Component<PieProps, PieState> {
item: { item: {
checked: boolean; checked: boolean;
}, },
i: string | number i: string | number,
) => { ) => {
const newItem = { const newItem = {
...item, ...item,
@ -152,14 +152,9 @@ class Pie extends Component<PieProps, PieState> {
newItem.checked = !newItem.checked; newItem.checked = !newItem.checked;
const { legendData } = this.state; const { legendData } = this.state;
legendData[i as unknown as number] = newItem as any; legendData[i as unknown as number] = newItem as any;
const filteredLegendData = legendData const filteredLegendData = legendData.filter((l) => l.checked).map((l) => l.x);
.filter((l) => l.checked)
.map((l) => l.x);
if (this.chart) { if (this.chart) {
this.chart.filter( this.chart.filter('x', (val) => filteredLegendData.indexOf(`${val}`) > -1);
"x",
(val) => filteredLegendData.indexOf(`${val}`) > -1
);
} }
this.setState({ this.setState({
legendData, legendData,
@ -202,7 +197,7 @@ class Pie extends Component<PieProps, PieState> {
let formatColor; let formatColor;
const scale = { const scale = {
x: { x: {
type: "cat", type: 'cat',
range: [0, 1], range: [0, 1],
}, },
y: { y: {
@ -213,18 +208,18 @@ class Pie extends Component<PieProps, PieState> {
selected = false; selected = false;
tooltip = false; tooltip = false;
formatColor = (value: string) => { formatColor = (value: string) => {
if (value === "占比") { if (value === '占比') {
return color || "rgba(24, 144, 255, 0.85)"; return color || 'rgba(24, 144, 255, 0.85)';
} }
return "#F0F2F5"; return '#F0F2F5';
}; };
data = [ data = [
{ {
x: "占比", x: '占比',
y: parseFloat(`${percent}`), y: parseFloat(`${percent}`),
}, },
{ {
x: "反比", x: '反比',
y: 100 - parseFloat(`${percent}`), y: 100 - parseFloat(`${percent}`),
}, },
]; ];
@ -234,9 +229,9 @@ class Pie extends Component<PieProps, PieState> {
(...args: any[]) => { (...args: any[]) => {
name?: string; name?: string;
value: string; value: string;
} },
] = [ ] = [
"x*percent", 'x*percent',
(x: string, p: number) => ({ (x: string, p: number) => ({
name: x, name: x,
value: `${(p * 100).toFixed(2)}%`, value: `${(p * 100).toFixed(2)}%`,
@ -245,10 +240,10 @@ class Pie extends Component<PieProps, PieState> {
const padding = [12, 0, 12, 0] as [number, number, number, number]; const padding = [12, 0, 12, 0] as [number, number, number, number];
const dv = new DataView(); const dv = new DataView();
dv.source(data).transform({ dv.source(data).transform({
type: "percent", type: 'percent',
field: "y", field: 'y',
dimension: "x", dimension: 'x',
as: "percent", as: 'percent',
}); });
return ( return (
<div ref={this.handleRoot} className={pieClassName} style={style}> <div ref={this.handleRoot} className={pieClassName} style={style}>
@ -268,17 +263,12 @@ class Pie extends Component<PieProps, PieState> {
<Geom <Geom
style={{ style={{
lineWidth, lineWidth,
stroke: "#fff", stroke: '#fff',
}} }}
tooltip={tooltip ? tooltipFormat : undefined} tooltip={tooltip ? tooltipFormat : undefined}
type="intervalStack" type="intervalStack"
position="percent" position="percent"
color={ color={['x', percent || percent === 0 ? formatColor : defaultColors] as any}
[
"x",
percent || percent === 0 ? formatColor : defaultColors,
] as any
}
selected={selected} selected={selected}
/> />
</Chart> </Chart>
@ -288,9 +278,7 @@ class Pie extends Component<PieProps, PieState> {
{subTitle && <h4 className="pie-sub-title">{subTitle}</h4>} {subTitle && <h4 className="pie-sub-title">{subTitle}</h4>}
{/* eslint-disable-next-line */} {/* eslint-disable-next-line */}
{total && ( {total && (
<div className="pie-stat"> <div className="pie-stat">{typeof total === 'function' ? total() : total}</div>
{typeof total === "function" ? total() : total}
</div>
)} )}
</div> </div>
)} )}
@ -304,20 +292,15 @@ class Pie extends Component<PieProps, PieState> {
<span <span
className={styles.dot} className={styles.dot}
style={{ style={{
backgroundColor: !item.checked ? "#aaa" : item.color, backgroundColor: !item.checked ? '#aaa' : item.color,
}} }}
/> />
<span className={styles.legendTitle}>{item.x}</span> <span className={styles.legendTitle}>{item.x}</span>
<Divider type="vertical" /> <Divider type="vertical" />
<span className={styles.percent}> <span className={styles.percent}>
{`${(Number.isNaN(item.percent) {`${(Number.isNaN(item.percent) ? 0 : item.percent * 100).toFixed(2)}%`}
? 0
: item.percent * 100
).toFixed(2)}%`}
</span>
<span className={styles.value}>
{valueFormat ? valueFormat(item.y) : item.y}
</span> </span>
<span className={styles.value}>{valueFormat ? valueFormat(item.y) : item.y}</span>
</li> </li>
))} ))}
</ul> </ul>

47
src/pages/dashboard/monitor/components/Charts/TagCloud/index.tsx

@ -1,16 +1,15 @@
import { Chart, Coord, Geom, Shape, Tooltip } from "bizcharts"; import { Chart, Coord, Geom, Shape, Tooltip } from 'bizcharts';
import React, { Component } from "react"; import React, { Component } from 'react';
import DataSet from "@antv/data-set"; import DataSet from '@antv/data-set';
import Debounce from "lodash.debounce"; import Debounce from 'lodash.debounce';
import classNames from "classnames"; import classNames from 'classnames';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
/* eslint no-underscore-dangle: 0 */ /* eslint no-underscore-dangle: 0 */
/* eslint no-param-reassign: 0 */ /* eslint no-param-reassign: 0 */
const imgUrl = const imgUrl = 'https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png';
"https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png";
export type TagCloudProps = { export type TagCloudProps = {
data: { data: {
name: string; name: string;
@ -40,7 +39,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
this.initTagCloud(); this.initTagCloud();
this.renderChart(this.props); this.renderChart(this.props);
}); });
window.addEventListener("resize", this.resize, { window.addEventListener('resize', this.resize, {
passive: true, passive: true,
}); });
} }
@ -53,7 +52,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
componentWillUnmount() { componentWillUnmount() {
this.isUnmount = true; this.isUnmount = true;
window.cancelAnimationFrame(this.requestRef); window.cancelAnimationFrame(this.requestRef);
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
} }
resize = () => { resize = () => {
this.requestRef = requestAnimationFrame(() => { this.requestRef = requestAnimationFrame(() => {
@ -78,13 +77,13 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
fontSize: cfg.origin._origin.size, fontSize: cfg.origin._origin.size,
rotate: cfg.origin._origin.rotate, rotate: cfg.origin._origin.rotate,
text: cfg.origin._origin.text, text: cfg.origin._origin.text,
textAlign: "center", textAlign: 'center',
fontFamily: cfg.origin._origin.font, fontFamily: cfg.origin._origin.font,
fill: cfg.color, fill: cfg.color,
textBaseline: "Alphabetic", textBaseline: 'Alphabetic',
}; };
} }
(Shape as any).registerShape("point", "cloud", { (Shape as any).registerShape('point', 'cloud', {
drawShape( drawShape(
cfg: { cfg: {
x: any; x: any;
@ -95,12 +94,12 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
arg0: string, arg0: string,
arg1: { arg1: {
attrs: any; attrs: any;
} },
) => void; ) => void;
} },
) { ) {
const attrs = getTextAttrs(cfg); const attrs = getTextAttrs(cfg);
return container.addShape("text", { return container.addShape('text', {
attrs: { attrs: {
...attrs, ...attrs,
x: cfg.x, x: cfg.x,
@ -120,13 +119,13 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
const w = this.root.offsetWidth; const w = this.root.offsetWidth;
const onload = () => { const onload = () => {
const dv = new DataSet.View().source(data); const dv = new DataSet.View().source(data);
const range = dv.range("value"); const range = dv.range('value');
const [min, max] = range; const [min, max] = range;
dv.transform({ dv.transform({
type: "tag-cloud", type: 'tag-cloud',
fields: ["name", "value"], fields: ['name', 'value'],
imageMask: this.imageMask, imageMask: this.imageMask,
font: "Verdana", font: 'Verdana',
size: [w, h], size: [w, h],
// 宽高设置最好根据 imageMask 做调整 // 宽高设置最好根据 imageMask 做调整
padding: 0, padding: 0,
@ -151,7 +150,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
}; };
if (!this.imageMask) { if (!this.imageMask) {
this.imageMask = new Image(); this.imageMask = new Image();
this.imageMask.crossOrigin = ""; this.imageMask.crossOrigin = '';
this.imageMask.src = imgUrl; this.imageMask.src = imgUrl;
this.imageMask.onload = onload; this.imageMask.onload = onload;
} else { } else {
@ -165,7 +164,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
<div <div
className={classNames(styles.tagCloud, className)} className={classNames(styles.tagCloud, className)}
style={{ style={{
width: "100%", width: '100%',
height, height,
}} }}
ref={this.saveRootRef} ref={this.saveRootRef}
@ -193,7 +192,7 @@ class TagCloud extends Component<TagCloudProps, TagCloudState> {
color="text" color="text"
shape="cloud" shape="cloud"
tooltip={[ tooltip={[
"text*value", 'text*value',
function trans(text, value) { function trans(text, value) {
return { return {
name: text, name: text,

34
src/pages/dashboard/monitor/components/Charts/WaterWave/index.tsx

@ -1,6 +1,6 @@
import React, { Component } from "react"; import React, { Component } from 'react';
import autoHeight from "../autoHeight"; import autoHeight from '../autoHeight';
import useStyles from "./index.style"; import useStyles from './index.style';
/* eslint no-return-assign: 0 */ /* eslint no-return-assign: 0 */
/* eslint no-mixed-operators: 0 */ /* eslint no-mixed-operators: 0 */
@ -24,28 +24,28 @@ class WaterWave extends Component<WaterWaveProps> {
this.renderChart(); this.renderChart();
this.resize(); this.resize();
window.addEventListener( window.addEventListener(
"resize", 'resize',
() => { () => {
requestAnimationFrame(() => this.resize()); requestAnimationFrame(() => this.resize());
}, },
{ {
passive: true, passive: true,
} },
); );
} }
componentDidUpdate(props: WaterWaveProps) { componentDidUpdate(props: WaterWaveProps) {
const { percent } = this.props; const { percent } = this.props;
if (props.percent !== percent) { if (props.percent !== percent) {
// 不加这个会造成绘制缓慢 // 不加这个会造成绘制缓慢
this.renderChart("update"); this.renderChart('update');
} }
} }
componentWillUnmount() { componentWillUnmount() {
cancelAnimationFrame(this.timer); cancelAnimationFrame(this.timer);
if (this.node) { if (this.node) {
this.node.innerHTML = ""; this.node.innerHTML = '';
} }
window.removeEventListener("resize", this.resize); window.removeEventListener('resize', this.resize);
} }
resize = () => { resize = () => {
if (this.root) { if (this.root) {
@ -57,14 +57,14 @@ class WaterWave extends Component<WaterWaveProps> {
} }
}; };
renderChart(type?: string) { renderChart(type?: string) {
const { percent, color = "#1890FF" } = this.props; const { percent, color = '#1890FF' } = this.props;
const data = percent / 100; const data = percent / 100;
cancelAnimationFrame(this.timer); cancelAnimationFrame(this.timer);
if (!this.node || (data !== 0 && !data)) { if (!this.node || (data !== 0 && !data)) {
return; return;
} }
const canvas = this.node; const canvas = this.node;
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext('2d');
if (!ctx) { if (!ctx) {
return; return;
} }
@ -88,11 +88,7 @@ class WaterWave extends Component<WaterWaveProps> {
const bR = radius - lineWidth; const bR = radius - lineWidth;
const circleOffset = -(Math.PI / 2); const circleOffset = -(Math.PI / 2);
let circleLock = true; let circleLock = true;
for ( for (let i = circleOffset; i < circleOffset + 2 * Math.PI; i += 1 / (8 * Math.PI)) {
let i = circleOffset;
i < circleOffset + 2 * Math.PI;
i += 1 / (8 * Math.PI)
) {
arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]); arcStack.push([radius + bR * Math.cos(i), radius + bR * Math.sin(i)]);
} }
const cStartPoint = arcStack.shift() as number[]; const cStartPoint = arcStack.shift() as number[];
@ -118,7 +114,7 @@ class WaterWave extends Component<WaterWaveProps> {
ctx.lineTo(xOffset, canvasHeight); ctx.lineTo(xOffset, canvasHeight);
ctx.lineTo(startPoint[0], startPoint[1]); ctx.lineTo(startPoint[0], startPoint[1]);
const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight); const gradient = ctx.createLinearGradient(0, 0, 0, canvasHeight);
gradient.addColorStop(0, "#ffffff"); gradient.addColorStop(0, '#ffffff');
gradient.addColorStop(1, color); gradient.addColorStop(1, color);
ctx.fillStyle = gradient; ctx.fillStyle = gradient;
ctx.fill(); ctx.fill();
@ -129,7 +125,7 @@ class WaterWave extends Component<WaterWaveProps> {
return; return;
} }
ctx.clearRect(0, 0, canvasWidth, canvasHeight); ctx.clearRect(0, 0, canvasWidth, canvasHeight);
if (circleLock && type !== "update") { if (circleLock && type !== 'update') {
if (arcStack.length) { if (arcStack.length) {
const temp = arcStack.shift() as number[]; const temp = arcStack.shift() as number[];
ctx.lineTo(temp[0], temp[1]); ctx.lineTo(temp[0], temp[1]);
@ -139,7 +135,7 @@ class WaterWave extends Component<WaterWaveProps> {
ctx.lineTo(cStartPoint[0], cStartPoint[1]); ctx.lineTo(cStartPoint[0], cStartPoint[1]);
ctx.stroke(); ctx.stroke();
arcStack = []; arcStack = [];
ctx.globalCompositeOperation = "destination-over"; ctx.globalCompositeOperation = 'destination-over';
ctx.beginPath(); ctx.beginPath();
ctx.lineWidth = lineWidth; ctx.lineWidth = lineWidth;
ctx.arc(radius, radius, bR, 0, 2 * Math.PI, true); ctx.arc(radius, radius, bR, 0, 2 * Math.PI, true);
@ -199,7 +195,7 @@ class WaterWave extends Component<WaterWaveProps> {
style={{ style={{
width: height, width: height,
height, height,
overflow: "hidden", overflow: 'hidden',
}} }}
> >
<canvas <canvas

71
src/pages/dashboard/monitor/index.tsx

@ -1,14 +1,14 @@
import { Card, Col, Row, Statistic } from "antd"; import { Card, Col, Row, Statistic } from 'antd';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import type { FC } from "react"; import type { FC } from 'react';
import { Gauge, WordCloud, Liquid, RingProgress } from "@ant-design/charts"; import { Gauge, WordCloud, Liquid, RingProgress } from '@ant-design/charts';
import type { WordCloudData } from "@antv/g2plot/esm/plots/word-cloud/layer"; import type { WordCloudData } from '@antv/g2plot/esm/plots/word-cloud/layer';
import { GridContent } from "@ant-design/pro-components"; import { GridContent } from '@ant-design/pro-components';
import numeral from "numeral"; import numeral from 'numeral';
import Map from "./components/Map"; import Map from './components/Map';
import ActiveChart from "./components/ActiveChart"; import ActiveChart from './components/ActiveChart';
import { queryTags } from "./service"; import { queryTags } from './service';
import useStyles from "./style.style"; import useStyles from './style.style';
const { Countdown } = Statistic; const { Countdown } = Statistic;
const deadline = Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 30; // Moment is also OK const deadline = Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 30; // Moment is also OK
@ -42,25 +42,17 @@ const Monitor: FC = () => {
<Statistic <Statistic
title="今日交易总额" title="今日交易总额"
suffix="元" suffix="元"
value={numeral(124543233).format("0,0")} value={numeral(124543233).format('0,0')}
/> />
</Col> </Col>
<Col md={6} sm={12} xs={24}> <Col md={6} sm={12} xs={24}>
<Statistic title="销售目标完成率" value="92%" /> <Statistic title="销售目标完成率" value="92%" />
</Col> </Col>
<Col md={6} sm={12} xs={24}> <Col md={6} sm={12} xs={24}>
<Countdown <Countdown title="活动剩余时间" value={deadline} format="HH:mm:ss:SSS" />
title="活动剩余时间"
value={deadline}
format="HH:mm:ss:SSS"
/>
</Col> </Col>
<Col md={6} sm={12} xs={24}> <Col md={6} sm={12} xs={24}>
<Statistic <Statistic title="每秒交易总额" suffix="元" value={numeral(234).format('0,0')} />
title="每秒交易总额"
suffix="元"
value={numeral(234).format("0,0")}
/>
</Col> </Col>
</Row> </Row>
<div className={styles.mapChart}> <div className={styles.mapChart}>
@ -84,7 +76,7 @@ const Monitor: FC = () => {
marginBottom: 24, marginBottom: 24,
}} }}
bodyStyle={{ bodyStyle={{
textAlign: "center", textAlign: 'center',
}} }}
bordered={false} bordered={false}
> >
@ -97,8 +89,8 @@ const Monitor: FC = () => {
range={[0, 25, 50, 75, 100]} range={[0, 25, 50, 75, 100]}
statistic={{ statistic={{
visible: true, visible: true,
text: "优", text: '优',
color: "#30bf78", color: '#30bf78',
}} }}
/> />
</Card> </Card>
@ -114,14 +106,10 @@ const Monitor: FC = () => {
marginBottom: 24, marginBottom: 24,
}} }}
> >
<Card <Card title="各品类占比" bordered={false} className={styles.pieCard}>
title="各品类占比"
bordered={false}
className={styles.pieCard}
>
<Row <Row
style={{ style={{
padding: "16px 0", padding: '16px 0',
}} }}
> >
<Col span={8}> <Col span={8}>
@ -136,20 +124,10 @@ const Monitor: FC = () => {
/> */} /> */}
</Col> </Col>
<Col span={8}> <Col span={8}>
<RingProgress <RingProgress color="#5DDECF" forceFit height={128} percent={0.22} />
color="#5DDECF"
forceFit
height={128}
percent={0.22}
/>
</Col> </Col>
<Col span={8}> <Col span={8}>
<RingProgress <RingProgress color="#2FC25B" forceFit height={128} percent={0.32} />
color="#2FC25B"
forceFit
height={128}
percent={0.32}
/>
</Col> </Col>
</Row> </Row>
</Card> </Card>
@ -168,7 +146,7 @@ const Monitor: FC = () => {
loading={loading} loading={loading}
bordered={false} bordered={false}
bodyStyle={{ bodyStyle={{
overflow: "hidden", overflow: 'hidden',
}} }}
> >
<WordCloud <WordCloud
@ -195,7 +173,7 @@ const Monitor: FC = () => {
<Card <Card
title="资源剩余" title="资源剩余"
bodyStyle={{ bodyStyle={{
textAlign: "center", textAlign: 'center',
fontSize: 0, fontSize: 0,
}} }}
bordered={false} bordered={false}
@ -208,8 +186,7 @@ const Monitor: FC = () => {
forceFit forceFit
padding={[0, 0, 0, 0]} padding={[0, 0, 0, 0]}
statistic={{ statistic={{
formatter: (value) => formatter: (value) => `${((100 * value) / 10000).toFixed(1)}%`,
`${((100 * value) / 10000).toFixed(1)}%`,
}} }}
/> />
</Card> </Card>

10
src/pages/dashboard/monitor/style.style.ts

@ -1,16 +1,16 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
mapChart: { mapChart: {
[`@media screen and (max-width: ${token.screenLG}px)`]: { [`@media screen and (max-width: ${token.screenLG}px)`]: {
height: "auto", height: 'auto',
}, },
}, },
img: { img: {
display: "inline-block", display: 'inline-block',
maxWidth: "100%", maxWidth: '100%',
maxHeight: "437px", maxHeight: '437px',
}, },
}; };
}); });

16
src/pages/dashboard/workplace/components/EditableLinkGroup/index.style.ts

@ -1,17 +1,17 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
linkGroup: { linkGroup: {
padding: "20px 0 8px 24px", padding: '20px 0 8px 24px',
fontSize: "0", fontSize: '0',
"& > a": { '& > a': {
display: "inline-block", display: 'inline-block',
width: "25%", width: '25%',
marginBottom: "13px", marginBottom: '13px',
color: token.colorText, color: token.colorText,
fontSize: token.fontSize, fontSize: token.fontSize,
"&:hover": { '&:hover': {
color: token.colorPrimary, color: token.colorPrimary,
}, },
}, },

14
src/pages/dashboard/workplace/components/EditableLinkGroup/index.tsx

@ -1,7 +1,7 @@
import React, { createElement } from "react"; import React, { createElement } from 'react';
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from '@ant-design/icons';
import { Button } from "antd"; import { Button } from 'antd';
import useStyles from "./index.style"; import useStyles from './index.style';
export type EditableLink = { export type EditableLink = {
title: string; title: string;
href: string; href: string;
@ -25,8 +25,8 @@ const EditableLinkGroup: React.FC<EditableLinkGroupProps> = (props) => {
to: link.href, to: link.href,
href: link.href, href: link.href,
}, },
link.title link.title,
) ),
)} )}
<Button size="small" type="primary" ghost onClick={onAdd}> <Button size="small" type="primary" ghost onClick={onAdd}>
<PlusOutlined /> <PlusOutlined />
@ -37,6 +37,6 @@ const EditableLinkGroup: React.FC<EditableLinkGroupProps> = (props) => {
EditableLinkGroup.defaultProps = { EditableLinkGroup.defaultProps = {
links: [], links: [],
onAdd: () => {}, onAdd: () => {},
linkElement: "a", linkElement: 'a',
}; };
export default EditableLinkGroup; export default EditableLinkGroup;

180
src/pages/dashboard/workplace/components/Radar/index.tsx

@ -1,8 +1,8 @@
import { Axis, Chart, Coord, Geom, Tooltip } from "bizcharts"; import { Axis, Chart, Coord, Geom, Tooltip } from 'bizcharts';
import { Col, Row } from "antd"; import { Col, Row } from 'antd';
import React, { Component } from "react"; import React, { useEffect, useRef, useState } from 'react';
import autoHeight from "./autoHeight"; import autoHeight from './autoHeight';
import useStyles from "./index.style";
export type RadarProps = { export type RadarProps = {
title?: React.ReactNode; title?: React.ReactNode;
height?: number; height?: number;
@ -28,41 +28,41 @@ type RadarState = {
value: string; value: string;
}[]; }[];
}; };
/* eslint react/no-danger:0 */
class Radar extends Component<RadarProps, RadarState> {
state: RadarState = {
legendData: [],
};
chart: G2.Chart | undefined = undefined;
node: HTMLDivElement | undefined = undefined;
componentDidMount() {
this.getLegendData();
}
componentDidUpdate(preProps: RadarProps) {
const { data } = this.props;
if (data !== preProps.data) {
this.getLegendData();
}
}
getG2Instance = (chart: G2.Chart) => {
this.chart = chart;
};
// for custom lengend view const defaultColors = [
getLegendData = () => { '#1890FF',
if (!this.chart) return; '#FACC14',
const geom = this.chart.getAllGeoms()[0]; // 获取所有的图形 '#2FC25B',
'#8543E0',
'#F04864',
'#13C2C2',
'#fa8c16',
'#a0d911',
];
const Radar: React.FC<RadarProps> = (props) => {
const {
data = [],
height = 0,
title,
hasLegend = false,
forceFit = true,
tickCount = 5,
padding = [35, 30, 16, 30] as [number, number, number, number],
animate = true,
colors = defaultColors,
} = props;
const chartRef = useRef<G2.Chart>();
const [legendData, setLegendData] = useState<RadarState['legendData']>([]);
const getLegendData = () => {
if (!chartRef.current) return;
const geom = chartRef.current.getAllGeoms()[0];
if (!geom) return; if (!geom) return;
const items = (geom as any).get("dataArray") || []; // 获取图形对应的 const items = (geom as any).get('dataArray') || [];
const legendData = items.map( const legendData = items.map((item: any[]) => {
(
item: {
color: any;
_origin: any;
}[]
) => {
// eslint-disable-next-line no-underscore-dangle
const origins = item.map((t) => t._origin); const origins = item.map((t) => t._origin);
const result = { const result = {
name: origins[0].name, name: origins[0].name,
@ -71,77 +71,37 @@ class Radar extends Component<RadarProps, RadarState> {
value: origins.reduce((p, n) => p + n.value, 0), value: origins.reduce((p, n) => p + n.value, 0),
}; };
return result; return result;
}
);
this.setState({
legendData,
}); });
setLegendData(legendData);
}; };
handleRef = (n: HTMLDivElement) => {
this.node = n; useEffect(() => {
}; getLegendData();
handleLegendClick = ( }, [data]);
item: {
checked: boolean; const handleLegendClick = (item: any, i: string | number) => {
name: string; const newItem = { ...item, checked: !item.checked };
}, const newLegendData = [...legendData];
i: string | number newLegendData[i as number] = newItem;
) => { const filteredLegendData = newLegendData.filter((l) => l.checked).map((l) => l.name);
const newItem = item; if (chartRef.current) {
newItem.checked = !newItem.checked; chartRef.current.filter('name', (val) => filteredLegendData.indexOf(`${val}`) > -1);
const { legendData } = this.state; chartRef.current.repaint();
legendData[i as number] = newItem as any;
const filteredLegendData = legendData
.filter((l) => l.checked)
.map((l) => l.name);
if (this.chart) {
this.chart.filter(
"name",
(val) => filteredLegendData.indexOf(`${val}`) > -1
);
this.chart.repaint();
} }
this.setState({ setLegendData(newLegendData);
legendData,
});
}; };
render() {
const defaultColors = [
"#1890FF",
"#FACC14",
"#2FC25B",
"#8543E0",
"#F04864",
"#13C2C2",
"#fa8c16",
"#a0d911",
];
const {
data = [],
height = 0,
title,
hasLegend = false,
forceFit = true,
tickCount = 5,
padding = [35, 30, 16, 30] as [number, number, number, number],
animate = true,
colors = defaultColors,
} = this.props;
const { legendData } = this.state;
const scale = { const scale = {
value: { value: {
min: 0, min: 0,
tickCount, tickCount,
}, },
}; };
const chartHeight = height - (hasLegend ? 80 : 22); const chartHeight = height - (hasLegend ? 80 : 22);
return ( return (
<div <div style={{ height }}>
className={styles.radar}
style={{
height,
}}
>
{title && <h4>{title}</h4>} {title && <h4>{title}</h4>}
<Chart <Chart
scale={scale} scale={scale}
@ -150,7 +110,9 @@ class Radar extends Component<RadarProps, RadarState> {
data={data} data={data}
padding={padding} padding={padding}
animate={animate} animate={animate}
onGetG2Instance={this.getG2Instance} onGetG2Instance={(chart: G2.Chart) => {
chartRef.current = chart;
}}
> >
<Tooltip /> <Tooltip />
<Coord type="polar" /> <Coord type="polar" />
@ -168,40 +130,34 @@ class Radar extends Component<RadarProps, RadarState> {
<Axis <Axis
name="value" name="value"
grid={{ grid={{
type: "polygon", type: 'polygon',
lineStyle: { lineStyle: {
lineDash: undefined, lineDash: undefined,
}, },
}} }}
/> />
<Geom <Geom type="line" position="label*value" color={['name', colors]} size={1} />
type="line"
position="label*value"
color={["name", colors]}
size={1}
/>
<Geom <Geom
type="point" type="point"
position="label*value" position="label*value"
color={["name", colors]} color={['name', colors]}
shape="circle" shape="circle"
size={3} size={3}
/> />
</Chart> </Chart>
{hasLegend && ( {hasLegend && (
<Row className={styles.legend}> <Row>
{legendData.map((item, i) => ( {legendData.map((item, i) => (
<Col <Col
span={24 / legendData.length} span={24 / legendData.length}
key={item.name} key={item.name}
onClick={() => this.handleLegendClick(item, i)} onClick={() => handleLegendClick(item, i)}
> >
<div className={styles.legendItem}> <div>
<p> <p>
<span <span
className={styles.dot}
style={{ style={{
backgroundColor: !item.checked ? "#aaa" : item.color, backgroundColor: !item.checked ? '#aaa' : item.color,
}} }}
/> />
<span>{item.name}</span> <span>{item.name}</span>
@ -214,6 +170,6 @@ class Radar extends Component<RadarProps, RadarState> {
)} )}
</div> </div>
); );
} };
}
export default autoHeight()(Radar); export default autoHeight()(Radar);

79
src/pages/dashboard/workplace/index.tsx

@ -1,39 +1,39 @@
import type { FC } from "react"; import type { FC } from 'react';
import { Avatar, Card, Col, List, Skeleton, Row, Statistic } from "antd"; import { Avatar, Card, Col, List, Skeleton, Row, Statistic } from 'antd';
import { Radar } from "@ant-design/charts"; import { Radar } from '@ant-design/charts';
import { Link, useRequest } from "@umijs/max"; import { Link, useRequest } from '@umijs/max';
import { PageContainer } from "@ant-design/pro-components"; import { PageContainer } from '@ant-design/pro-components';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import EditableLinkGroup from "./components/EditableLinkGroup"; import EditableLinkGroup from './components/EditableLinkGroup';
import useStyles from "./style.style"; import useStyles from './style.style';
import type { ActivitiesType, CurrentUser } from "./data.d"; import type { ActivitiesType, CurrentUser } from './data.d';
import { queryProjectNotice, queryActivities, fakeChartData } from "./service"; import { queryProjectNotice, queryActivities, fakeChartData } from './service';
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
const links = [ const links = [
{ {
title: "操作一", title: '操作一',
href: "", href: '',
}, },
{ {
title: "操作二", title: '操作二',
href: "", href: '',
}, },
{ {
title: "操作三", title: '操作三',
href: "", href: '',
}, },
{ {
title: "操作四", title: '操作四',
href: "", href: '',
}, },
{ {
title: "操作五", title: '操作五',
href: "", href: '',
}, },
{ {
title: "操作六", title: '操作六',
href: "", href: '',
}, },
]; ];
const PageHeaderContent: FC<{ const PageHeaderContent: FC<{
@ -88,15 +88,13 @@ const ExtraContent: FC<Record<string, any>> = () => {
}; };
const Workplace: FC = () => { const Workplace: FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const { loading: projectLoading, data: projectNotice = [] } = const { loading: projectLoading, data: projectNotice = [] } = useRequest(queryProjectNotice);
useRequest(queryProjectNotice); const { loading: activitiesLoading, data: activities = [] } = useRequest(queryActivities);
const { loading: activitiesLoading, data: activities = [] } =
useRequest(queryActivities);
const { data } = useRequest(fakeChartData); const { data } = useRequest(fakeChartData);
const renderActivities = (item: ActivitiesType) => { const renderActivities = (item: ActivitiesType) => {
const events = item.template.split(/@\{([^{}]*)\}/gi).map((key) => { const events = item.template.split(/@\{([^{}]*)\}/gi).map((key) => {
if (item[key as keyof ActivitiesType]) { if (item[key as keyof ActivitiesType]) {
const value = item[key as "user"]; const value = item[key as 'user'];
return ( return (
<a href={value?.link} key={value?.name}> <a href={value?.link} key={value?.name}>
{value.name} {value.name}
@ -130,14 +128,13 @@ const Workplace: FC = () => {
content={ content={
<PageHeaderContent <PageHeaderContent
currentUser={{ currentUser={{
avatar: avatar: 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
"https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png", name: '吴彦祖',
name: "吴彦祖", userid: '00000001',
userid: "00000001", email: 'antdesign@alipay.com',
email: "antdesign@alipay.com", signature: '海纳百川,有容乃大',
signature: "海纳百川,有容乃大", title: '交互专家',
title: "交互专家", group: '蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED',
group: "蚂蚁金服-某某某事业群-某某平台部-某某技术部-UED",
}} }}
/> />
} }
@ -176,7 +173,7 @@ const Workplace: FC = () => {
description={item.description} description={item.description}
/> />
<div className={styles.projectItemContent}> <div className={styles.projectItemContent}>
<Link to={item.memberLink}>{item.member || ""}</Link> <Link to={item.memberLink}>{item.member || ''}</Link>
{item.updatedAt && ( {item.updatedAt && (
<span className={styles.datetime} title={item.updatedAt}> <span className={styles.datetime} title={item.updatedAt}>
{dayjs(item.updatedAt).fromNow()} {dayjs(item.updatedAt).fromNow()}
@ -216,11 +213,7 @@ const Workplace: FC = () => {
padding: 0, padding: 0,
}} }}
> >
<EditableLinkGroup <EditableLinkGroup onAdd={() => {}} links={links} linkElement={Link} />
onAdd={() => {}}
links={links}
linkElement={Link}
/>
</Card> </Card>
<Card <Card
style={{ style={{
@ -244,7 +237,7 @@ const Workplace: FC = () => {
visible: true, visible: true,
}} }}
legend={{ legend={{
position: "bottom-center", position: 'bottom-center',
}} }}
/> />
</div> </div>

158
src/pages/dashboard/workplace/style.style.ts

@ -1,147 +1,147 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
activitiesList: { activitiesList: {
padding: "0 24px 8px 24px", padding: '0 24px 8px 24px',
}, },
username: { username: {
color: token.colorText, color: token.colorText,
}, },
event: { event: {
fontWeight: "normal", fontWeight: 'normal',
}, },
pageHeaderContent: { pageHeaderContent: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
display: "block", display: 'block',
}, },
}, },
avatar: { avatar: {
flex: "0 1 72px", flex: '0 1 72px',
"& > span": { '& > span': {
display: "block", display: 'block',
width: "72px", width: '72px',
height: "72px", height: '72px',
borderRadius: "72px", borderRadius: '72px',
}, },
}, },
content: { content: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
marginLeft: "0", marginLeft: '0',
}, },
}, },
contentTitle: { contentTitle: {
marginBottom: "12px", marginBottom: '12px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
fontSize: "20px", fontSize: '20px',
lineHeight: "28px", lineHeight: '28px',
}, },
extraContent: { extraContent: {
[`@media screen and (max-width: ${token.screenSM}px)`]: {}, [`@media screen and (max-width: ${token.screenSM}px)`]: {},
}, },
statItem: { statItem: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { float: "none" }, [`@media screen and (max-width: ${token.screenSM}px)`]: { float: 'none' },
}, },
"> p:first-child": { '> p:first-child': {
marginBottom: "4px", marginBottom: '4px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: token.fontSize, fontSize: token.fontSize,
lineHeight: "22px", lineHeight: '22px',
}, },
"> p": { '> p': {
margin: "0", margin: '0',
color: token.colorTextHeading, color: token.colorTextHeading,
fontSize: "30px", fontSize: '30px',
lineHeight: "38px", lineHeight: '38px',
}, },
"> span": { '> span': {
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: "20px", fontSize: '20px',
}, },
members: { members: {
[`@media screen and (max-width: ${token.screenLG}px)`]: { [`@media screen and (max-width: ${token.screenLG}px)`]: {
marginBottom: "0", marginBottom: '0',
}, },
}, },
a: { a: {
display: "inline-block", display: 'inline-block',
flex: "1 1 0", flex: '1 1 0',
color: token.colorTextSecondary, color: token.colorTextSecondary,
position: "relative", position: 'relative',
maxHeight: token.line * 1.5, maxHeight: token.line * 1.5,
marginRight: "-1em", marginRight: '-1em',
paddingRight: "1em", paddingRight: '1em',
overflow: "hidden", overflow: 'hidden',
lineHeight: "1.5em", lineHeight: '1.5em',
textAlign: "justify", textAlign: 'justify',
"&::before": { '&::before': {
position: "absolute", position: 'absolute',
right: "14px", right: '14px',
bottom: "0", bottom: '0',
padding: "0 1px", padding: '0 1px',
background: token.bg, background: token.bg,
content: "'...'", content: "'...'",
}, },
"&::after": { '&::after': {
position: "absolute", position: 'absolute',
right: "14px", right: '14px',
width: "1em", width: '1em',
height: "1em", height: '1em',
marginTop: "0.2em", marginTop: '0.2em',
background: "white", background: 'white',
content: "''", content: "''",
}, },
"&:hover": { color: token.colorPrimary }, '&:hover': { color: token.colorPrimary },
}, },
member: { member: {
marginLeft: "12px", marginLeft: '12px',
fontSize: token.fontSize, fontSize: token.fontSize,
lineHeight: "24px", lineHeight: '24px',
verticalAlign: "top", verticalAlign: 'top',
}, },
projectList: { projectList: {
[`@media screen and (max-width: ${token.screenXS}px)`]: {}, [`@media screen and (max-width: ${token.screenXS}px)`]: {},
}, },
".ant-card-meta-description": { '.ant-card-meta-description': {
height: "44px", height: '44px',
overflow: "hidden", overflow: 'hidden',
color: token.colorTextSecondary, color: token.colorTextSecondary,
lineHeight: "22px", lineHeight: '22px',
}, },
cardTitle: { cardTitle: {
fontSize: "0", fontSize: '0',
}, },
projectGrid: { projectGrid: {
[`@media screen and (max-width: ${token.screenXS}px)`]: { width: "100%" }, [`@media screen and (max-width: ${token.screenXS}px)`]: { width: '100%' },
}, },
projectItemContent: { projectItemContent: {
display: "flex", display: 'flex',
height: "20px", height: '20px',
marginTop: "8px", marginTop: '8px',
overflow: "hidden", overflow: 'hidden',
fontSize: "12px", fontSize: '12px',
lineHeight: "1.5em", lineHeight: '1.5em',
position: "relative", position: 'relative',
maxHeight: token.line * 1.5, maxHeight: token.line * 1.5,
marginRight: "-1em", marginRight: '-1em',
paddingRight: "1em", paddingRight: '1em',
textAlign: "justify", textAlign: 'justify',
"&::before": { '&::before': {
position: "absolute", position: 'absolute',
right: "14px", right: '14px',
bottom: "0", bottom: '0',
padding: "0 1px", padding: '0 1px',
background: token.bg, background: token.bg,
content: "'...'", content: "'...'",
}, },
"&::after": { '&::after': {
position: "absolute", position: 'absolute',
right: "14px", right: '14px',
width: "1em", width: '1em',
height: "1em", height: '1em',
marginTop: "0.2em", marginTop: '0.2em',
background: "white", background: 'white',
content: "''", content: "''",
}, },
}, },
@ -150,7 +150,7 @@ const useStyles = createStyles(({ token }) => {
}, },
activeCard: { activeCard: {
[`@media screen and (max-width: ${token.screenLG}px)`]: { [`@media screen and (max-width: ${token.screenLG}px)`]: {
marginBottom: "24px", marginBottom: '24px',
}, },
}, },
}; };

85
src/pages/form/advanced-form/components/TableForm.tsx

@ -1,8 +1,8 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from '@ant-design/icons';
import { Button, Divider, Input, Popconfirm, Table, message } from "antd"; import { Button, Divider, Input, Popconfirm, Table, message } from 'antd';
import type { FC } from "react"; import type { FC } from 'react';
import React, { useState } from "react"; import React, { useState } from 'react';
import useStyles from "../style.style"; import useStyles from '../style.style';
type TableFormDateType = { type TableFormDateType = {
key: string; key: string;
workId?: string; workId?: string;
@ -20,16 +20,11 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
const [clickedCancel, setClickedCancel] = useState(false); const [clickedCancel, setClickedCancel] = useState(false);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [index, setIndex] = useState(0); const [index, setIndex] = useState(0);
const [cacheOriginData, setCacheOriginData] = useState<Record<string, any>>( const [cacheOriginData, setCacheOriginData] = useState<Record<string, any>>({});
{}
);
const [data, setData] = useState(value); const [data, setData] = useState(value);
const getRowByKey = (key: string, newData?: TableFormDateType[]) => const getRowByKey = (key: string, newData?: TableFormDateType[]) =>
(newData || data)?.filter((item) => item.key === key)[0]; (newData || data)?.filter((item) => item.key === key)[0];
const toggleEditable = ( const toggleEditable = (e: React.MouseEvent | React.KeyboardEvent, key: string) => {
e: React.MouseEvent | React.KeyboardEvent,
key: string
) => {
e.preventDefault(); e.preventDefault();
const newData = data?.map((item) => ({ const newData = data?.map((item) => ({
...item, ...item,
@ -54,9 +49,9 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
})) || []; })) || [];
newData.push({ newData.push({
key: `NEW_TEMP_ID_${index}`, key: `NEW_TEMP_ID_${index}`,
workId: "", workId: '',
name: "", name: '',
department: "", department: '',
editable: true, editable: true,
isNew: true, isNew: true,
}); });
@ -64,9 +59,7 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
setData(newData); setData(newData);
}; };
const remove = (key: string) => { const remove = (key: string) => {
const newData = data?.filter( const newData = data?.filter((item) => item.key !== key) as TableFormDateType[];
(item) => item.key !== key
) as TableFormDateType[];
setData(newData); setData(newData);
if (onChange) { if (onChange) {
onChange(newData); onChange(newData);
@ -75,12 +68,12 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
const handleFieldChange = ( const handleFieldChange = (
e: React.ChangeEvent<HTMLInputElement>, e: React.ChangeEvent<HTMLInputElement>,
fieldName: keyof TableFormDateType, fieldName: keyof TableFormDateType,
key: string key: string,
) => { ) => {
const newData = [...(data as TableFormDateType[])]; const newData = [...(data as TableFormDateType[])];
const target = getRowByKey(key, newData); const target = getRowByKey(key, newData);
if (target && target[fieldName]) { if (target && target[fieldName]) {
target[fieldName as "key"] = e.target.value; target[fieldName as 'key'] = e.target.value;
setData(newData); setData(newData);
} }
}; };
@ -94,7 +87,7 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
} }
const target = getRowByKey(key) || ({} as any); const target = getRowByKey(key) || ({} as any);
if (!target.workId || !target.name || !target.department) { if (!target.workId || !target.name || !target.department) {
message.error("请填写完整成员信息。"); message.error('请填写完整成员信息。');
(e.target as HTMLInputElement).focus(); (e.target as HTMLInputElement).focus();
setLoading(false); setLoading(false);
return; return;
@ -108,7 +101,7 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
}, 500); }, 500);
}; };
const handleKeyPress = (e: React.KeyboardEvent, key: string) => { const handleKeyPress = (e: React.KeyboardEvent, key: string) => {
if (e.key === "Enter") { if (e.key === 'Enter') {
saveRow(e, key); saveRow(e, key);
} }
}; };
@ -138,17 +131,17 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
}; };
const columns = [ const columns = [
{ {
title: "成员姓名", title: '成员姓名',
dataIndex: "name", dataIndex: 'name',
key: "name", key: 'name',
width: "20%", width: '20%',
render: (text: string, record: TableFormDateType) => { render: (text: string, record: TableFormDateType) => {
if (record.editable) { if (record.editable) {
return ( return (
<Input <Input
value={text} value={text}
autoFocus autoFocus
onChange={(e) => handleFieldChange(e, "name", record.key)} onChange={(e) => handleFieldChange(e, 'name', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)} onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="成员姓名" placeholder="成员姓名"
/> />
@ -158,16 +151,16 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
}, },
}, },
{ {
title: "工号", title: '工号',
dataIndex: "workId", dataIndex: 'workId',
key: "workId", key: 'workId',
width: "20%", width: '20%',
render: (text: string, record: TableFormDateType) => { render: (text: string, record: TableFormDateType) => {
if (record.editable) { if (record.editable) {
return ( return (
<Input <Input
value={text} value={text}
onChange={(e) => handleFieldChange(e, "workId", record.key)} onChange={(e) => handleFieldChange(e, 'workId', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)} onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="工号" placeholder="工号"
/> />
@ -177,16 +170,16 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
}, },
}, },
{ {
title: "所属部门", title: '所属部门',
dataIndex: "department", dataIndex: 'department',
key: "department", key: 'department',
width: "40%", width: '40%',
render: (text: string, record: TableFormDateType) => { render: (text: string, record: TableFormDateType) => {
if (record.editable) { if (record.editable) {
return ( return (
<Input <Input
value={text} value={text}
onChange={(e) => handleFieldChange(e, "department", record.key)} onChange={(e) => handleFieldChange(e, 'department', record.key)}
onKeyPress={(e) => handleKeyPress(e, record.key)} onKeyPress={(e) => handleKeyPress(e, record.key)}
placeholder="所属部门" placeholder="所属部门"
/> />
@ -196,8 +189,8 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
}, },
}, },
{ {
title: "操作", title: '操作',
key: "action", key: 'action',
render: (text: string, record: TableFormDateType) => { render: (text: string, record: TableFormDateType) => {
if (!!record.editable && loading) { if (!!record.editable && loading) {
return null; return null;
@ -208,10 +201,7 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
<span> <span>
<a onClick={(e) => saveRow(e, record.key)}></a> <a onClick={(e) => saveRow(e, record.key)}></a>
<Divider type="vertical" /> <Divider type="vertical" />
<Popconfirm <Popconfirm title="是否要删除此行?" onConfirm={() => remove(record.key)}>
title="是否要删除此行?"
onConfirm={() => remove(record.key)}
>
<a></a> <a></a>
</Popconfirm> </Popconfirm>
</span> </span>
@ -229,10 +219,7 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
<span> <span>
<a onClick={(e) => toggleEditable(e, record.key)}></a> <a onClick={(e) => toggleEditable(e, record.key)}></a>
<Divider type="vertical" /> <Divider type="vertical" />
<Popconfirm <Popconfirm title="是否要删除此行?" onConfirm={() => remove(record.key)}>
title="是否要删除此行?"
onConfirm={() => remove(record.key)}
>
<a></a> <a></a>
</Popconfirm> </Popconfirm>
</span> </span>
@ -247,11 +234,11 @@ const TableForm: FC<TableFormProps> = ({ value, onChange }) => {
columns={columns} columns={columns}
dataSource={data} dataSource={data}
pagination={false} pagination={false}
rowClassName={(record) => (record.editable ? styles.editable : "")} rowClassName={(record) => (record.editable ? styles.editable : '')}
/> />
<Button <Button
style={{ style={{
width: "100%", width: '100%',
marginTop: 16, marginTop: 16,
marginBottom: 8, marginBottom: 8,
}} }}

196
src/pages/form/advanced-form/index.tsx

@ -1,19 +1,19 @@
import { CloseCircleOutlined } from "@ant-design/icons"; import { CloseCircleOutlined } from '@ant-design/icons';
import { Card, Col, Popover, Row, message } from "antd"; import { Card, Col, Popover, Row, message } from 'antd';
import type { FC } from "react"; import type { FC } from 'react';
import { useState } from "react"; import { useState } from 'react';
import { import {
ProForm, ProForm,
ProFormDateRangePicker, ProFormDateRangePicker,
ProFormSelect, ProFormSelect,
ProFormText, ProFormText,
ProFormTimePicker, ProFormTimePicker,
} from "@ant-design/pro-components"; } from '@ant-design/pro-components';
import type { ProColumnType } from "@ant-design/pro-components"; import type { ProColumnType } from '@ant-design/pro-components';
import { EditableProTable } from "@ant-design/pro-components"; import { EditableProTable } from '@ant-design/pro-components';
import { PageContainer, FooterToolbar } from "@ant-design/pro-components"; import { PageContainer, FooterToolbar } from '@ant-design/pro-components';
import { fakeSubmitForm } from "./service"; import { fakeSubmitForm } from './service';
import useStyles from "./style.style"; import useStyles from './style.style';
interface TableFormDateType { interface TableFormDateType {
key: string; key: string;
workId?: string; workId?: string;
@ -24,37 +24,37 @@ interface TableFormDateType {
} }
type InternalNamePath = (string | number)[]; type InternalNamePath = (string | number)[];
const fieldLabels = { const fieldLabels = {
name: "仓库名", name: '仓库名',
url: "仓库域名", url: '仓库域名',
owner: "仓库管理员", owner: '仓库管理员',
approver: "审批人", approver: '审批人',
dateRange: "生效日期", dateRange: '生效日期',
type: "仓库类型", type: '仓库类型',
name2: "任务名", name2: '任务名',
url2: "任务描述", url2: '任务描述',
owner2: "执行人", owner2: '执行人',
approver2: "责任人", approver2: '责任人',
dateRange2: "生效日期", dateRange2: '生效日期',
type2: "任务类型", type2: '任务类型',
}; };
const tableData = [ const tableData = [
{ {
key: "1", key: '1',
workId: "00001", workId: '00001',
name: "John Brown", name: 'John Brown',
department: "New York No. 1 Lake Park", department: 'New York No. 1 Lake Park',
}, },
{ {
key: "2", key: '2',
workId: "00002", workId: '00002',
name: "Jim Green", name: 'Jim Green',
department: "London No. 1 Lake Park", department: 'London No. 1 Lake Park',
}, },
{ {
key: "3", key: '3',
workId: "00003", workId: '00003',
name: "Joe Black", name: 'Joe Black',
department: "Sidney No. 1 Lake Park", department: 'Sidney No. 1 Lake Park',
}, },
]; ];
interface ErrorField { interface ErrorField {
@ -79,19 +79,9 @@ const AdvancedForm: FC<Record<string, any>> = () => {
if (!err || err.errors.length === 0) { if (!err || err.errors.length === 0) {
return null; return null;
} }
const key = err.name[0] as const key = err.name[0] as 'name' | 'url' | 'owner' | 'approver' | 'dateRange' | 'type';
| "name"
| "url"
| "owner"
| "approver"
| "dateRange"
| "type";
return ( return (
<li <li key={key} className={styles.errorListItem} onClick={() => scrollToField(key)}>
key={key}
className={styles.errorListItem}
onClick={() => scrollToField(key)}
>
<CloseCircleOutlined className={styles.errorIcon} /> <CloseCircleOutlined className={styles.errorIcon} />
<div className={styles.errorMessage}>{err.errors[0]}</div> <div className={styles.errorMessage}>{err.errors[0]}</div>
<div className={styles.errorField}>{fieldLabels[key]}</div> <div className={styles.errorField}>{fieldLabels[key]}</div>
@ -122,7 +112,7 @@ const AdvancedForm: FC<Record<string, any>> = () => {
setError([]); setError([]);
try { try {
await fakeSubmitForm(values); await fakeSubmitForm(values);
message.success("提交成功"); message.success('提交成功');
} catch { } catch {
// console.log // console.log
} }
@ -132,27 +122,27 @@ const AdvancedForm: FC<Record<string, any>> = () => {
}; };
const columns: ProColumnType<TableFormDateType>[] = [ const columns: ProColumnType<TableFormDateType>[] = [
{ {
title: "成员姓名", title: '成员姓名',
dataIndex: "name", dataIndex: 'name',
key: "name", key: 'name',
width: "20%", width: '20%',
}, },
{ {
title: "工号", title: '工号',
dataIndex: "workId", dataIndex: 'workId',
key: "workId", key: 'workId',
width: "20%", width: '20%',
}, },
{ {
title: "所属部门", title: '所属部门',
dataIndex: "department", dataIndex: 'department',
key: "department", key: 'department',
width: "40%", width: '40%',
}, },
{ {
title: "操作", title: '操作',
key: "action", key: 'action',
valueType: "option", valueType: 'option',
render: (_, record: TableFormDateType, index, action) => { render: (_, record: TableFormDateType, index, action) => {
return [ return [
<a <a
@ -197,7 +187,7 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入仓库名称", message: '请输入仓库名称',
}, },
]} ]}
placeholder="请输入仓库名称" placeholder="请输入仓库名称"
@ -222,15 +212,15 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择", message: '请选择',
}, },
]} ]}
fieldProps={{ fieldProps={{
style: { style: {
width: "100%", width: '100%',
}, },
addonBefore: "http://", addonBefore: 'http://',
addonAfter: ".com", addonAfter: '.com',
}} }}
placeholder="请输入" placeholder="请输入"
/> />
@ -254,17 +244,17 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择管理员", message: '请选择管理员',
}, },
]} ]}
options={[ options={[
{ {
label: "付晓晓", label: '付晓晓',
value: "xiao", value: 'xiao',
}, },
{ {
label: "周毛毛", label: '周毛毛',
value: "mao", value: 'mao',
}, },
]} ]}
placeholder="请选择管理员" placeholder="请选择管理员"
@ -279,17 +269,17 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择审批员", message: '请选择审批员',
}, },
]} ]}
options={[ options={[
{ {
label: "付晓晓", label: '付晓晓',
value: "xiao", value: 'xiao',
}, },
{ {
label: "周毛毛", label: '周毛毛',
value: "mao", value: 'mao',
}, },
]} ]}
placeholder="请选择审批员" placeholder="请选择审批员"
@ -313,13 +303,13 @@ const AdvancedForm: FC<Record<string, any>> = () => {
name="dateRange" name="dateRange"
fieldProps={{ fieldProps={{
style: { style: {
width: "100%", width: '100%',
}, },
}} }}
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择生效日期", message: '请选择生效日期',
}, },
]} ]}
/> />
@ -343,17 +333,17 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择仓库类型", message: '请选择仓库类型',
}, },
]} ]}
options={[ options={[
{ {
label: "私密", label: '私密',
value: "private", value: 'private',
}, },
{ {
label: "公开", label: '公开',
value: "public", value: 'public',
}, },
]} ]}
placeholder="请选择仓库类型" placeholder="请选择仓库类型"
@ -370,7 +360,7 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入", message: '请输入',
}, },
]} ]}
/> />
@ -394,7 +384,7 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择", message: '请选择',
}, },
]} ]}
/> />
@ -418,17 +408,17 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择管理员", message: '请选择管理员',
}, },
]} ]}
options={[ options={[
{ {
label: "付晓晓", label: '付晓晓',
value: "xiao", value: 'xiao',
}, },
{ {
label: "周毛毛", label: '周毛毛',
value: "mao", value: 'mao',
}, },
]} ]}
/> />
@ -442,17 +432,17 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择审批员", message: '请选择审批员',
}, },
]} ]}
options={[ options={[
{ {
label: "付晓晓", label: '付晓晓',
value: "xiao", value: 'xiao',
}, },
{ {
label: "周毛毛", label: '周毛毛',
value: "mao", value: 'mao',
}, },
]} ]}
placeholder="请选择审批员" placeholder="请选择审批员"
@ -477,13 +467,13 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入", message: '请输入',
}, },
]} ]}
placeholder="提醒时间" placeholder="提醒时间"
fieldProps={{ fieldProps={{
style: { style: {
width: "100%", width: '100%',
}, },
}} }}
/> />
@ -507,17 +497,17 @@ const AdvancedForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择仓库类型", message: '请选择仓库类型',
}, },
]} ]}
options={[ options={[
{ {
label: "私密", label: '私密',
value: "private", value: 'private',
}, },
{ {
label: "公开", label: '公开',
value: "public", value: 'public',
}, },
]} ]}
placeholder="请选择仓库类型" placeholder="请选择仓库类型"

52
src/pages/form/advanced-form/style.style.ts

@ -1,48 +1,48 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
card: { card: {
marginBottom: "24px", marginBottom: '24px',
}, },
".ant-legacy-form-item .ant-legacy-form-item-control-wrapper": { '.ant-legacy-form-item .ant-legacy-form-item-control-wrapper': {
width: "100%", width: '100%',
}, },
errorIcon: { errorIcon: {
float: "left", float: 'left',
marginTop: "4px", marginTop: '4px',
marginRight: "12px", marginRight: '12px',
paddingBottom: "22px", paddingBottom: '22px',
color: token.colorError, color: token.colorError,
}, },
"span.anticon": { 'span.anticon': {
marginRight: "4px", marginRight: '4px',
}, },
errorPopover: {}, errorPopover: {},
".ant-popover-inner-content": { '.ant-popover-inner-content': {
minWidth: "256px", minWidth: '256px',
maxHeight: "290px", maxHeight: '290px',
padding: "0", padding: '0',
overflow: "auto", overflow: 'auto',
}, },
errorListItem: { errorListItem: {
padding: "8px 16px", padding: '8px 16px',
listStyle: "none", listStyle: 'none',
borderBottom: "1px solid @border-color-split", borderBottom: '1px solid @border-color-split',
cursor: "pointer", cursor: 'pointer',
transition: "all 0.3s", transition: 'all 0.3s',
"&:hover": { background: token.colorBgTextActive }, '&:hover': { background: token.colorBgTextActive },
"&:last-child": { border: "0" }, '&:last-child': { border: '0' },
}, },
errorField: { errorField: {
marginTop: "2px", marginTop: '2px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: "12px", fontSize: '12px',
}, },
editable: {}, editable: {},
td: { td: {
paddingTop: "13px", paddingTop: '13px',
paddingBottom: "12.5px", paddingBottom: '12.5px',
}, },
}; };
}); });

63
src/pages/form/basic-form/index.tsx

@ -1,4 +1,4 @@
import { Card, message } from "antd"; import { Card, message } from 'antd';
import { import {
ProForm, ProForm,
ProFormDateRangePicker, ProFormDateRangePicker,
@ -8,18 +8,18 @@ import {
ProFormSelect, ProFormSelect,
ProFormText, ProFormText,
ProFormTextArea, ProFormTextArea,
} from "@ant-design/pro-components"; } from '@ant-design/pro-components';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import type { FC } from "react"; import type { FC } from 'react';
import { PageContainer } from "@ant-design/pro-components"; import { PageContainer } from '@ant-design/pro-components';
import { fakeSubmitForm } from "./service"; import { fakeSubmitForm } from './service';
import useStyles from "./style.style"; import useStyles from './style.style';
const BasicForm: FC<Record<string, any>> = () => { const BasicForm: FC<Record<string, any>> = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const { run } = useRequest(fakeSubmitForm, { const { run } = useRequest(fakeSubmitForm, {
manual: true, manual: true,
onSuccess: () => { onSuccess: () => {
message.success("提交成功"); message.success('提交成功');
}, },
}); });
const onFinish = async (values: Record<string, any>) => { const onFinish = async (values: Record<string, any>) => {
@ -31,14 +31,14 @@ const BasicForm: FC<Record<string, any>> = () => {
<ProForm <ProForm
hideRequiredMark hideRequiredMark
style={{ style={{
margin: "auto", margin: 'auto',
marginTop: 8, marginTop: 8,
maxWidth: 600, maxWidth: 600,
}} }}
name="basic" name="basic"
layout="vertical" layout="vertical"
initialValues={{ initialValues={{
public: "1", public: '1',
}} }}
onFinish={onFinish} onFinish={onFinish}
> >
@ -49,7 +49,7 @@ const BasicForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入标题", message: '请输入标题',
}, },
]} ]}
placeholder="给目标起个名字" placeholder="给目标起个名字"
@ -61,10 +61,10 @@ const BasicForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择起止日期", message: '请选择起止日期',
}, },
]} ]}
placeholder={["开始日期", "结束日期"]} placeholder={['开始日期', '结束日期']}
/> />
<ProFormTextArea <ProFormTextArea
label="目标描述" label="目标描述"
@ -73,7 +73,7 @@ const BasicForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入目标描述", message: '请输入目标描述',
}, },
]} ]}
placeholder="请输入你的阶段性工作目标" placeholder="请输入你的阶段性工作目标"
@ -86,7 +86,7 @@ const BasicForm: FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入衡量标准", message: '请输入衡量标准',
}, },
]} ]}
placeholder="请输入衡量标准" placeholder="请输入衡量标准"
@ -131,30 +131,30 @@ const BasicForm: FC<Record<string, any>> = () => {
width="xs" width="xs"
fieldProps={{ fieldProps={{
formatter: (value) => `${value || 0}%`, formatter: (value) => `${value || 0}%`,
parser: (value) => Number(value ? value.replace("%", "") : "0"), parser: (value) => Number(value ? value.replace('%', '') : '0'),
}} }}
/> />
<ProFormRadio.Group <ProFormRadio.Group
options={[ options={[
{ {
value: "1", value: '1',
label: "公开", label: '公开',
}, },
{ {
value: "2", value: '2',
label: "部分公开", label: '部分公开',
}, },
{ {
value: "3", value: '3',
label: "不公开", label: '不公开',
}, },
]} ]}
label="目标公开" label="目标公开"
help="客户、邀评人默认被分享" help="客户、邀评人默认被分享"
name="publicType" name="publicType"
/> />
<ProFormDependency name={["publicType"]}> <ProFormDependency name={['publicType']}>
{({ publicType }) => { {({ publicType }) => {
return ( return (
<ProFormSelect <ProFormSelect
@ -162,23 +162,22 @@ const BasicForm: FC<Record<string, any>> = () => {
name="publicUsers" name="publicUsers"
fieldProps={{ fieldProps={{
style: { style: {
margin: "8px 0", margin: '8px 0',
display: display: publicType && publicType === '2' ? 'block' : 'none',
publicType && publicType === "2" ? "block" : "none",
}, },
}} }}
options={[ options={[
{ {
value: "1", value: '1',
label: "同事甲", label: '同事甲',
}, },
{ {
value: "2", value: '2',
label: "同事乙", label: '同事乙',
}, },
{ {
value: "3", value: '3',
label: "同事丙", label: '同事丙',
}, },
]} ]}
/> />

4
src/pages/form/basic-form/style.style.ts

@ -1,10 +1,10 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
optional: { optional: {
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontStyle: "normal", fontStyle: 'normal',
}, },
}; };
}); });

62
src/pages/form/step-form/index.tsx

@ -1,24 +1,16 @@
import React, { useRef, useState } from "react"; import React, { useRef, useState } from 'react';
import type { FormInstance } from "antd"; import type { FormInstance } from 'antd';
import { import { Card, Result, Button, Descriptions, Divider, Alert, Statistic } from 'antd';
Card, import { PageContainer } from '@ant-design/pro-components';
Result,
Button,
Descriptions,
Divider,
Alert,
Statistic,
} from "antd";
import { PageContainer } from "@ant-design/pro-components";
import { import {
ProForm, ProForm,
ProFormDigit, ProFormDigit,
ProFormSelect, ProFormSelect,
ProFormText, ProFormText,
StepsForm, StepsForm,
} from "@ant-design/pro-components"; } from '@ant-design/pro-components';
import type { StepDataType } from "./data.d"; import type { StepDataType } from './data.d';
import useStyles from "./style.style"; import useStyles from './style.style';
const StepDescriptions: React.FC<{ const StepDescriptions: React.FC<{
stepData: StepDataType; stepData: StepDataType;
bordered?: boolean; bordered?: boolean;
@ -74,11 +66,11 @@ const StepResult: React.FC<{
const StepForm: React.FC<Record<string, any>> = () => { const StepForm: React.FC<Record<string, any>> = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const [stepData, setStepData] = useState<StepDataType>({ const [stepData, setStepData] = useState<StepDataType>({
payAccount: "ant-design@alipay.com", payAccount: 'ant-design@alipay.com',
receiverAccount: "test@example.com", receiverAccount: 'test@example.com',
receiverName: "Alex", receiverName: 'Alex',
amount: "500", amount: '500',
receiverMode: "alipay", receiverMode: 'alipay',
}); });
const [current, setCurrent] = useState(0); const [current, setCurrent] = useState(0);
const formRef = useRef<FormInstance>(); const formRef = useRef<FormInstance>();
@ -113,11 +105,11 @@ const StepForm: React.FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择付款账户", message: '请选择付款账户',
}, },
]} ]}
valueEnum={{ valueEnum={{
"ant-design@alipay.com": "ant-design@alipay.com", 'ant-design@alipay.com': 'ant-design@alipay.com',
}} }}
/> />
@ -127,12 +119,12 @@ const StepForm: React.FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择付款账户", message: '请选择付款账户',
}, },
]} ]}
valueEnum={{ valueEnum={{
alipay: "支付宝", alipay: '支付宝',
bank: "银行账户", bank: '银行账户',
}} }}
/> />
<ProFormText <ProFormText
@ -140,11 +132,11 @@ const StepForm: React.FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入收款人账户", message: '请输入收款人账户',
}, },
{ {
type: "email", type: 'email',
message: "账户名应为邮箱格式", message: '账户名应为邮箱格式',
}, },
]} ]}
placeholder="test@example.com" placeholder="test@example.com"
@ -157,7 +149,7 @@ const StepForm: React.FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入收款人姓名", message: '请输入收款人姓名',
}, },
]} ]}
placeholder="请输入收款人姓名" placeholder="请输入收款人姓名"
@ -169,16 +161,16 @@ const StepForm: React.FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入转账金额", message: '请输入转账金额',
}, },
{ {
pattern: /^(\d+)((?:\.\d+)?)$/, pattern: /^(\d+)((?:\.\d+)?)$/,
message: "请输入合法金额数字", message: '请输入合法金额数字',
}, },
]} ]}
placeholder="请输入金额" placeholder="请输入金额"
fieldProps={{ fieldProps={{
prefix: "¥", prefix: '¥',
}} }}
/> />
</StepsForm.StepForm> </StepsForm.StepForm>
@ -196,7 +188,7 @@ const StepForm: React.FC<Record<string, any>> = () => {
<StepDescriptions stepData={stepData} bordered /> <StepDescriptions stepData={stepData} bordered />
<Divider <Divider
style={{ style={{
margin: "24px 0", margin: '24px 0',
}} }}
/> />
<ProFormText.Password <ProFormText.Password
@ -207,7 +199,7 @@ const StepForm: React.FC<Record<string, any>> = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "需要支付密码才能进行支付", message: '需要支付密码才能进行支付',
}, },
]} ]}
/> />
@ -226,7 +218,7 @@ const StepForm: React.FC<Record<string, any>> = () => {
</StepsForm> </StepsForm>
<Divider <Divider
style={{ style={{
margin: "40px 0 24px", margin: '40px 0 24px',
}} }}
/> />
<div className={styles.desc}> <div className={styles.desc}>

10
src/pages/form/step-form/style.style.ts

@ -1,14 +1,14 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return { return {
card: { card: {
marginBottom: "24px", marginBottom: '24px',
}, },
result: { result: {
maxWidth: "560px", maxWidth: '560px',
margin: "0 auto", margin: '0 auto',
padding: "24px 0 8px", padding: '24px 0 8px',
}, },
}; };
}); });

32
src/pages/list/basic-list/components/OperationModal.tsx

@ -1,14 +1,14 @@
import type { FC } from "react"; import type { FC } from 'react';
import { import {
ModalForm, ModalForm,
ProFormSelect, ProFormSelect,
ProFormDateTimePicker, ProFormDateTimePicker,
ProFormText, ProFormText,
ProFormTextArea, ProFormTextArea,
} from "@ant-design/pro-components"; } from '@ant-design/pro-components';
import type { BasicListItemDataType } from "../data.d"; import type { BasicListItemDataType } from '../data.d';
import useStyles from "../style.style"; import useStyles from '../style.style';
import { Button, Result } from "antd"; import { Button, Result } from 'antd';
type OperationModalProps = { type OperationModalProps = {
done: boolean; done: boolean;
visible: boolean; visible: boolean;
@ -26,7 +26,7 @@ const OperationModal: FC<OperationModalProps> = (props) => {
return ( return (
<ModalForm<BasicListItemDataType> <ModalForm<BasicListItemDataType>
visible={visible} visible={visible}
title={done ? null : `任务${current ? "编辑" : "添加"}`} title={done ? null : `任务${current ? '编辑' : '添加'}`}
className={styles.standardListForm} className={styles.standardListForm}
width={640} width={640}
onFinish={async (values) => { onFinish={async (values) => {
@ -42,7 +42,7 @@ const OperationModal: FC<OperationModalProps> = (props) => {
destroyOnClose: true, destroyOnClose: true,
bodyStyle: done bodyStyle: done
? { ? {
padding: "72px 0", padding: '72px 0',
} }
: {}, : {},
}} }}
@ -55,7 +55,7 @@ const OperationModal: FC<OperationModalProps> = (props) => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入任务名称", message: '请输入任务名称',
}, },
]} ]}
placeholder="请输入" placeholder="请输入"
@ -66,12 +66,12 @@ const OperationModal: FC<OperationModalProps> = (props) => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择开始时间", message: '请选择开始时间',
}, },
]} ]}
fieldProps={{ fieldProps={{
style: { style: {
width: "100%", width: '100%',
}, },
}} }}
placeholder="请选择" placeholder="请选择"
@ -82,17 +82,17 @@ const OperationModal: FC<OperationModalProps> = (props) => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请选择任务负责人", message: '请选择任务负责人',
}, },
]} ]}
options={[ options={[
{ {
label: "付晓晓", label: '付晓晓',
value: "xiao", value: 'xiao',
}, },
{ {
label: "周毛毛", label: '周毛毛',
value: "mao", value: 'mao',
}, },
]} ]}
placeholder="请选择管理员" placeholder="请选择管理员"
@ -102,7 +102,7 @@ const OperationModal: FC<OperationModalProps> = (props) => {
label="产品描述" label="产品描述"
rules={[ rules={[
{ {
message: "请输入至少五个字符的产品描述!", message: '请输入至少五个字符的产品描述!',
min: 5, min: 5,
}, },
]} ]}

74
src/pages/list/basic-list/index.tsx

@ -1,6 +1,6 @@
import type { FC } from "react"; import type { FC } from 'react';
import React, { useState } from "react"; import React, { useState } from 'react';
import { DownOutlined, PlusOutlined } from "@ant-design/icons"; import { DownOutlined, PlusOutlined } from '@ant-design/icons';
import { import {
Avatar, Avatar,
Button, Button,
@ -14,19 +14,14 @@ import {
Progress, Progress,
Radio, Radio,
Row, Row,
} from "antd"; } from 'antd';
import { PageContainer } from "@ant-design/pro-components"; import { PageContainer } from '@ant-design/pro-components';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import OperationModal from "./components/OperationModal"; import OperationModal from './components/OperationModal';
import { import { addFakeList, queryFakeList, removeFakeList, updateFakeList } from './service';
addFakeList, import type { BasicListItemDataType } from './data.d';
queryFakeList, import useStyles from './style.style';
removeFakeList,
updateFakeList,
} from "./service";
import type { BasicListItemDataType } from "./data.d";
import useStyles from "./style.style";
const RadioButton = Radio.Button; const RadioButton = Radio.Button;
const RadioGroup = Radio.Group; const RadioGroup = Radio.Group;
const { Search } = Input; const { Search } = Input;
@ -58,7 +53,7 @@ const ListContent = ({
</div> </div>
<div className={styles.listContentItem}> <div className={styles.listContentItem}>
<span></span> <span></span>
<p>{dayjs(createdAt).format("YYYY-MM-DD HH:mm")}</p> <p>{dayjs(createdAt).format('YYYY-MM-DD HH:mm')}</p>
</div> </div>
<div className={styles.listContentItem}> <div className={styles.listContentItem}>
<Progress <Progress
@ -77,9 +72,7 @@ export const BasicList: FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const [done, setDone] = useState<boolean>(false); const [done, setDone] = useState<boolean>(false);
const [visible, setVisible] = useState<boolean>(false); const [visible, setVisible] = useState<boolean>(false);
const [current, setCurrent] = useState< const [current, setCurrent] = useState<Partial<BasicListItemDataType> | undefined>(undefined);
Partial<BasicListItemDataType> | undefined
>(undefined);
const { const {
data: listData, data: listData,
loading, loading,
@ -91,10 +84,10 @@ export const BasicList: FC = () => {
}); });
const { run: postRun } = useRequest( const { run: postRun } = useRequest(
(method, params) => { (method, params) => {
if (method === "remove") { if (method === 'remove') {
return removeFakeList(params); return removeFakeList(params);
} }
if (method === "update") { if (method === 'update') {
return updateFakeList(params); return updateFakeList(params);
} }
return addFakeList(params); return addFakeList(params);
@ -104,7 +97,7 @@ export const BasicList: FC = () => {
onSuccess: (result) => { onSuccess: (result) => {
mutate(result); mutate(result);
}, },
} },
); );
const list = listData?.list || []; const list = listData?.list || [];
const paginationProps = { const paginationProps = {
@ -118,21 +111,18 @@ export const BasicList: FC = () => {
setCurrent(item); setCurrent(item);
}; };
const deleteItem = (id: string) => { const deleteItem = (id: string) => {
postRun("remove", { postRun('remove', {
id, id,
}); });
}; };
const editAndDelete = ( const editAndDelete = (key: string | number, currentItem: BasicListItemDataType) => {
key: string | number, if (key === 'edit') showEditModal(currentItem);
currentItem: BasicListItemDataType else if (key === 'delete') {
) => {
if (key === "edit") showEditModal(currentItem);
else if (key === "delete") {
Modal.confirm({ Modal.confirm({
title: "删除任务", title: '删除任务',
content: "确定删除该任务吗?", content: '确定删除该任务吗?',
okText: "确认", okText: '确认',
cancelText: "取消", cancelText: '取消',
onOk: () => deleteItem(currentItem.id), onOk: () => deleteItem(currentItem.id),
}); });
} }
@ -144,11 +134,7 @@ export const BasicList: FC = () => {
<RadioButton value="progress"></RadioButton> <RadioButton value="progress"></RadioButton>
<RadioButton value="waiting"></RadioButton> <RadioButton value="waiting"></RadioButton>
</RadioGroup> </RadioGroup>
<Search <Search className={styles.extraContentSearch} placeholder="请输入" onSearch={() => ({})} />
className={styles.extraContentSearch}
placeholder="请输入"
onSearch={() => ({})}
/>
</div> </div>
); );
const MoreBtn: React.FC<{ const MoreBtn: React.FC<{
@ -174,7 +160,7 @@ export const BasicList: FC = () => {
}; };
const handleSubmit = (values: BasicListItemDataType) => { const handleSubmit = (values: BasicListItemDataType) => {
setDone(true); setDone(true);
const method = values?.id ? "update" : "add"; const method = values?.id ? 'update' : 'add';
postRun(method, values); postRun(method, values);
}; };
return ( return (
@ -203,7 +189,7 @@ export const BasicList: FC = () => {
marginTop: 24, marginTop: 24,
}} }}
bodyStyle={{ bodyStyle={{
padding: "0 32px 40px 32px", padding: '0 32px 40px 32px',
}} }}
extra={extraContent} extra={extraContent}
> >
@ -229,9 +215,7 @@ export const BasicList: FC = () => {
]} ]}
> >
<List.Item.Meta <List.Item.Meta
avatar={ avatar={<Avatar src={item.logo} shape="square" size="large" />}
<Avatar src={item.logo} shape="square" size="large" />
}
title={<a href={item.href}>{item.title}</a>} title={<a href={item.href}>{item.title}</a>}
description={item.subDescription} description={item.subDescription}
/> />
@ -248,7 +232,7 @@ export const BasicList: FC = () => {
setVisible(true); setVisible(true);
}} }}
style={{ style={{
width: "100%", width: '100%',
marginBottom: 8, marginBottom: 8,
}} }}
> >

94
src/pages/list/basic-list/style.style.ts

@ -1,98 +1,98 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
standardList: { standardList: {
"@media screen and (max-width: 1400px)": {}, '@media screen and (max-width: 1400px)': {},
}, },
".ant-card-head": { '.ant-card-head': {
borderBottom: "none", borderBottom: 'none',
}, },
".ant-card-head-title": { '.ant-card-head-title': {
[`@media screen and (max-width: ${token.screenXS}px)`]: { [`@media screen and (max-width: ${token.screenXS}px)`]: {
overflow: "visible", overflow: 'visible',
}, },
}, },
".ant-card-extra": { '.ant-card-extra': {
padding: "24px 0", padding: '24px 0',
}, },
".ant-list-pagination": { '.ant-list-pagination': {
marginTop: "24px", marginTop: '24px',
textAlign: "right", textAlign: 'right',
}, },
".ant-avatar-lg": { '.ant-avatar-lg': {
width: "48px", width: '48px',
height: "48px", height: '48px',
lineHeight: "48px", lineHeight: '48px',
}, },
headerInfo: { headerInfo: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
marginBottom: "16px", marginBottom: '16px',
"& > em": { '& > em': {
display: "none", display: 'none',
}, },
}, },
}, },
listContent: { listContent: {
"@media screen and (max-width: 1400px)": { '@media screen and (max-width: 1400px)': {
textAlign: "right", textAlign: 'right',
"& > div:last-child": { '& > div:last-child': {
top: "0", top: '0',
}, },
}, },
}, },
listContentItem: { listContentItem: {
display: "inline-block", display: 'inline-block',
marginLeft: "40px", marginLeft: '40px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: token.fontSize, fontSize: token.fontSize,
verticalAlign: "middle", verticalAlign: 'middle',
}, },
"> span": { '> span': {
lineHeight: "20px", lineHeight: '20px',
}, },
"> p": { '> p': {
marginTop: "4px", marginTop: '4px',
marginBottom: "0", marginBottom: '0',
lineHeight: "22px", lineHeight: '22px',
}, },
extraContentSearch: { extraContentSearch: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
width: "100%", width: '100%',
marginLeft: "0", marginLeft: '0',
}, },
}, },
".ant-list-item-content": { '.ant-list-item-content': {
[`@media screen and (max-width: ${token.screenXS}px)`]: { [`@media screen and (max-width: ${token.screenXS}px)`]: {
display: "block", display: 'block',
flex: "none", flex: 'none',
width: "100%", width: '100%',
}, },
}, },
".ant-list-item-action": { '.ant-list-item-action': {
[`@media screen and (max-width: ${token.screenXS}px)`]: { [`@media screen and (max-width: ${token.screenXS}px)`]: {
marginLeft: "0", marginLeft: '0',
}, },
}, },
listCard: { listCard: {
[`@media screen and (max-width: ${token.screenMD}px)`]: {}, [`@media screen and (max-width: ${token.screenMD}px)`]: {},
}, },
".ant-radio-group": { '.ant-radio-group': {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
display: "block", display: 'block',
marginBottom: "8px", marginBottom: '8px',
}, },
}, },
standardListForm: {}, standardListForm: {},
".ant-form-item": { '.ant-form-item': {
marginBottom: "12px", marginBottom: '12px',
"&:last-child": { marginBottom: "32px", paddingTop: "4px" }, '&:last-child': { marginBottom: '32px', paddingTop: '4px' },
}, },
formResult: { formResult: {
width: "100%", width: '100%',
}, },
"[class^='title']": { "[class^='title']": {
marginBottom: "8px", marginBottom: '8px',
}, },
}; };
}); });

2
src/pages/list/basic-list/utils/utils.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return {}; return {};

45
src/pages/list/card-list/index.tsx

@ -1,10 +1,10 @@
import { PlusOutlined } from "@ant-design/icons"; import { PlusOutlined } from '@ant-design/icons';
import { Button, Card, List, Typography } from "antd"; import { Button, Card, List, Typography } from 'antd';
import { PageContainer } from "@ant-design/pro-components"; import { PageContainer } from '@ant-design/pro-components';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import { queryFakeList } from "./service"; import { queryFakeList } from './service';
import type { CardListItemDataType } from "./data.d"; import type { CardListItemDataType } from './data.d';
import useStyles from "./style.style"; import useStyles from './style.style';
const { Paragraph } = Typography; const { Paragraph } = Typography;
const CardList = () => { const CardList = () => {
const { styles } = useStyles(); const { styles } = useStyles();
@ -17,30 +17,20 @@ const CardList = () => {
const content = ( const content = (
<div className={styles.pageHeaderContent}> <div className={styles.pageHeaderContent}>
<p> <p>
ant.design
ant.design
</p> </p>
<div className={styles.contentLink}> <div className={styles.contentLink}>
<a> <a>
<img <img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/MjEImQtenlyueSmVEfUD.svg" />{' '}
alt=""
src="https://gw.alipayobjects.com/zos/rmsportal/MjEImQtenlyueSmVEfUD.svg"
/>{" "}
</a> </a>
<a> <a>
<img <img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/NbuDUAuBlIApFuDvWiND.svg" />{' '}
alt=""
src="https://gw.alipayobjects.com/zos/rmsportal/NbuDUAuBlIApFuDvWiND.svg"
/>{" "}
</a> </a>
<a> <a>
<img <img alt="" src="https://gw.alipayobjects.com/zos/rmsportal/ohOEPSYdDTNnyMbGuyLb.svg" />{' '}
alt=""
src="https://gw.alipayobjects.com/zos/rmsportal/ohOEPSYdDTNnyMbGuyLb.svg"
/>{" "}
</a> </a>
</div> </div>
@ -78,19 +68,10 @@ const CardList = () => {
<Card <Card
hoverable hoverable
className={styles.card} className={styles.card}
actions={[ actions={[<a key="option1"></a>, <a key="option2"></a>]}
<a key="option1"></a>,
<a key="option2"></a>,
]}
> >
<Card.Meta <Card.Meta
avatar={ avatar={<img alt="" className={styles.cardAvatar} src={item.avatar} />}
<img
alt=""
className={styles.cardAvatar}
src={item.avatar}
/>
}
title={<a>{item.title}</a>} title={<a>{item.title}</a>}
description={ description={
<Paragraph <Paragraph

56
src/pages/list/card-list/style.style.ts

@ -1,71 +1,71 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
cardList: {}, cardList: {},
card: {}, card: {},
".ant-card-meta-title": { '.ant-card-meta-title': {
marginBottom: "12px", marginBottom: '12px',
"& > a": { '& > a': {
display: "inline-block", display: 'inline-block',
maxWidth: "100%", maxWidth: '100%',
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
}, },
".ant-card-body:hover": {}, '.ant-card-body:hover': {},
".ant-card-meta-title > a": { '.ant-card-meta-title > a': {
color: token.colorPrimary, color: token.colorPrimary,
}, },
item: { item: {
height: "64px", height: '64px',
}, },
".ant-list .ant-list-item-content-single": { '.ant-list .ant-list-item-content-single': {
maxWidth: "100%", maxWidth: '100%',
}, },
extraImg: { extraImg: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
display: "none", display: 'none',
}, },
}, },
img: { img: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
marginRight: "4px", marginRight: '4px',
}, },
}, },
newButton: { newButton: {
width: "100%", width: '100%',
height: "201px", height: '201px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
backgroundColor: token.colorBgContainer, backgroundColor: token.colorBgContainer,
borderColor: token.colorBorder, borderColor: token.colorBorder,
}, },
cardAvatar: { cardAvatar: {
width: "48px", width: '48px',
height: "48px", height: '48px',
borderRadius: "48px", borderRadius: '48px',
}, },
cardDescription: { cardDescription: {
overflow: "hidden", overflow: 'hidden',
whiteSpace: "nowrap", whiteSpace: 'nowrap',
textOverflow: "ellipsis", textOverflow: 'ellipsis',
wordBreak: "break-all", wordBreak: 'break-all',
}, },
pageHeaderContent: { pageHeaderContent: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
paddingBottom: "30px", paddingBottom: '30px',
}, },
}, },
contentLink: { contentLink: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
position: "absolute", position: 'absolute',
bottom: "-4px", bottom: '-4px',
left: "0", left: '0',
width: "1000px", width: '1000px',
}, },
}, },
a: { a: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
marginRight: "16px", marginRight: '16px',
}, },
}, },
}; };

2
src/pages/list/card-list/utils/utils.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return {}; return {};

52
src/pages/list/search/applications/components/StandardFormRow/index.style.ts

@ -1,51 +1,51 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
standardFormRow: { standardFormRow: {
display: "flex", display: 'flex',
marginBottom: "16px", marginBottom: '16px',
paddingBottom: "16px", paddingBottom: '16px',
borderBottom: "1px dashed @border-color-split", borderBottom: '1px dashed @border-color-split',
}, },
".ant-form-item, .ant-legacy-form-item": { '.ant-form-item, .ant-legacy-form-item': {
"&:last-child": { marginRight: "0" }, '&:last-child': { marginRight: '0' },
}, },
".ant-form-item-label, .ant-legacy-form-item-label": { '.ant-form-item-label, .ant-legacy-form-item-label': {
float: "left", float: 'left',
}, },
label: { label: {
flex: "0 0 auto", flex: '0 0 auto',
marginRight: "24px", marginRight: '24px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontSize: token.fontSize, fontSize: token.fontSize,
textAlign: "right", textAlign: 'right',
"& > span": { '& > span': {
display: "inline-block", display: 'inline-block',
height: "32px", height: '32px',
lineHeight: "32px", lineHeight: '32px',
"&::after": { '&::after': {
content: "':'", content: "':'",
}, },
}, },
}, },
".ant-form-item-label, .ant-legacy-form-item-label, .ant-form-item-control, .ant-legacy-form-item-control": '.ant-form-item-label, .ant-legacy-form-item-label, .ant-form-item-control, .ant-legacy-form-item-control':
{ {
padding: "0", padding: '0',
lineHeight: "32px", lineHeight: '32px',
}, },
content: { content: {
flex: "1 1 0", flex: '1 1 0',
}, },
standardFormRowLast: { standardFormRowLast: {
marginBottom: "0", marginBottom: '0',
paddingBottom: "0", paddingBottom: '0',
border: "none", border: 'none',
}, },
standardFormRowBlock: {}, standardFormRowBlock: {},
".ant-form-item, .ant-legacy-form-item, div.ant-form-item-control-wrapper, div.ant-legacy-form-item-control-wrapper": '.ant-form-item, .ant-legacy-form-item, div.ant-form-item-control-wrapper, div.ant-legacy-form-item-control-wrapper':
{ {
display: "block", display: 'block',
}, },
standardFormRowGrid: {}, standardFormRowGrid: {},
}; };

6
src/pages/list/search/applications/components/StandardFormRow/index.tsx

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
type StandardFormRowProps = { type StandardFormRowProps = {
title?: string; title?: string;
last?: boolean; last?: boolean;

38
src/pages/list/search/applications/components/TagSelect/index.style.ts

@ -1,35 +1,35 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
tagSelect: { tagSelect: {
position: "relative", position: 'relative',
maxHeight: "32px", maxHeight: '32px',
marginLeft: "-8px", marginLeft: '-8px',
overflow: "hidden", overflow: 'hidden',
lineHeight: "32px", lineHeight: '32px',
transition: "all 0.3s", transition: 'all 0.3s',
userSelect: "none", userSelect: 'none',
}, },
".ant-tag": { '.ant-tag': {
marginRight: "24px", marginRight: '24px',
padding: "0 8px", padding: '0 8px',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
expanded: { expanded: {
maxHeight: "200px", maxHeight: '200px',
transition: "all 0.3s", transition: 'all 0.3s',
}, },
trigger: { trigger: {
position: "absolute", position: 'absolute',
top: "0", top: '0',
right: "0", right: '0',
}, },
"span.anticon": { 'span.anticon': {
fontSize: "12px", fontSize: '12px',
}, },
hasExpandTag: { hasExpandTag: {
paddingRight: "50px", paddingRight: '50px',
}, },
}; };
}); });

47
src/pages/list/search/applications/components/TagSelect/index.tsx

@ -1,10 +1,10 @@
import { DownOutlined, UpOutlined } from "@ant-design/icons"; import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useBoolean, useControllableValue } from "ahooks"; import { useBoolean, useControllableValue } from 'ahooks';
import { Tag } from "antd"; import { Tag } from 'antd';
import classNames from "classnames"; import classNames from 'classnames';
import type { FC } from "react"; import type { FC } from 'react';
import React from "react"; import React from 'react';
import useStyles from "./index.style"; import useStyles from './index.style';
const { CheckableTag } = Tag; const { CheckableTag } = Tag;
export interface TagSelectOptionProps { export interface TagSelectOptionProps {
value: string | number; value: string | number;
@ -25,10 +25,7 @@ const TagSelectOption: React.FC<TagSelectOptionProps> & {
</CheckableTag> </CheckableTag>
); );
TagSelectOption.isTagSelectOption = true; TagSelectOption.isTagSelectOption = true;
type TagSelectOptionElement = React.ReactElement< type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
TagSelectOptionProps,
typeof TagSelectOption
>;
export interface TagSelectProps { export interface TagSelectProps {
onChange?: (value: (string | number)[]) => void; onChange?: (value: (string | number)[]) => void;
expandable?: boolean; expandable?: boolean;
@ -49,25 +46,15 @@ const TagSelect: FC<TagSelectProps> & {
Option: typeof TagSelectOption; Option: typeof TagSelectOption;
} = (props) => { } = (props) => {
const { styles } = useStyles(); const { styles } = useStyles();
const { const { children, hideCheckAll = false, className, style, expandable, actionsText = {} } = props;
children,
hideCheckAll = false,
className,
style,
expandable,
actionsText = {},
} = props;
const [expand, { toggle }] = useBoolean(); const [expand, { toggle }] = useBoolean();
const [value, setValue] = useControllableValue<(string | number)[]>(props); const [value, setValue] = useControllableValue<(string | number)[]>(props);
const isTagSelectOption = (node: TagSelectOptionElement) => const isTagSelectOption = (node: TagSelectOptionElement) =>
node && node &&
node.type && node.type &&
(node.type.isTagSelectOption || (node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption');
node.type.displayName === "TagSelectOption");
const getAllTags = () => { const getAllTags = () => {
const childrenArray = React.Children.toArray( const childrenArray = React.Children.toArray(children) as TagSelectOptionElement[];
children
) as TagSelectOptionElement[];
const checkedTags = childrenArray const checkedTags = childrenArray
.filter((child) => isTagSelectOption(child)) .filter((child) => isTagSelectOption(child))
.map((child) => child.props.value); .map((child) => child.props.value);
@ -91,11 +78,7 @@ const TagSelect: FC<TagSelectProps> & {
setValue(checkedTags); setValue(checkedTags);
}; };
const checkedAll = getAllTags().length === value?.length; const checkedAll = getAllTags().length === value?.length;
const { const { expandText = '展开', collapseText = '收起', selectAllText = '全部' } = actionsText;
expandText = "展开",
collapseText = "收起",
selectAllText = "全部",
} = actionsText;
const cls = classNames(styles.tagSelect, className, { const cls = classNames(styles.tagSelect, className, {
[styles.hasExpandTag]: expandable, [styles.hasExpandTag]: expandable,
[styles.expanded]: expand, [styles.expanded]: expand,
@ -103,11 +86,7 @@ const TagSelect: FC<TagSelectProps> & {
return ( return (
<div className={cls} style={style}> <div className={cls} style={style}>
{hideCheckAll ? null : ( {hideCheckAll ? null : (
<CheckableTag <CheckableTag checked={checkedAll} key="tag-select-__all__" onChange={onSelectAll}>
checked={checkedAll}
key="tag-select-__all__"
onChange={onSelectAll}
>
{selectAllText} {selectAllText}
</CheckableTag> </CheckableTag>
)} )}

70
src/pages/list/search/applications/index.tsx

@ -3,32 +3,21 @@ import {
EditOutlined, EditOutlined,
EllipsisOutlined, EllipsisOutlined,
ShareAltOutlined, ShareAltOutlined,
} from "@ant-design/icons"; } from '@ant-design/icons';
import { import { Avatar, Card, Col, Dropdown, Form, List, Menu, Row, Select, Tooltip } from 'antd';
Avatar, import numeral from 'numeral';
Card, import type { FC } from 'react';
Col, import React from 'react';
Dropdown, import { useRequest } from '@umijs/max';
Form, import StandardFormRow from './components/StandardFormRow';
List, import TagSelect from './components/TagSelect';
Menu, import type { ListItemDataType } from './data.d';
Row, import { queryFakeList } from './service';
Select, import useStyles from './style.style';
Tooltip,
} from "antd";
import numeral from "numeral";
import type { FC } from "react";
import React from "react";
import { useRequest } from "@umijs/max";
import StandardFormRow from "./components/StandardFormRow";
import TagSelect from "./components/TagSelect";
import type { ListItemDataType } from "./data.d";
import { queryFakeList } from "./service";
import useStyles from "./style.style";
const { Option } = Select; const { Option } = Select;
export function formatWan(val: number) { export function formatWan(val: number) {
const v = val * 1; const v = val * 1;
if (!v || Number.isNaN(v)) return ""; if (!v || Number.isNaN(v)) return '';
let result: React.ReactNode = val; let result: React.ReactNode = val;
if (val > 10000) { if (val > 10000) {
result = ( result = (
@ -36,10 +25,10 @@ export function formatWan(val: number) {
{Math.floor(val / 10000)} {Math.floor(val / 10000)}
<span <span
style={{ style={{
position: "relative", position: 'relative',
top: -2, top: -2,
fontSize: 14, fontSize: 14,
fontStyle: "normal", fontStyle: 'normal',
marginLeft: 2, marginLeft: 2,
}} }}
> >
@ -81,7 +70,7 @@ const CardInfo: React.FC<{
export const Applications: FC<Record<string, any>> = () => { export const Applications: FC<Record<string, any>> = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const { data, loading, run } = useRequest((values: any) => { const { data, loading, run } = useRequest((values: any) => {
console.log("form data", values); console.log('form data', values);
return queryFakeList({ return queryFakeList({
count: 8, count: 8,
}); });
@ -90,29 +79,17 @@ export const Applications: FC<Record<string, any>> = () => {
const itemMenu = ( const itemMenu = (
<Menu> <Menu>
<Menu.Item> <Menu.Item>
<a <a target="_blank" rel="noopener noreferrer" href="https://www.alipay.com/">
target="_blank"
rel="noopener noreferrer"
href="https://www.alipay.com/"
>
1st menu item 1st menu item
</a> </a>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<a <a target="_blank" rel="noopener noreferrer" href="https://www.taobao.com/">
target="_blank"
rel="noopener noreferrer"
href="https://www.taobao.com/"
>
2nd menu item 2nd menu item
</a> </a>
</Menu.Item> </Menu.Item>
<Menu.Item> <Menu.Item>
<a <a target="_blank" rel="noopener noreferrer" href="https://www.tmall.com/">
target="_blank"
rel="noopener noreferrer"
href="https://www.tmall.com/"
>
3d menu item 3d menu item
</a> </a>
</Menu.Item> </Menu.Item>
@ -158,7 +135,7 @@ export const Applications: FC<Record<string, any>> = () => {
placeholder="不限" placeholder="不限"
style={{ style={{
maxWidth: 200, maxWidth: 200,
width: "100%", width: '100%',
}} }}
> >
<Option value="lisa"></Option> <Option value="lisa"></Option>
@ -171,7 +148,7 @@ export const Applications: FC<Record<string, any>> = () => {
placeholder="不限" placeholder="不限"
style={{ style={{
maxWidth: 200, maxWidth: 200,
width: "100%", width: '100%',
}} }}
> >
<Option value="good"></Option> <Option value="good"></Option>
@ -219,14 +196,11 @@ export const Applications: FC<Record<string, any>> = () => {
</Dropdown>, </Dropdown>,
]} ]}
> >
<Card.Meta <Card.Meta avatar={<Avatar size="small" src={item.avatar} />} title={item.title} />
avatar={<Avatar size="small" src={item.avatar} />}
title={item.title}
/>
<div className={styles.cardItemContent}> <div className={styles.cardItemContent}>
<CardInfo <CardInfo
activeUser={formatWan(item.activeUser)} activeUser={formatWan(item.activeUser)}
newUser={numeral(item.newUser).format("0,0")} newUser={numeral(item.newUser).format('0,0')}
/> />
</div> </div>
</Card> </Card>

58
src/pages/list/search/applications/style.style.ts

@ -1,45 +1,45 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
filterCardList: {}, filterCardList: {},
".ant-card-meta-content": { '.ant-card-meta-content': {
marginTop: "0", marginTop: '0',
}, },
"// disabled white space .ant-card-meta-avatar": { '// disabled white space .ant-card-meta-avatar': {
fontSize: "0", fontSize: '0',
}, },
".ant-list .ant-list-item-content-single": { '.ant-list .ant-list-item-content-single': {
maxWidth: "100%", maxWidth: '100%',
}, },
cardInfo: { cardInfo: {
marginTop: "16px", marginTop: '16px',
marginLeft: "40px", marginLeft: '40px',
zoom: "1", zoom: '1',
"&::before, &::after": { display: "table", content: "' '" }, '&::before, &::after': { display: 'table', content: "' '" },
"&::after": { '&::after': {
clear: "both", clear: 'both',
height: "0", height: '0',
fontSize: "0", fontSize: '0',
visibility: "hidden", visibility: 'hidden',
}, },
"& > div": { '& > div': {
position: "relative", position: 'relative',
float: "left", float: 'left',
width: "50%", width: '50%',
textAlign: "left", textAlign: 'left',
}, },
}, },
p: { p: {
margin: "0", margin: '0',
fontSize: "24px", fontSize: '24px',
lineHeight: "32px", lineHeight: '32px',
}, },
"p:first-child": { 'p:first-child': {
marginBottom: "4px", marginBottom: '4px',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: "12px", fontSize: '12px',
lineHeight: "20px", lineHeight: '20px',
}, },
}; };
}); });

2
src/pages/list/search/applications/utils/utils.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return {}; return {};

14
src/pages/list/search/articles/components/ArticleListContent/index.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
@ -6,15 +6,15 @@ const useStyles = createStyles(({ token }) => {
[`@media screen and (max-width: ${token.screenXS}px)`]: {}, [`@media screen and (max-width: ${token.screenXS}px)`]: {},
}, },
description: { description: {
maxWidth: "720px", maxWidth: '720px',
lineHeight: "22px", lineHeight: '22px',
}, },
extra: { extra: {
[`@media screen and (max-width: ${token.screenXS}px)`]: { [`@media screen and (max-width: ${token.screenXS}px)`]: {
"& > em": { '& > em': {
display: "block", display: 'block',
marginTop: "8px", marginTop: '8px',
marginLeft: "0", marginLeft: '0',
}, },
}, },
}, },

10
src/pages/list/search/articles/components/ArticleListContent/index.tsx

@ -1,7 +1,7 @@
import { Avatar } from "antd"; import { Avatar } from 'antd';
import React from "react"; import React from 'react';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import useStyles from "./index.style"; import useStyles from './index.style';
type ArticleListContentProps = { type ArticleListContentProps = {
data: { data: {
content: React.ReactNode; content: React.ReactNode;
@ -21,7 +21,7 @@ const ArticleListContent: React.FC<ArticleListContentProps> = ({
<div className={styles.extra}> <div className={styles.extra}>
<Avatar src={avatar} size="small" /> <Avatar src={avatar} size="small" />
<a href={href}>{owner}</a> <a href={href}>{href}</a> <a href={href}>{owner}</a> <a href={href}>{href}</a>
<em>{dayjs(updatedAt).format("YYYY-MM-DD HH:mm")}</em> <em>{dayjs(updatedAt).format('YYYY-MM-DD HH:mm')}</em>
</div> </div>
</div> </div>
); );

54
src/pages/list/search/articles/components/StandardFormRow/index.style.ts

@ -1,52 +1,52 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
standardFormRow: { standardFormRow: {
display: "flex", display: 'flex',
width: "100%", width: '100%',
marginBottom: "16px", marginBottom: '16px',
paddingBottom: "16px", paddingBottom: '16px',
borderBottom: "1px dashed @border-color-split", borderBottom: '1px dashed @border-color-split',
}, },
".ant-form-item, .ant-legacy-form-item": { '.ant-form-item, .ant-legacy-form-item': {
"&:last-child": { display: "block", marginRight: "0" }, '&:last-child': { display: 'block', marginRight: '0' },
}, },
".ant-form-item-label, .ant-legacy-form-item-label": { '.ant-form-item-label, .ant-legacy-form-item-label': {
float: "left", float: 'left',
}, },
label: { label: {
flex: "0 0 auto", flex: '0 0 auto',
marginRight: "24px", marginRight: '24px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontSize: token.fontSize, fontSize: token.fontSize,
textAlign: "right", textAlign: 'right',
"& > span": { '& > span': {
display: "inline-block", display: 'inline-block',
height: "32px", height: '32px',
lineHeight: "32px", lineHeight: '32px',
"&::after": { '&::after': {
content: "':'", content: "':'",
}, },
}, },
}, },
".ant-form-item-label, .ant-legacy-form-item-label, .ant-form-item-control, .ant-legacy-form-item-control": '.ant-form-item-label, .ant-legacy-form-item-label, .ant-form-item-control, .ant-legacy-form-item-control':
{ {
padding: "0", padding: '0',
lineHeight: "32px", lineHeight: '32px',
}, },
content: { content: {
flex: "1 1 0", flex: '1 1 0',
}, },
standardFormRowLast: { standardFormRowLast: {
marginBottom: "0", marginBottom: '0',
paddingBottom: "0", paddingBottom: '0',
border: "none", border: 'none',
}, },
standardFormRowBlock: {}, standardFormRowBlock: {},
".ant-form-item, .ant-legacy-form-item, div.ant-form-item-control-wrapper, div.ant-legacy-form-item-control-wrapper": '.ant-form-item, .ant-legacy-form-item, div.ant-form-item-control-wrapper, div.ant-legacy-form-item-control-wrapper':
{ {
display: "block", display: 'block',
}, },
standardFormRowGrid: {}, standardFormRowGrid: {},
}; };

6
src/pages/list/search/articles/components/StandardFormRow/index.tsx

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
type StandardFormRowProps = { type StandardFormRowProps = {
title?: string; title?: string;
last?: boolean; last?: boolean;

38
src/pages/list/search/articles/components/TagSelect/index.style.ts

@ -1,35 +1,35 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
tagSelect: { tagSelect: {
position: "relative", position: 'relative',
maxHeight: "32px", maxHeight: '32px',
marginLeft: "-8px", marginLeft: '-8px',
overflow: "hidden", overflow: 'hidden',
lineHeight: "32px", lineHeight: '32px',
transition: "all 0.3s", transition: 'all 0.3s',
userSelect: "none", userSelect: 'none',
}, },
".ant-tag": { '.ant-tag': {
marginRight: "24px", marginRight: '24px',
padding: "0 8px", padding: '0 8px',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
expanded: { expanded: {
maxHeight: "200px", maxHeight: '200px',
transition: "all 0.3s", transition: 'all 0.3s',
}, },
trigger: { trigger: {
position: "absolute", position: 'absolute',
top: "0", top: '0',
right: "0", right: '0',
}, },
"span.anticon": { 'span.anticon': {
fontSize: "12px", fontSize: '12px',
}, },
hasExpandTag: { hasExpandTag: {
paddingRight: "50px", paddingRight: '50px',
}, },
}; };
}); });

47
src/pages/list/search/articles/components/TagSelect/index.tsx

@ -1,10 +1,10 @@
import { DownOutlined, UpOutlined } from "@ant-design/icons"; import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useBoolean, useControllableValue } from "ahooks"; import { useBoolean, useControllableValue } from 'ahooks';
import { Tag } from "antd"; import { Tag } from 'antd';
import classNames from "classnames"; import classNames from 'classnames';
import type { FC } from "react"; import type { FC } from 'react';
import React from "react"; import React from 'react';
import useStyles from "./index.style"; import useStyles from './index.style';
const { CheckableTag } = Tag; const { CheckableTag } = Tag;
export interface TagSelectOptionProps { export interface TagSelectOptionProps {
value: string | number; value: string | number;
@ -25,10 +25,7 @@ const TagSelectOption: React.FC<TagSelectOptionProps> & {
</CheckableTag> </CheckableTag>
); );
TagSelectOption.isTagSelectOption = true; TagSelectOption.isTagSelectOption = true;
type TagSelectOptionElement = React.ReactElement< type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
TagSelectOptionProps,
typeof TagSelectOption
>;
export interface TagSelectProps { export interface TagSelectProps {
onChange?: (value: (string | number)[]) => void; onChange?: (value: (string | number)[]) => void;
expandable?: boolean; expandable?: boolean;
@ -49,25 +46,15 @@ const TagSelect: FC<TagSelectProps> & {
Option: typeof TagSelectOption; Option: typeof TagSelectOption;
} = (props) => { } = (props) => {
const { styles } = useStyles(); const { styles } = useStyles();
const { const { children, hideCheckAll = false, className, style, expandable, actionsText = {} } = props;
children,
hideCheckAll = false,
className,
style,
expandable,
actionsText = {},
} = props;
const [expand, { toggle }] = useBoolean(); const [expand, { toggle }] = useBoolean();
const [value, setValue] = useControllableValue<(string | number)[]>(props); const [value, setValue] = useControllableValue<(string | number)[]>(props);
const isTagSelectOption = (node: TagSelectOptionElement) => const isTagSelectOption = (node: TagSelectOptionElement) =>
node && node &&
node.type && node.type &&
(node.type.isTagSelectOption || (node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption');
node.type.displayName === "TagSelectOption");
const getAllTags = () => { const getAllTags = () => {
const childrenArray = React.Children.toArray( const childrenArray = React.Children.toArray(children) as TagSelectOptionElement[];
children
) as TagSelectOptionElement[];
const checkedTags = childrenArray const checkedTags = childrenArray
.filter((child) => isTagSelectOption(child)) .filter((child) => isTagSelectOption(child))
.map((child) => child.props.value); .map((child) => child.props.value);
@ -91,11 +78,7 @@ const TagSelect: FC<TagSelectProps> & {
setValue(checkedTags); setValue(checkedTags);
}; };
const checkedAll = getAllTags().length === value?.length; const checkedAll = getAllTags().length === value?.length;
const { const { expandText = '展开', collapseText = '收起', selectAllText = '全部' } = actionsText;
expandText = "展开",
collapseText = "收起",
selectAllText = "全部",
} = actionsText;
const cls = classNames(styles.tagSelect, className, { const cls = classNames(styles.tagSelect, className, {
[styles.hasExpandTag]: expandable, [styles.hasExpandTag]: expandable,
[styles.expanded]: expand, [styles.expanded]: expand,
@ -103,11 +86,7 @@ const TagSelect: FC<TagSelectProps> & {
return ( return (
<div className={cls} style={style}> <div className={cls} style={style}>
{hideCheckAll ? null : ( {hideCheckAll ? null : (
<CheckableTag <CheckableTag checked={checkedAll} key="tag-select-__all__" onChange={onSelectAll}>
checked={checkedAll}
key="tag-select-__all__"
onChange={onSelectAll}
>
{selectAllText} {selectAllText}
</CheckableTag> </CheckableTag>
)} )}

71
src/pages/list/search/articles/index.tsx

@ -1,19 +1,14 @@
import { import { LikeOutlined, LoadingOutlined, MessageOutlined, StarOutlined } from '@ant-design/icons';
LikeOutlined, import { Button, Card, Col, Form, List, Row, Select, Tag } from 'antd';
LoadingOutlined, import type { FC } from 'react';
MessageOutlined, import React from 'react';
StarOutlined, import { useRequest } from '@umijs/max';
} from "@ant-design/icons"; import ArticleListContent from './components/ArticleListContent';
import { Button, Card, Col, Form, List, Row, Select, Tag } from "antd"; import StandardFormRow from './components/StandardFormRow';
import type { FC } from "react"; import TagSelect from './components/TagSelect';
import React from "react"; import type { ListItemDataType } from './data.d';
import { useRequest } from "@umijs/max"; import { queryFakeList } from './service';
import ArticleListContent from "./components/ArticleListContent"; import useStyles from './style.style';
import StandardFormRow from "./components/StandardFormRow";
import TagSelect from "./components/TagSelect";
import type { ListItemDataType } from "./data.d";
import { queryFakeList } from "./service";
import useStyles from "./style.style";
const { Option } = Select; const { Option } = Select;
const FormItem = Form.Item; const FormItem = Form.Item;
const pageSize = 5; const pageSize = 5;
@ -28,34 +23,34 @@ const Articles: FC = () => {
}, },
{ {
loadMore: true, loadMore: true,
} },
); );
const list = data?.list || []; const list = data?.list || [];
const setOwner = () => { const setOwner = () => {
form.setFieldsValue({ form.setFieldsValue({
owner: ["wzj"], owner: ['wzj'],
}); });
}; };
const owners = [ const owners = [
{ {
id: "wzj", id: 'wzj',
name: "我自己", name: '我自己',
}, },
{ {
id: "wjh", id: 'wjh',
name: "吴家豪", name: '吴家豪',
}, },
{ {
id: "zxx", id: 'zxx',
name: "周星星", name: '周星星',
}, },
{ {
id: "zly", id: 'zly',
name: "赵丽颖", name: '赵丽颖',
}, },
{ {
id: "ym", id: 'ym',
name: "姚明", name: '姚明',
}, },
]; ];
const IconText: React.FC<{ const IconText: React.FC<{
@ -63,7 +58,7 @@ const Articles: FC = () => {
text: React.ReactNode; text: React.ReactNode;
}> = ({ type, text }) => { }> = ({ type, text }) => {
switch (type) { switch (type) {
case "star-o": case 'star-o':
return ( return (
<span> <span>
<StarOutlined <StarOutlined
@ -74,7 +69,7 @@ const Articles: FC = () => {
{text} {text}
</span> </span>
); );
case "like-o": case 'like-o':
return ( return (
<span> <span>
<LikeOutlined <LikeOutlined
@ -85,7 +80,7 @@ const Articles: FC = () => {
{text} {text}
</span> </span>
); );
case "message": case 'message':
return ( return (
<span> <span>
<MessageOutlined <MessageOutlined
@ -116,7 +111,7 @@ const Articles: FC = () => {
const loadMoreDom = list.length > 0 && ( const loadMoreDom = list.length > 0 && (
<div <div
style={{ style={{
textAlign: "center", textAlign: 'center',
marginTop: 16, marginTop: 16,
}} }}
> >
@ -132,7 +127,7 @@ const Articles: FC = () => {
<LoadingOutlined /> ... <LoadingOutlined /> ...
</span> </span>
) : ( ) : (
"加载更多" '加载更多'
)} )}
</Button> </Button>
</div> </div>
@ -144,7 +139,7 @@ const Articles: FC = () => {
layout="inline" layout="inline"
form={form} form={form}
initialValues={{ initialValues={{
owner: ["wjh", "zxx"], owner: ['wjh', 'zxx'],
}} }}
onValuesChange={reload} onValuesChange={reload}
> >
@ -178,7 +173,7 @@ const Articles: FC = () => {
mode="multiple" mode="multiple"
placeholder="选择 owner" placeholder="选择 owner"
style={{ style={{
minWidth: "6rem", minWidth: '6rem',
}} }}
> >
{owners.map((owner) => ( {owners.map((owner) => (
@ -200,7 +195,7 @@ const Articles: FC = () => {
placeholder="不限" placeholder="不限"
style={{ style={{
maxWidth: 200, maxWidth: 200,
width: "100%", width: '100%',
}} }}
> >
<Option value="lisa"></Option> <Option value="lisa"></Option>
@ -213,7 +208,7 @@ const Articles: FC = () => {
placeholder="不限" placeholder="不限"
style={{ style={{
maxWidth: 200, maxWidth: 200,
width: "100%", width: '100%',
}} }}
> >
<Option value="good"></Option> <Option value="good"></Option>
@ -230,7 +225,7 @@ const Articles: FC = () => {
}} }}
bordered={false} bordered={false}
bodyStyle={{ bodyStyle={{
padding: "8px 32px 32px 32px", padding: '8px 32px 32px 32px',
}} }}
> >
<List<ListItemDataType> <List<ListItemDataType>

12
src/pages/list/search/articles/style.style.ts

@ -1,20 +1,20 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
"a.listItemMetaTitle": { 'a.listItemMetaTitle': {
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
listItemExtra: { listItemExtra: {
[`@media screen and (max-width: ${token.screenLG}px)`]: { [`@media screen and (max-width: ${token.screenLG}px)`]: {
width: "0", width: '0',
height: "1px", height: '1px',
}, },
}, },
selfTrigger: { selfTrigger: {
[`@media screen and (max-width: ${token.screenMD}px)`]: { [`@media screen and (max-width: ${token.screenMD}px)`]: {
display: "block", display: 'block',
marginLeft: "0", marginLeft: '0',
}, },
}, },
}; };

32
src/pages/list/search/projects/components/AvatarList/index.style.ts

@ -1,26 +1,26 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
avatarList: { avatarList: {
display: "inline-block", display: 'inline-block',
}, },
ul: { ul: {
display: "inline-block", display: 'inline-block',
marginLeft: "8px", marginLeft: '8px',
fontSize: "0", fontSize: '0',
}, },
avatarItem: { avatarItem: {
display: "inline-block", display: 'inline-block',
width: token.controlHeight, width: token.controlHeight,
height: token.controlHeight, height: token.controlHeight,
marginLeft: "-8px", marginLeft: '-8px',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
".ant-avatar": { '.ant-avatar': {
width: "20px", width: '20px',
height: "20px", height: '20px',
lineHeight: "20px", lineHeight: '20px',
}, },
avatarItemLarge: { avatarItemLarge: {
width: token.controlHeightLG, width: token.controlHeightLG,
@ -31,12 +31,12 @@ const useStyles = createStyles(({ token }) => {
height: token.controlHeightSM, height: token.controlHeightSM,
}, },
avatarItemMini: { avatarItemMini: {
width: "20px", width: '20px',
height: "20px", height: '20px',
}, },
".ant-avatar-string": { '.ant-avatar-string': {
fontSize: "12px", fontSize: '12px',
lineHeight: "18px", lineHeight: '18px',
}, },
}; };
}); });

43
src/pages/list/search/projects/components/AvatarList/index.tsx

@ -1,8 +1,8 @@
import { Avatar, Tooltip } from "antd"; import { Avatar, Tooltip } from 'antd';
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
export declare type SizeType = number | "small" | "default" | "large"; export declare type SizeType = number | 'small' | 'default' | 'large';
export type AvatarItemProps = { export type AvatarItemProps = {
tips: React.ReactNode; tips: React.ReactNode;
src: string; src: string;
@ -16,22 +16,15 @@ export type AvatarListProps = {
maxLength?: number; maxLength?: number;
excessItemsStyle?: React.CSSProperties; excessItemsStyle?: React.CSSProperties;
style?: React.CSSProperties; style?: React.CSSProperties;
children: children: React.ReactElement<AvatarItemProps> | React.ReactElement<AvatarItemProps>[];
| React.ReactElement<AvatarItemProps>
| React.ReactElement<AvatarItemProps>[];
}; };
const avatarSizeToClassName = (size?: SizeType | "mini") => const avatarSizeToClassName = (size?: SizeType | 'mini') =>
classNames(styles.avatarItem, { classNames(styles.avatarItem, {
[styles.avatarItemLarge]: size === "large", [styles.avatarItemLarge]: size === 'large',
[styles.avatarItemSmall]: size === "small", [styles.avatarItemSmall]: size === 'small',
[styles.avatarItemMini]: size === "mini", [styles.avatarItemMini]: size === 'mini',
}); });
const Item: React.FC<AvatarItemProps> = ({ const Item: React.FC<AvatarItemProps> = ({ src, size, tips, onClick = () => {} }) => {
src,
size,
tips,
onClick = () => {},
}) => {
const cls = avatarSizeToClassName(size); const cls = avatarSizeToClassName(size);
return ( return (
<li className={cls} onClick={onClick}> <li className={cls} onClick={onClick}>
@ -41,7 +34,7 @@ const Item: React.FC<AvatarItemProps> = ({
src={src} src={src}
size={size} size={size}
style={{ style={{
cursor: "pointer", cursor: 'pointer',
}} }}
/> />
</Tooltip> </Tooltip>
@ -57,22 +50,18 @@ const AvatarList: React.FC<AvatarListProps> & {
const { styles } = useStyles(); const { styles } = useStyles();
const numOfChildren = React.Children.count(children); const numOfChildren = React.Children.count(children);
const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength; const numToShow = maxLength >= numOfChildren ? numOfChildren : maxLength;
const childrenArray = React.Children.toArray( const childrenArray = React.Children.toArray(children) as React.ReactElement<AvatarItemProps>[];
children
) as React.ReactElement<AvatarItemProps>[];
const childrenWithProps = childrenArray.slice(0, numToShow).map((child) => const childrenWithProps = childrenArray.slice(0, numToShow).map((child) =>
React.cloneElement(child, { React.cloneElement(child, {
size, size,
}) }),
); );
if (numToShow < numOfChildren) { if (numToShow < numOfChildren) {
const cls = avatarSizeToClassName(size); const cls = avatarSizeToClassName(size);
childrenWithProps.push( childrenWithProps.push(
<li key="exceed" className={cls}> <li key="exceed" className={cls}>
<Avatar size={size} style={excessItemsStyle}>{`+${ <Avatar size={size} style={excessItemsStyle}>{`+${numOfChildren - maxLength}`}</Avatar>
numOfChildren - maxLength </li>,
}`}</Avatar>
</li>
); );
} }
return ( return (

54
src/pages/list/search/projects/components/StandardFormRow/index.style.ts

@ -1,52 +1,52 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
standardFormRow: { standardFormRow: {
display: "flex", display: 'flex',
width: "100%", width: '100%',
marginBottom: "16px", marginBottom: '16px',
paddingBottom: "16px", paddingBottom: '16px',
borderBottom: "1px dashed @border-color-split", borderBottom: '1px dashed @border-color-split',
}, },
".ant-form-item, .ant-legacy-form-item": { '.ant-form-item, .ant-legacy-form-item': {
"&:last-child": { display: "block", marginRight: "0" }, '&:last-child': { display: 'block', marginRight: '0' },
}, },
".ant-form-item-label, .ant-legacy-form-item-label": { '.ant-form-item-label, .ant-legacy-form-item-label': {
float: "left", float: 'left',
}, },
label: { label: {
flex: "0 0 auto", flex: '0 0 auto',
marginRight: "24px", marginRight: '24px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontSize: token.fontSize, fontSize: token.fontSize,
textAlign: "right", textAlign: 'right',
"& > span": { '& > span': {
display: "inline-block", display: 'inline-block',
height: "32px", height: '32px',
lineHeight: "32px", lineHeight: '32px',
"&::after": { '&::after': {
content: "':'", content: "':'",
}, },
}, },
}, },
".ant-form-item-label, .ant-legacy-form-item-label, .ant-form-item-control, .ant-legacy-form-item-control": '.ant-form-item-label, .ant-legacy-form-item-label, .ant-form-item-control, .ant-legacy-form-item-control':
{ {
padding: "0", padding: '0',
lineHeight: "32px", lineHeight: '32px',
}, },
content: { content: {
flex: "1 1 0", flex: '1 1 0',
}, },
standardFormRowLast: { standardFormRowLast: {
marginBottom: "0", marginBottom: '0',
paddingBottom: "0", paddingBottom: '0',
border: "none", border: 'none',
}, },
standardFormRowBlock: {}, standardFormRowBlock: {},
".ant-form-item, .ant-legacy-form-item, div.ant-form-item-control-wrapper, div.ant-legacy-form-item-control-wrapper": '.ant-form-item, .ant-legacy-form-item, div.ant-form-item-control-wrapper, div.ant-legacy-form-item-control-wrapper':
{ {
display: "block", display: 'block',
}, },
standardFormRowGrid: {}, standardFormRowGrid: {},
}; };

6
src/pages/list/search/projects/components/StandardFormRow/index.tsx

@ -1,6 +1,6 @@
import React from "react"; import React from 'react';
import classNames from "classnames"; import classNames from 'classnames';
import useStyles from "./index.style"; import useStyles from './index.style';
type StandardFormRowProps = { type StandardFormRowProps = {
title?: string; title?: string;
last?: boolean; last?: boolean;

38
src/pages/list/search/projects/components/TagSelect/index.style.ts

@ -1,35 +1,35 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
tagSelect: { tagSelect: {
position: "relative", position: 'relative',
maxHeight: "32px", maxHeight: '32px',
marginLeft: "-8px", marginLeft: '-8px',
overflow: "hidden", overflow: 'hidden',
lineHeight: "32px", lineHeight: '32px',
transition: "all 0.3s", transition: 'all 0.3s',
userSelect: "none", userSelect: 'none',
}, },
".ant-tag": { '.ant-tag': {
marginRight: "24px", marginRight: '24px',
padding: "0 8px", padding: '0 8px',
fontSize: token.fontSize, fontSize: token.fontSize,
}, },
expanded: { expanded: {
maxHeight: "200px", maxHeight: '200px',
transition: "all 0.3s", transition: 'all 0.3s',
}, },
trigger: { trigger: {
position: "absolute", position: 'absolute',
top: "0", top: '0',
right: "0", right: '0',
}, },
"span.anticon": { 'span.anticon': {
fontSize: "12px", fontSize: '12px',
}, },
hasExpandTag: { hasExpandTag: {
paddingRight: "50px", paddingRight: '50px',
}, },
}; };
}); });

47
src/pages/list/search/projects/components/TagSelect/index.tsx

@ -1,10 +1,10 @@
import { DownOutlined, UpOutlined } from "@ant-design/icons"; import { DownOutlined, UpOutlined } from '@ant-design/icons';
import { useBoolean, useControllableValue } from "ahooks"; import { useBoolean, useControllableValue } from 'ahooks';
import { Tag } from "antd"; import { Tag } from 'antd';
import classNames from "classnames"; import classNames from 'classnames';
import type { FC } from "react"; import type { FC } from 'react';
import React from "react"; import React from 'react';
import useStyles from "./index.style"; import useStyles from './index.style';
const { CheckableTag } = Tag; const { CheckableTag } = Tag;
export interface TagSelectOptionProps { export interface TagSelectOptionProps {
value: string | number; value: string | number;
@ -25,10 +25,7 @@ const TagSelectOption: React.FC<TagSelectOptionProps> & {
</CheckableTag> </CheckableTag>
); );
TagSelectOption.isTagSelectOption = true; TagSelectOption.isTagSelectOption = true;
type TagSelectOptionElement = React.ReactElement< type TagSelectOptionElement = React.ReactElement<TagSelectOptionProps, typeof TagSelectOption>;
TagSelectOptionProps,
typeof TagSelectOption
>;
export interface TagSelectProps { export interface TagSelectProps {
onChange?: (value: (string | number)[]) => void; onChange?: (value: (string | number)[]) => void;
expandable?: boolean; expandable?: boolean;
@ -49,25 +46,15 @@ const TagSelect: FC<TagSelectProps> & {
Option: typeof TagSelectOption; Option: typeof TagSelectOption;
} = (props) => { } = (props) => {
const { styles } = useStyles(); const { styles } = useStyles();
const { const { children, hideCheckAll = false, className, style, expandable, actionsText = {} } = props;
children,
hideCheckAll = false,
className,
style,
expandable,
actionsText = {},
} = props;
const [expand, { toggle }] = useBoolean(); const [expand, { toggle }] = useBoolean();
const [value, setValue] = useControllableValue<(string | number)[]>(props); const [value, setValue] = useControllableValue<(string | number)[]>(props);
const isTagSelectOption = (node: TagSelectOptionElement) => const isTagSelectOption = (node: TagSelectOptionElement) =>
node && node &&
node.type && node.type &&
(node.type.isTagSelectOption || (node.type.isTagSelectOption || node.type.displayName === 'TagSelectOption');
node.type.displayName === "TagSelectOption");
const getAllTags = () => { const getAllTags = () => {
const childrenArray = React.Children.toArray( const childrenArray = React.Children.toArray(children) as TagSelectOptionElement[];
children
) as TagSelectOptionElement[];
const checkedTags = childrenArray const checkedTags = childrenArray
.filter((child) => isTagSelectOption(child)) .filter((child) => isTagSelectOption(child))
.map((child) => child.props.value); .map((child) => child.props.value);
@ -91,11 +78,7 @@ const TagSelect: FC<TagSelectProps> & {
setValue(checkedTags); setValue(checkedTags);
}; };
const checkedAll = getAllTags().length === value?.length; const checkedAll = getAllTags().length === value?.length;
const { const { expandText = '展开', collapseText = '收起', selectAllText = '全部' } = actionsText;
expandText = "展开",
collapseText = "收起",
selectAllText = "全部",
} = actionsText;
const cls = classNames(styles.tagSelect, className, { const cls = classNames(styles.tagSelect, className, {
[styles.hasExpandTag]: expandable, [styles.hasExpandTag]: expandable,
[styles.expanded]: expand, [styles.expanded]: expand,
@ -103,11 +86,7 @@ const TagSelect: FC<TagSelectProps> & {
return ( return (
<div className={cls} style={style}> <div className={cls} style={style}>
{hideCheckAll ? null : ( {hideCheckAll ? null : (
<CheckableTag <CheckableTag checked={checkedAll} key="tag-select-__all__" onChange={onSelectAll}>
checked={checkedAll}
key="tag-select-__all__"
onChange={onSelectAll}
>
{selectAllText} {selectAllText}
</CheckableTag> </CheckableTag>
)} )}

34
src/pages/list/search/projects/index.tsx

@ -1,14 +1,14 @@
import { Card, Col, Form, List, Row, Select, Typography } from "antd"; import { Card, Col, Form, List, Row, Select, Typography } from 'antd';
import type { FC } from "react"; import type { FC } from 'react';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import AvatarList from "./components/AvatarList"; import AvatarList from './components/AvatarList';
import StandardFormRow from "./components/StandardFormRow"; import StandardFormRow from './components/StandardFormRow';
import TagSelect from "./components/TagSelect"; import TagSelect from './components/TagSelect';
import type { ListItemDataType } from "./data.d"; import type { ListItemDataType } from './data.d';
import { queryFakeList } from "./service"; import { queryFakeList } from './service';
import useStyles from "./style.style"; import useStyles from './style.style';
import dayjs from "dayjs"; import dayjs from 'dayjs';
import relativeTime from "dayjs/plugin/relativeTime"; import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime); dayjs.extend(relativeTime);
const { Option } = Select; const { Option } = Select;
const FormItem = Form.Item; const FormItem = Form.Item;
@ -17,7 +17,7 @@ const getKey = (id: string, index: number) => `${id}-${index}`;
const Projects: FC = () => { const Projects: FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const { data, loading, run } = useRequest((values: any) => { const { data, loading, run } = useRequest((values: any) => {
console.log("form data", values); console.log('form data', values);
return queryFakeList({ return queryFakeList({
count: 8, count: 8,
}); });
@ -39,11 +39,7 @@ const Projects: FC = () => {
dataSource={list} dataSource={list}
renderItem={(item) => ( renderItem={(item) => (
<List.Item> <List.Item>
<Card <Card className={styles.card} hoverable cover={<img alt={item.title} src={item.cover} />}>
className={styles.card}
hoverable
cover={<img alt={item.title} src={item.cover} />}
>
<Card.Meta <Card.Meta
title={<a>{item.title}</a>} title={<a>{item.title}</a>}
description={ description={
@ -129,7 +125,7 @@ const Projects: FC = () => {
placeholder="不限" placeholder="不限"
style={{ style={{
maxWidth: 200, maxWidth: 200,
width: "100%", width: '100%',
}} }}
> >
<Option value="lisa"></Option> <Option value="lisa"></Option>
@ -142,7 +138,7 @@ const Projects: FC = () => {
placeholder="不限" placeholder="不限"
style={{ style={{
maxWidth: 200, maxWidth: 200,
width: "100%", width: '100%',
}} }}
> >
<Option value="good"></Option> <Option value="good"></Option>

48
src/pages/list/search/projects/style.style.ts

@ -1,47 +1,47 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
coverCardList: {}, coverCardList: {},
card: { card: {
"&:hover": {}, '&:hover': {},
}, },
".ant-card-meta-title": { '.ant-card-meta-title': {
marginBottom: "4px", marginBottom: '4px',
"& > a": { '& > a': {
display: "inline-block", display: 'inline-block',
maxWidth: "100%", maxWidth: '100%',
color: token.colorTextHeading, color: token.colorTextHeading,
}, },
}, },
".ant-card-meta-description": { '.ant-card-meta-description': {
height: "44px", height: '44px',
overflow: "hidden", overflow: 'hidden',
lineHeight: "22px", lineHeight: '22px',
}, },
".ant-card-meta-title > a": { '.ant-card-meta-title > a': {
color: token.colorPrimary, color: token.colorPrimary,
}, },
cardItemContent: { cardItemContent: {
display: "flex", display: 'flex',
height: "20px", height: '20px',
marginTop: "16px", marginTop: '16px',
marginBottom: "-4px", marginBottom: '-4px',
lineHeight: "20px", lineHeight: '20px',
"& > span": { '& > span': {
flex: "1", flex: '1',
color: token.colorTextSecondary, color: token.colorTextSecondary,
fontSize: "12px", fontSize: '12px',
}, },
}, },
avatarList: { avatarList: {
flex: "0 1 auto", flex: '0 1 auto',
}, },
cardList: { cardList: {
marginTop: "24px", marginTop: '24px',
}, },
".ant-list .ant-list-item-content-single": { '.ant-list .ant-list-item-content-single': {
maxWidth: "100%", maxWidth: '100%',
}, },
}; };
}); });

2
src/pages/list/search/projects/utils/utils.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(() => { const useStyles = createStyles(() => {
return {}; return {};

34
src/pages/profile/advanced/style.style.ts

@ -1,38 +1,38 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
main: {}, main: {},
".ant-descriptions-row > td": { '.ant-descriptions-row > td': {
paddingBottom: "8px", paddingBottom: '8px',
}, },
".ant-page-header-heading-extra": { '.ant-page-header-heading-extra': {
flexDirection: "column", flexDirection: 'column',
}, },
headerList: { headerList: {
marginBottom: "4px", marginBottom: '4px',
}, },
stepDescription: { stepDescription: {
[`@media screen and (max-width: ${token.screenSM}px)`]: { left: "8px" }, [`@media screen and (max-width: ${token.screenSM}px)`]: { left: '8px' },
}, },
"> div": { '> div': {
marginTop: "8px", marginTop: '8px',
marginBottom: "4px", marginBottom: '4px',
}, },
pageHeader: { pageHeader: {
[`@media screen and (max-width: ${token.screenSM}px)`]: {}, [`@media screen and (max-width: ${token.screenSM}px)`]: {},
}, },
".ant-page-header-heading-extra > * + *": { '.ant-page-header-heading-extra > * + *': {
marginLeft: "8px", marginLeft: '8px',
}, },
moreInfo: { moreInfo: {
display: "flex", display: 'flex',
justifyContent: "space-between", justifyContent: 'space-between',
width: "200px", width: '200px',
}, },
".ant-pro-page-header-wrap-row": { '.ant-pro-page-header-wrap-row': {
[`@media screen and (max-width: ${token.screenSM}px)`]: { [`@media screen and (max-width: ${token.screenSM}px)`]: {
flexDirection: "column", flexDirection: 'column',
}, },
}, },
}; };

100
src/pages/profile/basic/index.tsx

@ -1,44 +1,44 @@
import { PageContainer } from "@ant-design/pro-components"; import { PageContainer } from '@ant-design/pro-components';
import type { ProColumns } from "@ant-design/pro-components"; import type { ProColumns } from '@ant-design/pro-components';
import { ProTable } from "@ant-design/pro-components"; import { ProTable } from '@ant-design/pro-components';
import { Badge, Card, Descriptions, Divider } from "antd"; import { Badge, Card, Descriptions, Divider } from 'antd';
import type { FC } from "react"; import type { FC } from 'react';
import React from "react"; import React from 'react';
import { useRequest } from "@umijs/max"; import { useRequest } from '@umijs/max';
import type { BasicGood, BasicProgress } from "./data.d"; import type { BasicGood, BasicProgress } from './data.d';
import { queryBasicProfile } from "./service"; import { queryBasicProfile } from './service';
import useStyles from "./style.style"; import useStyles from './style.style';
const progressColumns: ProColumns<BasicProgress>[] = [ const progressColumns: ProColumns<BasicProgress>[] = [
{ {
title: "时间", title: '时间',
dataIndex: "time", dataIndex: 'time',
key: "time", key: 'time',
}, },
{ {
title: "当前进度", title: '当前进度',
dataIndex: "rate", dataIndex: 'rate',
key: "rate", key: 'rate',
}, },
{ {
title: "状态", title: '状态',
dataIndex: "status", dataIndex: 'status',
key: "status", key: 'status',
render: (text: React.ReactNode) => { render: (text: React.ReactNode) => {
if (text === "success") { if (text === 'success') {
return <Badge status="success" text="成功" />; return <Badge status="success" text="成功" />;
} }
return <Badge status="processing" text="进行中" />; return <Badge status="processing" text="进行中" />;
}, },
}, },
{ {
title: "操作员ID", title: '操作员ID',
dataIndex: "operator", dataIndex: 'operator',
key: "operator", key: 'operator',
}, },
{ {
title: "耗时", title: '耗时',
dataIndex: "cost", dataIndex: 'cost',
key: "cost", key: 'cost',
}, },
]; ];
const Basic: FC = () => { const Basic: FC = () => {
@ -59,7 +59,7 @@ const Basic: FC = () => {
amount += Number(item.amount); amount += Number(item.amount);
}); });
goodsData = basicGoods.concat({ goodsData = basicGoods.concat({
id: "总计", id: '总计',
num, num,
amount, amount,
}); });
@ -81,9 +81,9 @@ const Basic: FC = () => {
}; };
const goodsColumns: ProColumns<BasicGood>[] = [ const goodsColumns: ProColumns<BasicGood>[] = [
{ {
title: "商品编号", title: '商品编号',
dataIndex: "id", dataIndex: 'id',
key: "id", key: 'id',
render: (text: React.ReactNode, _: any, index: number) => { render: (text: React.ReactNode, _: any, index: number) => {
if (index < basicGoods.length) { if (index < basicGoods.length) {
return <span>{text}</span>; return <span>{text}</span>;
@ -105,29 +105,29 @@ const Basic: FC = () => {
}, },
}, },
{ {
title: "商品名称", title: '商品名称',
dataIndex: "name", dataIndex: 'name',
key: "name", key: 'name',
render: renderContent, render: renderContent,
}, },
{ {
title: "商品条码", title: '商品条码',
dataIndex: "barcode", dataIndex: 'barcode',
key: "barcode", key: 'barcode',
render: renderContent, render: renderContent,
}, },
{ {
title: "单价", title: '单价',
dataIndex: "price", dataIndex: 'price',
key: "price", key: 'price',
align: "right" as "left" | "right" | "center", align: 'right' as 'left' | 'right' | 'center',
render: renderContent, render: renderContent,
}, },
{ {
title: "数量(件)", title: '数量(件)',
dataIndex: "num", dataIndex: 'num',
key: "num", key: 'num',
align: "right" as "left" | "right" | "center", align: 'right' as 'left' | 'right' | 'center',
render: (text: React.ReactNode, _: any, index: number) => { render: (text: React.ReactNode, _: any, index: number) => {
if (index < basicGoods.length) { if (index < basicGoods.length) {
return text; return text;
@ -144,10 +144,10 @@ const Basic: FC = () => {
}, },
}, },
{ {
title: "金额", title: '金额',
dataIndex: "amount", dataIndex: 'amount',
key: "amount", key: 'amount',
align: "right" as "left" | "right" | "center", align: 'right' as 'left' | 'right' | 'center',
render: (text: React.ReactNode, _: any, index: number) => { render: (text: React.ReactNode, _: any, index: number) => {
if (index < basicGoods.length) { if (index < basicGoods.length) {
return text; return text;
@ -192,9 +192,7 @@ const Basic: FC = () => {
<Descriptions.Item label="用户姓名"></Descriptions.Item> <Descriptions.Item label="用户姓名"></Descriptions.Item>
<Descriptions.Item label="联系电话">18100000000</Descriptions.Item> <Descriptions.Item label="联系电话">18100000000</Descriptions.Item>
<Descriptions.Item label="常用快递"></Descriptions.Item> <Descriptions.Item label="常用快递"></Descriptions.Item>
<Descriptions.Item label="取货地址"> <Descriptions.Item label="取货地址">西18</Descriptions.Item>
西18
</Descriptions.Item>
<Descriptions.Item label="备注"></Descriptions.Item> <Descriptions.Item label="备注"></Descriptions.Item>
</Descriptions> </Descriptions>
<Divider <Divider

8
src/pages/profile/basic/style.style.ts

@ -1,12 +1,12 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
title: { title: {
marginBottom: "16px", marginBottom: '16px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
fontSize: "16px", fontSize: '16px',
}, },
}; };
}); });

8
src/pages/result/fail/index.style.ts

@ -1,4 +1,4 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
@ -6,10 +6,10 @@ const useStyles = createStyles(({ token }) => {
color: token.colorBgTextActive, color: token.colorBgTextActive,
}, },
title: { title: {
marginBottom: "16px", marginBottom: '16px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500", fontWeight: '500',
fontSize: "16px", fontSize: '16px',
}, },
}; };
}); });

16
src/pages/result/success/index.style.ts

@ -1,18 +1,18 @@
import { createStyles } from "antd-style"; import { createStyles } from 'antd-style';
const useStyles = createStyles(({ token }) => { const useStyles = createStyles(({ token }) => {
return { return {
title: { title: {
position: "relative", position: 'relative',
color: token.colorText, color: token.colorText,
fontSize: "12px", fontSize: '12px',
textAlign: "center", textAlign: 'center',
}, },
"head-title": { 'head-title': {
marginBottom: "20px", marginBottom: '20px',
color: token.colorTextHeading, color: token.colorTextHeading,
fontWeight: "500px", fontWeight: '500px',
fontSize: "16px", fontSize: '16px',
}, },
}; };
}); });

10
src/pages/user/register-result/index.tsx

@ -1,7 +1,7 @@
import { Button, Result } from "antd"; import { Button, Result } from 'antd';
import { Link, useSearchParams } from "@umijs/max"; import { Link, useSearchParams } from '@umijs/max';
import React from "react"; import React from 'react';
import useStyles from "./style.style"; import useStyles from './style.style';
const actions = ( const actions = (
<div className={styles.actions}> <div className={styles.actions}>
<a href=""> <a href="">
@ -17,7 +17,7 @@ const actions = (
const RegisterResult: React.FC<Record<string, unknown>> = () => { const RegisterResult: React.FC<Record<string, unknown>> = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const [params] = useSearchParams(); const [params] = useSearchParams();
const email = params?.get("account") || "AntDesign@example.com"; const email = params?.get('account') || 'AntDesign@example.com';
return ( return (
<Result <Result
className={styles.registerResult} className={styles.registerResult}

100
src/pages/user/register/index.tsx

@ -1,21 +1,11 @@
import type { FC } from "react"; import type { FC } from 'react';
import { useState, useEffect } from "react"; import { useState, useEffect } from 'react';
import { import { Form, Button, Col, Input, Popover, Progress, Row, Select, message } from 'antd';
Form, import type { Store } from 'antd/es/form/interface';
Button, import { Link, useRequest, history } from '@umijs/max';
Col, import type { StateType } from './service';
Input, import { fakeRegister } from './service';
Popover, import useStyles from './style.style';
Progress,
Row,
Select,
message,
} from "antd";
import type { Store } from "antd/es/form/interface";
import { Link, useRequest, history } from "@umijs/max";
import type { StateType } from "./service";
import { fakeRegister } from "./service";
import useStyles from "./style.style";
const FormItem = Form.Item; const FormItem = Form.Item;
const { Option } = Select; const { Option } = Select;
const InputGroup = Input.Group; const InputGroup = Input.Group;
@ -37,19 +27,19 @@ const passwordStatusMap = {
), ),
}; };
const passwordProgressMap: { const passwordProgressMap: {
ok: "success"; ok: 'success';
pass: "normal"; pass: 'normal';
poor: "exception"; poor: 'exception';
} = { } = {
ok: "success", ok: 'success',
pass: "normal", pass: 'normal',
poor: "exception", poor: 'exception',
}; };
const Register: FC = () => { const Register: FC = () => {
const { styles } = useStyles(); const { styles } = useStyles();
const [count, setCount]: [number, any] = useState(0); const [count, setCount]: [number, any] = useState(0);
const [visible, setVisible]: [boolean, any] = useState(false); const [visible, setVisible]: [boolean, any] = useState(false);
const [prefix, setPrefix]: [string, any] = useState("86"); const [prefix, setPrefix]: [string, any] = useState('86');
const [popover, setPopover]: [boolean, any] = useState(false); const [popover, setPopover]: [boolean, any] = useState(false);
const confirmDirty = false; const confirmDirty = false;
let interval: number | undefined; let interval: number | undefined;
@ -58,7 +48,7 @@ const Register: FC = () => {
() => () => { () => () => {
clearInterval(interval); clearInterval(interval);
}, },
[interval] [interval],
); );
const onGetCaptcha = () => { const onGetCaptcha = () => {
let counts = 59; let counts = 59;
@ -72,24 +62,24 @@ const Register: FC = () => {
}, 1000); }, 1000);
}; };
const getPasswordStatus = () => { const getPasswordStatus = () => {
const value = form.getFieldValue("password"); const value = form.getFieldValue('password');
if (value && value.length > 9) { if (value && value.length > 9) {
return "ok"; return 'ok';
} }
if (value && value.length > 5) { if (value && value.length > 5) {
return "pass"; return 'pass';
} }
return "poor"; return 'poor';
}; };
const { loading: submitting, run: register } = useRequest<{ const { loading: submitting, run: register } = useRequest<{
data: StateType; data: StateType;
}>(fakeRegister, { }>(fakeRegister, {
manual: true, manual: true,
onSuccess: (data, params) => { onSuccess: (data, params) => {
if (data.status === "ok") { if (data.status === 'ok') {
message.success("注册成功!"); message.success('注册成功!');
history.push({ history.push({
pathname: "/user/register-result?account=" + params.email, pathname: '/user/register-result?account=' + params.email,
}); });
} }
}, },
@ -99,8 +89,8 @@ const Register: FC = () => {
}; };
const checkConfirm = (_: any, value: string) => { const checkConfirm = (_: any, value: string) => {
const promise = Promise; const promise = Promise;
if (value && value !== form.getFieldValue("password")) { if (value && value !== form.getFieldValue('password')) {
return promise.reject("两次输入的密码不匹配!"); return promise.reject('两次输入的密码不匹配!');
} }
return promise.resolve(); return promise.resolve();
}; };
@ -109,7 +99,7 @@ const Register: FC = () => {
// 没有值的情况 // 没有值的情况
if (!value) { if (!value) {
setVisible(!!value); setVisible(!!value);
return promise.reject("请输入密码!"); return promise.reject('请输入密码!');
} }
// 有值的情况 // 有值的情况
if (!visible) { if (!visible) {
@ -117,10 +107,10 @@ const Register: FC = () => {
} }
setPopover(!popover); setPopover(!popover);
if (value.length < 6) { if (value.length < 6) {
return promise.reject(""); return promise.reject('');
} }
if (value && confirmDirty) { if (value && confirmDirty) {
form.validateFields(["confirm"]); form.validateFields(['confirm']);
} }
return promise.resolve(); return promise.resolve();
}; };
@ -128,7 +118,7 @@ const Register: FC = () => {
setPrefix(value); setPrefix(value);
}; };
const renderPasswordProgress = () => { const renderPasswordProgress = () => {
const value = form.getFieldValue("password"); const value = form.getFieldValue('password');
const passwordStatus = getPasswordStatus(); const passwordStatus = getPasswordStatus();
return value && value.length ? ( return value && value.length ? (
<div className={styles[`progress-${passwordStatus}`]}> <div className={styles[`progress-${passwordStatus}`]}>
@ -151,11 +141,11 @@ const Register: FC = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入邮箱地址!", message: '请输入邮箱地址!',
}, },
{ {
type: "email", type: 'email',
message: "邮箱地址格式错误!", message: '邮箱地址格式错误!',
}, },
]} ]}
> >
@ -172,7 +162,7 @@ const Register: FC = () => {
visible && ( visible && (
<div <div
style={{ style={{
padding: "4px 0", padding: '4px 0',
}} }}
> >
{passwordStatusMap[getPasswordStatus()]} {passwordStatusMap[getPasswordStatus()]}
@ -196,8 +186,8 @@ const Register: FC = () => {
<FormItem <FormItem
name="password" name="password"
className={ className={
form.getFieldValue("password") && form.getFieldValue('password') &&
form.getFieldValue("password").length > 0 && form.getFieldValue('password').length > 0 &&
styles.password styles.password
} }
rules={[ rules={[
@ -206,11 +196,7 @@ const Register: FC = () => {
}, },
]} ]}
> >
<Input <Input size="large" type="password" placeholder="至少6位密码,区分大小写" />
size="large"
type="password"
placeholder="至少6位密码,区分大小写"
/>
</FormItem> </FormItem>
</Popover> </Popover>
<FormItem <FormItem
@ -218,7 +204,7 @@ const Register: FC = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "确认密码", message: '确认密码',
}, },
{ {
validator: checkConfirm, validator: checkConfirm,
@ -233,7 +219,7 @@ const Register: FC = () => {
value={prefix} value={prefix}
onChange={changePrefix} onChange={changePrefix}
style={{ style={{
width: "20%", width: '20%',
}} }}
> >
<Option value="86">+86</Option> <Option value="86">+86</Option>
@ -241,17 +227,17 @@ const Register: FC = () => {
</Select> </Select>
<FormItem <FormItem
style={{ style={{
width: "80%", width: '80%',
}} }}
name="mobile" name="mobile"
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入手机号!", message: '请输入手机号!',
}, },
{ {
pattern: /^\d{11}$/, pattern: /^\d{11}$/,
message: "手机号格式错误!", message: '手机号格式错误!',
}, },
]} ]}
> >
@ -265,7 +251,7 @@ const Register: FC = () => {
rules={[ rules={[
{ {
required: true, required: true,
message: "请输入验证码!", message: '请输入验证码!',
}, },
]} ]}
> >
@ -279,7 +265,7 @@ const Register: FC = () => {
className={styles.getCaptcha} className={styles.getCaptcha}
onClick={onGetCaptcha} onClick={onGetCaptcha}
> >
{count ? `${count} s` : "获取验证码"} {count ? `${count} s` : '获取验证码'}
</Button> </Button>
</Col> </Col>
</Row> </Row>

Loading…
Cancel
Save