Skip to content

Commit 7624645

Browse files
committed
refactor: statistic
1 parent fc6c358 commit 7624645

File tree

5 files changed

+129
-110
lines changed

5 files changed

+129
-110
lines changed

components/statistic/Countdown.tsx

+59-56
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { defineComponent } from 'vue';
1+
import { defineComponent, onBeforeUnmount, onMounted, onUpdated, ref } from 'vue';
22
import moment from 'moment';
33
import interopDefault from '../_util/interopDefault';
44
import initDefaultProps from '../_util/props-util/initDefaultProps';
55
import Statistic, { StatisticProps } from './Statistic';
6-
import { formatCountdown, countdownValueType, FormatConfig } from './utils';
6+
import { formatCountdown as formatCD, countdownValueType, FormatConfig } from './utils';
77

88
const REFRESH_INTERVAL = 1000 / 30;
99

@@ -16,73 +16,76 @@ export default defineComponent({
1616
props: initDefaultProps(StatisticProps, {
1717
format: 'HH:mm:ss',
1818
}),
19-
setup() {
20-
return {
21-
countdownId: undefined,
22-
} as { countdownId: number };
23-
},
24-
mounted() {
25-
this.syncTimer();
26-
},
27-
28-
updated() {
29-
this.syncTimer();
30-
},
31-
32-
beforeUnmount() {
33-
this.stopTimer();
34-
},
35-
36-
methods: {
37-
syncTimer() {
38-
const { value } = this.$props;
19+
emits: ['finish', 'change'],
20+
setup(props, { emit }) {
21+
const countdownId = ref<number>();
22+
const statistic = ref();
23+
const syncTimer = () => {
24+
const { value } = props;
3925
const timestamp = getTime(value);
4026
if (timestamp >= Date.now()) {
41-
this.startTimer();
27+
startTimer();
4228
} else {
43-
this.stopTimer();
29+
stopTimer();
4430
}
45-
},
31+
};
4632

47-
startTimer() {
48-
if (this.countdownId) return;
49-
this.countdownId = window.setInterval(() => {
50-
(this.$refs.statistic as any).$forceUpdate();
51-
this.syncTimer();
33+
const startTimer = () => {
34+
if (countdownId.value) return;
35+
const timestamp = getTime(props.value);
36+
countdownId.value = window.setInterval(() => {
37+
statistic.value.$forceUpdate();
38+
if (timestamp > Date.now()) {
39+
emit('change', timestamp - Date.now());
40+
}
5241
}, REFRESH_INTERVAL);
53-
},
42+
};
5443

55-
stopTimer() {
56-
const { value } = this.$props;
57-
if (this.countdownId) {
58-
clearInterval(this.countdownId);
59-
this.countdownId = undefined;
44+
const stopTimer = () => {
45+
const { value } = props;
46+
if (countdownId) {
47+
clearInterval(countdownId.value);
48+
countdownId.value = undefined;
6049

6150
const timestamp = getTime(value);
6251
if (timestamp < Date.now()) {
63-
this.$emit('finish');
52+
emit('finish');
6453
}
6554
}
66-
},
55+
};
6756

68-
formatCountdown({ value, config }: { value: countdownValueType; config: FormatConfig }) {
69-
const { format } = this.$props;
70-
return formatCountdown(value, { ...config, format });
71-
},
72-
73-
valueRenderHtml: node => node,
74-
},
57+
const formatCountdown = ({
58+
value,
59+
config,
60+
}: {
61+
value: countdownValueType;
62+
config: FormatConfig;
63+
}) => {
64+
const { format } = props;
65+
return formatCD(value, { ...config, format });
66+
};
7567

76-
render() {
77-
return (
78-
<Statistic
79-
ref="statistic"
80-
{...{
81-
...this.$props,
82-
valueRender: this.valueRenderHtml,
83-
formatter: this.formatCountdown,
84-
}}
85-
/>
86-
);
68+
const valueRenderHtml = (node: any) => node;
69+
onMounted(() => {
70+
syncTimer();
71+
});
72+
onUpdated(() => {
73+
syncTimer();
74+
});
75+
onBeforeUnmount(() => {
76+
stopTimer();
77+
});
78+
return () => {
79+
return (
80+
<Statistic
81+
ref={statistic}
82+
{...{
83+
...props,
84+
valueRender: valueRenderHtml,
85+
formatter: formatCountdown,
86+
}}
87+
/>
88+
);
89+
};
8790
},
8891
});

components/statistic/Statistic.tsx

+38-41
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { defineComponent, inject, PropType } from 'vue';
1+
import { defineComponent, PropType } from 'vue';
22
import PropTypes from '../_util/vue-types';
3-
import { getComponent } from '../_util/props-util';
43
import initDefaultProps from '../_util/props-util/initDefaultProps';
5-
import { defaultConfigProvider } from '../config-provider';
64
import StatisticNumber from './Number';
75
import { countdownValueType } from './utils';
6+
import Skeleton from '../skeleton/Skeleton';
7+
import useConfigInject from '../_util/hooks/useConfigInject';
88

99
export const StatisticProps = {
1010
prefixCls: PropTypes.string,
@@ -22,52 +22,49 @@ export const StatisticProps = {
2222
suffix: PropTypes.VNodeChild,
2323
title: PropTypes.VNodeChild,
2424
onFinish: PropTypes.func,
25+
loading: PropTypes.looseBool,
2526
};
2627

2728
export default defineComponent({
2829
name: 'AStatistic',
2930
props: initDefaultProps(StatisticProps, {
3031
decimalSeparator: '.',
3132
groupSeparator: ',',
33+
loading: false,
3234
}),
33-
34-
setup() {
35-
return {
36-
configProvider: inject('configProvider', defaultConfigProvider),
37-
};
38-
},
39-
40-
render() {
41-
const { prefixCls: customizePrefixCls, value = 0, valueStyle, valueRender } = this.$props;
42-
const { getPrefixCls } = this.configProvider;
43-
const prefixCls = getPrefixCls('statistic', customizePrefixCls);
44-
45-
const title = getComponent(this, 'title');
46-
const prefix = getComponent(this, 'prefix');
47-
const suffix = getComponent(this, 'suffix');
48-
const formatter = getComponent(this, 'formatter', {}, false);
49-
const props = {
50-
...this.$props,
51-
prefixCls,
52-
value,
53-
formatter,
54-
};
55-
// data-for-update just for update component
56-
// https://github.com/vueComponent/ant-design-vue/pull/3170
57-
let valueNode = <StatisticNumber data-for-update={Date.now()} {...props} />;
58-
if (valueRender) {
59-
valueNode = valueRender(valueNode);
60-
}
61-
62-
return (
63-
<div class={prefixCls}>
64-
{title && <div class={`${prefixCls}-title`}>{title}</div>}
65-
<div style={valueStyle} class={`${prefixCls}-content`}>
66-
{prefix && <span class={`${prefixCls}-content-prefix`}>{prefix}</span>}
67-
{valueNode}
68-
{suffix && <span class={`${prefixCls}-content-suffix`}>{suffix}</span>}
35+
slots: ['title', 'prefix', 'suffix', 'formatter'],
36+
setup(props, { slots }) {
37+
const { prefixCls, direction } = useConfigInject('statistic', props);
38+
return () => {
39+
const { value = 0, valueStyle, valueRender } = props;
40+
const pre = prefixCls.value;
41+
const title = props.title ?? slots.title?.();
42+
const prefix = props.prefix ?? slots.prefix?.();
43+
const suffix = props.suffix ?? slots.suffix?.();
44+
const formatter = props.formatter ?? slots.formatter;
45+
// data-for-update just for update component
46+
// https://github.com/vueComponent/ant-design-vue/pull/3170
47+
let valueNode = (
48+
<StatisticNumber
49+
data-for-update={Date.now()}
50+
{...{ ...props, prefixCls: pre, value, formatter }}
51+
/>
52+
);
53+
if (valueRender) {
54+
valueNode = valueRender(valueNode);
55+
}
56+
return (
57+
<div class={[pre, { [`${pre}-rtl`]: direction.value === 'rtl' }]}>
58+
{title && <div class={`${pre}-title`}>{title}</div>}
59+
<Skeleton paragraph={false} loading={props.loading}>
60+
<div style={valueStyle} class={`${pre}-content`}>
61+
{prefix && <span class={`${pre}-content-prefix`}>{prefix}</span>}
62+
{valueNode}
63+
{suffix && <span class={`${pre}-content-suffix`}>{suffix}</span>}
64+
</div>
65+
</Skeleton>
6966
</div>
70-
</div>
71-
);
67+
);
68+
};
7269
},
7370
});

components/statistic/style/index.less

+5-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
.reset-component();
88

99
&-title {
10-
margin-bottom: 4px;
10+
margin-bottom: @margin-xss;
1111
color: @text-color-secondary;
1212
font-size: @statistic-title-font-size;
1313
}
@@ -18,9 +18,8 @@
1818
font-family: @statistic-font-family;
1919

2020
&-value {
21-
&-decimal {
22-
font-size: @statistic-unit-font-size;
23-
}
21+
display: inline-block;
22+
direction: ltr;
2423
}
2524

2625
&-prefix,
@@ -34,7 +33,8 @@
3433

3534
&-suffix {
3635
margin-left: 4px;
37-
font-size: @statistic-unit-font-size;
3836
}
3937
}
4038
}
39+
40+
@import './rtl';

components/statistic/style/rtl.less

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.@{statistic-prefix-cls} {
2+
&-rtl {
3+
direction: rtl;
4+
}
5+
6+
&-content {
7+
&-prefix {
8+
.@{statistic-prefix-cls}-rtl & {
9+
margin-right: 0;
10+
margin-left: 4px;
11+
}
12+
}
13+
14+
&-suffix {
15+
.@{statistic-prefix-cls}-rtl & {
16+
margin-right: 4px;
17+
margin-left: 0;
18+
}
19+
}
20+
}
21+
}

components/statistic/utils.ts

+6-8
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
import { VNodeTypes } from 'vue';
2-
import moment from 'moment';
32
import padStart from 'lodash-es/padStart';
43

5-
import interopDefault from '../_util/interopDefault';
6-
74
export type valueType = number | string;
85
export type countdownValueType = valueType | string;
96

@@ -39,15 +36,15 @@ const timeUnits: [string, number][] = [
3936
export function formatTimeStr(duration: number, format: string) {
4037
let leftDuration: number = duration;
4138

42-
const escapeRegex = /\[[^\]]*\]/g;
43-
const keepList = (format.match(escapeRegex) || []).map(str => str.slice(1, -1));
39+
const escapeRegex = /\[[^\]]*]/g;
40+
const keepList: string[] = (format.match(escapeRegex) || []).map(str => str.slice(1, -1));
4441
const templateText = format.replace(escapeRegex, '[]');
4542

4643
const replacedText = timeUnits.reduce((current, [name, unit]) => {
4744
if (current.indexOf(name) !== -1) {
4845
const value = Math.floor(leftDuration / unit);
4946
leftDuration -= value * unit;
50-
return current.replace(new RegExp(`${name}+`, 'g'), match => {
47+
return current.replace(new RegExp(`${name}+`, 'g'), (match: string) => {
5148
const len = match.length;
5249
return padStart(value.toString(), len, '0');
5350
});
@@ -65,8 +62,9 @@ export function formatTimeStr(duration: number, format: string) {
6562

6663
export function formatCountdown(value: countdownValueType, config: CountdownFormatConfig) {
6764
const { format = '' } = config;
68-
const target = interopDefault(moment)(value).valueOf();
69-
const current = interopDefault(moment)().valueOf();
65+
const target = new Date(value).getTime();
66+
const current = Date.now();
7067
const diff = Math.max(target - current, 0);
68+
7169
return formatTimeStr(diff, format);
7270
}

0 commit comments

Comments
 (0)