feat: add statistic
parent
00f104757c
commit
14d4b7745d
|
@ -95,6 +95,8 @@ import { default as Slider } from './slider';
|
||||||
|
|
||||||
import { default as Spin } from './spin';
|
import { default as Spin } from './spin';
|
||||||
|
|
||||||
|
import { default as Statistic } from './statistic';
|
||||||
|
|
||||||
import { default as Steps } from './steps';
|
import { default as Steps } from './steps';
|
||||||
|
|
||||||
import { default as Switch } from './switch';
|
import { default as Switch } from './switch';
|
||||||
|
@ -172,6 +174,7 @@ const components = [
|
||||||
Select,
|
Select,
|
||||||
Slider,
|
Slider,
|
||||||
Spin,
|
Spin,
|
||||||
|
Statistic,
|
||||||
Steps,
|
Steps,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
|
@ -254,6 +257,7 @@ export {
|
||||||
Select,
|
Select,
|
||||||
Slider,
|
Slider,
|
||||||
Spin,
|
Spin,
|
||||||
|
Statistic,
|
||||||
Steps,
|
Steps,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import interopDefault from '../_util/interopDefault';
|
||||||
|
import { cloneElement } from '../_util/vnode';
|
||||||
|
import { initDefaultProps } from '../_util/props-util';
|
||||||
|
|
||||||
|
import Statistic, { StatisticProps } from './Statistic';
|
||||||
|
import { formatCountdown } from './utils';
|
||||||
|
|
||||||
|
const REFRESH_INTERVAL = 1000 / 30;
|
||||||
|
|
||||||
|
function getTime(value) {
|
||||||
|
return interopDefault(moment)(value).valueOf();
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AStatisticCountdown',
|
||||||
|
props: initDefaultProps(StatisticProps, {
|
||||||
|
prefixCls: 'ant-statistic',
|
||||||
|
format: 'HH:mm:ss',
|
||||||
|
}),
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
uniKey: 0,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
countdownId: undefined,
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncTimer();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
updated() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncTimer();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.stopTimer();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
syncTimer() {
|
||||||
|
const { value } = this.$props;
|
||||||
|
const timestamp = getTime(value);
|
||||||
|
if (timestamp >= Date.now()) {
|
||||||
|
this.startTimer();
|
||||||
|
} else {
|
||||||
|
this.stopTimer();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
startTimer() {
|
||||||
|
if (this.countdownId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.countdownId = window.setInterval(() => {
|
||||||
|
this.uniKey++;
|
||||||
|
}, REFRESH_INTERVAL);
|
||||||
|
},
|
||||||
|
|
||||||
|
stopTimer() {
|
||||||
|
const { value } = this.$props;
|
||||||
|
if (this.countdownId) {
|
||||||
|
clearInterval(this.countdownId);
|
||||||
|
this.countdownId = undefined;
|
||||||
|
|
||||||
|
const timestamp = getTime(value);
|
||||||
|
if (timestamp < Date.now()) {
|
||||||
|
this.$emit('finish');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
formatCountdown(value, config) {
|
||||||
|
const { format } = this.$props;
|
||||||
|
return formatCountdown(value, { ...config, format });
|
||||||
|
},
|
||||||
|
|
||||||
|
// Countdown do not need display the timestamp
|
||||||
|
valueRenderHtml: node =>
|
||||||
|
cloneElement(node, {
|
||||||
|
props: {
|
||||||
|
title: undefined,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<Statistic
|
||||||
|
key={this.uniKey}
|
||||||
|
{...{
|
||||||
|
props: {
|
||||||
|
...this.$props,
|
||||||
|
valueRender: this.valueRenderHtml,
|
||||||
|
formatter: this.formatCountdown,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,58 @@
|
||||||
|
import padEnd from 'lodash/padEnd';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AStatisticNumber',
|
||||||
|
functional: true,
|
||||||
|
render(h, context) {
|
||||||
|
const {
|
||||||
|
value,
|
||||||
|
formatter,
|
||||||
|
precision,
|
||||||
|
decimalSeparator,
|
||||||
|
groupSeparator = '',
|
||||||
|
prefixCls,
|
||||||
|
} = context.props;
|
||||||
|
|
||||||
|
let valueNode;
|
||||||
|
|
||||||
|
if (typeof formatter === 'function') {
|
||||||
|
// Customize formatter
|
||||||
|
valueNode = formatter(value);
|
||||||
|
} else {
|
||||||
|
// Internal formatter
|
||||||
|
const val = String(value);
|
||||||
|
const cells = val.match(/^(-?)(\d*)(\.(\d+))?$/);
|
||||||
|
// Process if illegal number
|
||||||
|
if (!cells) {
|
||||||
|
valueNode = val;
|
||||||
|
} else {
|
||||||
|
const negative = cells[1];
|
||||||
|
let int = cells[2] || '0';
|
||||||
|
let decimal = cells[4] || '';
|
||||||
|
|
||||||
|
int = int.replace(/\B(?=(\d{3})+(?!\d))/g, groupSeparator);
|
||||||
|
if (typeof precision === 'number') {
|
||||||
|
decimal = padEnd(decimal, precision, '0').slice(0, precision);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (decimal) {
|
||||||
|
decimal = `${decimalSeparator}${decimal}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
valueNode = [
|
||||||
|
<span key="int" class={`${prefixCls}-content-value-int`}>
|
||||||
|
{negative}
|
||||||
|
{int}
|
||||||
|
</span>,
|
||||||
|
decimal && (
|
||||||
|
<span key="decimal" class={`${prefixCls}-content-value-decimal`}>
|
||||||
|
{decimal}
|
||||||
|
</span>
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return <span class={`${prefixCls}-content-value`}>{valueNode}</span>;
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,53 @@
|
||||||
|
import PropTypes from '../_util/vue-types';
|
||||||
|
import { getComponentFromProp, initDefaultProps } from '../_util/props-util';
|
||||||
|
|
||||||
|
import StatisticNumber from './Number';
|
||||||
|
|
||||||
|
export const StatisticProps = {
|
||||||
|
prefixCls: PropTypes.string,
|
||||||
|
decimalSeparator: PropTypes.string,
|
||||||
|
groupSeparator: PropTypes.string,
|
||||||
|
format: PropTypes.string,
|
||||||
|
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||||
|
valueStyle: PropTypes.any,
|
||||||
|
valueRender: PropTypes.any,
|
||||||
|
formatter: PropTypes.any,
|
||||||
|
precision: PropTypes.number,
|
||||||
|
prefix: PropTypes.any,
|
||||||
|
suffix: PropTypes.any,
|
||||||
|
title: PropTypes.any,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AStatistic',
|
||||||
|
props: initDefaultProps(StatisticProps, {
|
||||||
|
prefixCls: 'ant-statistic',
|
||||||
|
decimalSeparator: '.',
|
||||||
|
groupSeparator: ',',
|
||||||
|
}),
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { prefixCls, value = 0, valueStyle, valueRender } = this.$props;
|
||||||
|
const title = getComponentFromProp(this, 'title');
|
||||||
|
let prefix = getComponentFromProp(this, 'prefix');
|
||||||
|
let suffix = getComponentFromProp(this, 'suffix');
|
||||||
|
const formatter = getComponentFromProp(this, 'formatter');
|
||||||
|
let valueNode = (
|
||||||
|
<StatisticNumber {...{ props: this.$props }} value={value} formatter={formatter} />
|
||||||
|
);
|
||||||
|
if (valueRender) {
|
||||||
|
valueNode = valueRender(valueNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={prefixCls}>
|
||||||
|
{title && <div class={`${prefixCls}-title`}>{title}</div>}
|
||||||
|
<div style={valueStyle} class={`${prefixCls}-content`}>
|
||||||
|
{prefix && <span class={`${prefixCls}-content-prefix`}>{prefix}</span>}
|
||||||
|
{valueNode}
|
||||||
|
{suffix && <span class={`${prefixCls}-content-suffix`}>{suffix}</span>}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`renders ./components/statistic/demo/basic.md correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div class="ant-statistic" style="margin-right: 50px;">
|
||||||
|
<div class="ant-statistic-title">Active Users</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">112,893</span></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="ant-statistic">
|
||||||
|
<div class="ant-statistic-title">Account Balance (CNY)</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">112,893</span><span class="ant-statistic-content-value-decimal">.00</span></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/statistic/demo/card.md correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div class="ant-card ant-card-bordered" style="padding: 30px;">
|
||||||
|
<div class="ant-card-body">
|
||||||
|
<div class="ant-statistic" style="margin-right: 50px;">
|
||||||
|
<div class="ant-statistic-title">Feedback</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-prefix"><i class="anticon anticon-like"><svg viewBox="64 64 896 896" data-icon="like" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M885.9 533.7c16.8-22.2 26.1-49.4 26.1-77.7 0-44.9-25.1-87.4-65.5-111.1a67.67 67.67 0 0 0-34.3-9.3H572.4l6-122.9c1.4-29.7-9.1-57.9-29.5-79.4A106.62 106.62 0 0 0 471 99.9c-52 0-98 35-111.8 85.1l-85.9 311H144c-17.7 0-32 14.3-32 32v364c0 17.7 14.3 32 32 32h601.3c9.2 0 18.2-1.8 26.5-5.4 47.6-20.3 78.3-66.8 78.3-118.4 0-12.6-1.8-25-5.4-37 16.8-22.2 26.1-49.4 26.1-77.7 0-12.6-1.8-25-5.4-37 16.8-22.2 26.1-49.4 26.1-77.7-.2-12.6-2-25.1-5.6-37.1zM184 852V568h81v284h-81zm636.4-353l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 16.5-7.2 32.2-19.6 43l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 16.5-7.2 32.2-19.6 43l-21.9 19 13.9 25.4a56.2 56.2 0 0 1 6.9 27.3c0 22.4-13.2 42.6-33.6 51.8H329V564.8l99.5-360.5a44.1 44.1 0 0 1 42.2-32.3c7.6 0 15.1 2.2 21.1 6.7 9.9 7.4 15.2 18.6 14.6 30.5l-9.6 198.4h314.4C829 418.5 840 436.9 840 456c0 16.5-7.2 32.1-19.6 43z"></path></svg></i></span><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">1,128</span></span></div>
|
||||||
|
</div>
|
||||||
|
<div class="ant-statistic" valueclass="demo-class">
|
||||||
|
<div class="ant-statistic-title">Unmerged</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">1,234,567,890</span></span><span class="ant-statistic-content-suffix"><span> / 100</span></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/statistic/demo/countdown.md correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div class="ant-statistic" style="margin-right: 50px;">
|
||||||
|
<div class="ant-statistic-title">Countdown</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-value">48:00:30</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="ant-statistic">
|
||||||
|
<div class="ant-statistic-title">Million Seconds</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-value">48:00:30:000</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`renders ./components/statistic/demo/unit.md correctly 1`] = `
|
||||||
|
<div>
|
||||||
|
<div class="ant-statistic" style="margin-right: 50px;">
|
||||||
|
<div class="ant-statistic-title">Feedback</div>
|
||||||
|
<div class="ant-statistic-content" style="color: rgb(63, 134, 0);"><span class="ant-statistic-content-prefix"><i class="anticon anticon-arrow-up"><svg viewBox="64 64 896 896" data-icon="arrow-up" width="1em" height="1em" fill="currentColor" aria-hidden="true" class=""><path d="M868 545.5L536.1 163a31.96 31.96 0 0 0-48.3 0L156 545.5a7.97 7.97 0 0 0 6 13.2h81c4.6 0 9-2 12.1-5.5L474 300.9V864c0 4.4 3.6 8 8 8h60c4.4 0 8-3.6 8-8V300.9l218.9 252.3c3 3.5 7.4 5.5 12.1 5.5h81c6.8 0 10.5-8 6-13.2z"></path></svg></i></span><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">11</span><span class="ant-statistic-content-value-decimal">.28</span></span><span class="ant-statistic-content-suffix">%</span></div>
|
||||||
|
</div>
|
||||||
|
<div class="ant-statistic" valueclass="demo-class">
|
||||||
|
<div class="ant-statistic-title">Unmerged</div>
|
||||||
|
<div class="ant-statistic-content"><span class="ant-statistic-content-value"><span class="ant-statistic-content-value-int">78</span></span><span class="ant-statistic-content-suffix"><span> / 100</span></span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
|
@ -0,0 +1,3 @@
|
||||||
|
import demoTest from '../../../tests/shared/demoTest';
|
||||||
|
|
||||||
|
demoTest('statistic');
|
|
@ -0,0 +1,18 @@
|
||||||
|
<cn>
|
||||||
|
#### 基本
|
||||||
|
简单展示
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Basic
|
||||||
|
Simplest Usage.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-statistic title="Active Users" :value="112893" style="margin-right: 50px" />
|
||||||
|
<a-statistic title="Account Balance (CNY)" :precision="2" :value="112893" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
|
@ -0,0 +1,32 @@
|
||||||
|
<cn>
|
||||||
|
#### 在卡片中使用
|
||||||
|
在卡片中展示统计数值。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### In Card
|
||||||
|
Display statistic data in Card.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-card style="padding: 30px">
|
||||||
|
<a-statistic
|
||||||
|
title="Feedback"
|
||||||
|
:value="1128"
|
||||||
|
style="margin-right: 50px"
|
||||||
|
>
|
||||||
|
<a-icon slot="prefix" type="like" />
|
||||||
|
</a-statistic>
|
||||||
|
<a-statistic
|
||||||
|
title="Unmerged"
|
||||||
|
:value="1234567890"
|
||||||
|
valueClass="demo-class"
|
||||||
|
>
|
||||||
|
<span slot="suffix"> / 100</span>
|
||||||
|
</a-statistic>
|
||||||
|
</a-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
|
@ -0,0 +1,41 @@
|
||||||
|
<cn>
|
||||||
|
#### 倒计时
|
||||||
|
倒计时组件。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Countdown
|
||||||
|
Countdown component.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-statistic-countdown
|
||||||
|
title="Countdown"
|
||||||
|
:value="deadline"
|
||||||
|
@finish="onFinish"
|
||||||
|
style="margin-right: 50px"
|
||||||
|
/>
|
||||||
|
<a-statistic-countdown
|
||||||
|
title="Million Seconds"
|
||||||
|
:value="deadline"
|
||||||
|
format="HH:mm:ss:SSS"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
deadline: Date.now() + 1000 * 60 * 60 * 24 * 2 + 1000 * 30,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
onFinish() {
|
||||||
|
console.log('over');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
|
@ -0,0 +1,50 @@
|
||||||
|
<script>
|
||||||
|
import Basic from './basic';
|
||||||
|
import Card from './card';
|
||||||
|
import Unit from './unit';
|
||||||
|
import Countdown from './countdown';
|
||||||
|
import CN from '../index.zh-CN.md';
|
||||||
|
import US from '../index.en-US.md';
|
||||||
|
|
||||||
|
const md = {
|
||||||
|
cn: `# Statistic 统计数值
|
||||||
|
展示统计数值。
|
||||||
|
## 何时使用
|
||||||
|
- 当需要突出某个或某组数字时
|
||||||
|
- 当需要展示带描述的统计类数据时使用
|
||||||
|
|
||||||
|
## 代码演示`,
|
||||||
|
us: `# Statistic
|
||||||
|
Statistics can be used to represent people or objects. It supports images, 'Icon's, or letters.
|
||||||
|
## Examples
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
export default {
|
||||||
|
category: 'Components',
|
||||||
|
subtitle: '统计数值',
|
||||||
|
type: 'Data Display',
|
||||||
|
zhType: '数据展示',
|
||||||
|
title: 'Statistic',
|
||||||
|
render () {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<md cn={md.cn} us={md.us}/>
|
||||||
|
<Basic/>
|
||||||
|
<br/>
|
||||||
|
<Unit/>
|
||||||
|
<br/>
|
||||||
|
<Card/>
|
||||||
|
<br/>
|
||||||
|
<Countdown/>
|
||||||
|
<br/>
|
||||||
|
<api>
|
||||||
|
<template slot='cn'>
|
||||||
|
<CN/>
|
||||||
|
</template>
|
||||||
|
<US/>
|
||||||
|
</api>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,33 @@
|
||||||
|
<cn>
|
||||||
|
#### 单位
|
||||||
|
通过前缀和后缀添加单位。
|
||||||
|
</cn>
|
||||||
|
|
||||||
|
<us>
|
||||||
|
#### Unit
|
||||||
|
Add unit through `prefix` and `suffix`.
|
||||||
|
</us>
|
||||||
|
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-statistic
|
||||||
|
title="Feedback"
|
||||||
|
:value="11.28"
|
||||||
|
:precision="2"
|
||||||
|
suffix="%"
|
||||||
|
:valueStyle="{color: '#3f8600'}"
|
||||||
|
style="margin-right: 50px"
|
||||||
|
>
|
||||||
|
<a-icon slot="prefix" type="arrow-up" />
|
||||||
|
</a-statistic>
|
||||||
|
<a-statistic
|
||||||
|
title="Unmerged"
|
||||||
|
:value="78"
|
||||||
|
valueClass="demo-class"
|
||||||
|
>
|
||||||
|
<span slot="suffix"> / 100</span>
|
||||||
|
</a-statistic>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
```
|
|
@ -0,0 +1,26 @@
|
||||||
|
## API
|
||||||
|
|
||||||
|
#### Statistic
|
||||||
|
|
||||||
|
| Property | Description | Type | Default |
|
||||||
|
| -------- | ----------- | ---- | ------- |
|
||||||
|
| decimalSeparator | decimal separator | string | . |
|
||||||
|
| formatter | customize value display logic | (h) => VNode | - |
|
||||||
|
| groupSeparator | group separator | string | , |
|
||||||
|
| precision | precision of input value | number | - |
|
||||||
|
| prefix | prefix node of value | string \| VNode | - |
|
||||||
|
| suffix | suffix node of value | string \| VNode | - |
|
||||||
|
| title | Display title | string \| VNode | - |
|
||||||
|
| value | Display value | string \| number | - |
|
||||||
|
|
||||||
|
|
||||||
|
#### Statistic.Countdown
|
||||||
|
|
||||||
|
| Property | Description | Type | Default |
|
||||||
|
| -------- | ----------- | ---- | ------- |
|
||||||
|
| format | Format as [moment](http://momentjs.com/) | string | 'HH:mm:ss' |
|
||||||
|
| onFinish | Trigger when time's up | () => void | - |
|
||||||
|
| prefix | prefix node of value | string \| VNode | - |
|
||||||
|
| suffix | suffix node of value | string \| VNode | - |
|
||||||
|
| title | Display title | string \| VNode | - |
|
||||||
|
| value | Set target countdown time | number \| moment | - |
|
|
@ -0,0 +1,11 @@
|
||||||
|
import Statistic from './Statistic';
|
||||||
|
import Countdown from './Countdown';
|
||||||
|
|
||||||
|
Statistic.Countdown = Countdown;
|
||||||
|
/* istanbul ignore next */
|
||||||
|
Statistic.install = function(Vue) {
|
||||||
|
Vue.component(Statistic.name, Statistic);
|
||||||
|
Vue.component(Statistic.Countdown.name, Statistic.Countdown);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Statistic;
|
|
@ -0,0 +1,25 @@
|
||||||
|
## API
|
||||||
|
|
||||||
|
#### Statistic
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| -------- | ----------- | ---- | ------- |
|
||||||
|
| decimalSeparator | 设置小数点 | string | . |
|
||||||
|
| formatter | 自定义数值展示 | (h) => VNode | - |
|
||||||
|
| groupSeparator | 设置千分位标识符 | string | , |
|
||||||
|
| precision | 数值精度 | number | - |
|
||||||
|
| prefix | 设置数值的前缀 | string \| VNode | - |
|
||||||
|
| suffix | 设置数值的后缀 | string \| VNode | - |
|
||||||
|
| title | 数值的标题 | string \| VNode | - |
|
||||||
|
| value | 数值内容 | string \| number | - |
|
||||||
|
|
||||||
|
#### Statistic.Countdown
|
||||||
|
|
||||||
|
| 参数 | 说明 | 类型 | 默认值 |
|
||||||
|
| -------- | ----------- | ---- | ------- |
|
||||||
|
| format | 格式化倒计时展示,参考 [moment](http://momentjs.com/) | string | 'HH:mm:ss' |
|
||||||
|
| onFinish | 倒计时完成时触发 | () => void | - |
|
||||||
|
| prefix | 设置数值的前缀 | string \| VNode | - |
|
||||||
|
| suffix | 设置数值的后缀 | string \| VNode | - |
|
||||||
|
| title | 数值的标题 | string \| VNode | - |
|
||||||
|
| value | 数值内容 | number \| moment | - |
|
|
@ -0,0 +1,2 @@
|
||||||
|
import '../../style/index.less';
|
||||||
|
import './index.less';
|
|
@ -0,0 +1,23 @@
|
||||||
|
@import '../../style/themes/default';
|
||||||
|
@import '../../style/mixins/index';
|
||||||
|
|
||||||
|
@statistic-prefix-cls: ~'@{ant-prefix}-statistic';
|
||||||
|
|
||||||
|
.@{statistic-prefix-cls} {
|
||||||
|
.reset-component;
|
||||||
|
display: inline-block;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
vertical-align: middle;
|
||||||
|
|
||||||
|
&-content-value,
|
||||||
|
&-content-prefix {
|
||||||
|
font-size: 1.7em;
|
||||||
|
}
|
||||||
|
&-content-value-decimal {
|
||||||
|
font-size: 0.7em;
|
||||||
|
}
|
||||||
|
&-content-suffix {
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
import * as moment from 'moment';
|
||||||
|
import padStart from 'lodash/padStart';
|
||||||
|
|
||||||
|
import interopDefault from '../_util/interopDefault';
|
||||||
|
|
||||||
|
// Countdown
|
||||||
|
const timeUnits = [
|
||||||
|
['Y', 1000 * 60 * 60 * 24 * 365], // years
|
||||||
|
['M', 1000 * 60 * 60 * 24 * 30], // months
|
||||||
|
['D', 1000 * 60 * 60 * 24], // days
|
||||||
|
['H', 1000 * 60 * 60], // hours
|
||||||
|
['m', 1000 * 60], // minutes
|
||||||
|
['s', 1000], // seconds
|
||||||
|
['S', 1], // million seconds
|
||||||
|
];
|
||||||
|
|
||||||
|
function formatTimeStr(duration, format) {
|
||||||
|
let leftDuration = duration;
|
||||||
|
|
||||||
|
return timeUnits.reduce((current, [name, unit]) => {
|
||||||
|
if (current.indexOf(name) !== -1) {
|
||||||
|
const value = Math.floor(leftDuration / unit);
|
||||||
|
leftDuration -= value * unit;
|
||||||
|
return current.replace(new RegExp(`${name}+`, 'g'), match => {
|
||||||
|
const len = match.length;
|
||||||
|
return padStart(value.toString(), len, '0');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return current;
|
||||||
|
}, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatCountdown(value, config) {
|
||||||
|
const { format = '' } = config;
|
||||||
|
const target = interopDefault(moment)(value).valueOf();
|
||||||
|
const current = interopDefault(moment)().valueOf();
|
||||||
|
const diff = Math.max(target - current, 0);
|
||||||
|
return formatTimeStr(diff, format);
|
||||||
|
}
|
|
@ -54,3 +54,4 @@ import './skeleton/style';
|
||||||
import './comment/style';
|
import './comment/style';
|
||||||
import './config-provider/style';
|
import './config-provider/style';
|
||||||
import './empty/style';
|
import './empty/style';
|
||||||
|
import './statistic/style';
|
||||||
|
|
|
@ -40,6 +40,7 @@ import {
|
||||||
Select,
|
Select,
|
||||||
Slider,
|
Slider,
|
||||||
Spin,
|
Spin,
|
||||||
|
Statistic,
|
||||||
Steps,
|
Steps,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
|
@ -110,6 +111,7 @@ Vue.use(Row);
|
||||||
Vue.use(Select);
|
Vue.use(Select);
|
||||||
Vue.use(Slider);
|
Vue.use(Slider);
|
||||||
Vue.use(Spin);
|
Vue.use(Spin);
|
||||||
|
Vue.use(Statistic);
|
||||||
Vue.use(Steps);
|
Vue.use(Steps);
|
||||||
Vue.use(Switch);
|
Vue.use(Switch);
|
||||||
Vue.use(Table);
|
Vue.use(Table);
|
||||||
|
|
|
@ -352,4 +352,10 @@ export default {
|
||||||
title: 'Skeleton',
|
title: 'Skeleton',
|
||||||
subtitle: '骨架屏',
|
subtitle: '骨架屏',
|
||||||
},
|
},
|
||||||
|
statistic: {
|
||||||
|
category: 'Components',
|
||||||
|
subtitle: '统计数值',
|
||||||
|
type: 'Data Display',
|
||||||
|
title: 'Statistic',
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,6 +7,14 @@ export default [
|
||||||
path: 'avatar-cn',
|
path: 'avatar-cn',
|
||||||
component: () => import('../components/avatar/demo/index.vue'),
|
component: () => import('../components/avatar/demo/index.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'statistic',
|
||||||
|
component: () => import('../components/statistic/demo/index.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'statistic-cn',
|
||||||
|
component: () => import('../components/statistic/demo/index.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: 'badge',
|
path: 'badge',
|
||||||
component: () => import('../components/badge/demo/index.vue'),
|
component: () => import('../components/badge/demo/index.vue'),
|
||||||
|
|
|
@ -44,6 +44,7 @@ Array [
|
||||||
"Select",
|
"Select",
|
||||||
"Slider",
|
"Slider",
|
||||||
"Spin",
|
"Spin",
|
||||||
|
"Statistic",
|
||||||
"Steps",
|
"Steps",
|
||||||
"Switch",
|
"Switch",
|
||||||
"Table",
|
"Table",
|
||||||
|
|
|
@ -46,6 +46,7 @@ import { Select } from './select/select';
|
||||||
import { Skeleton } from './skeleton';
|
import { Skeleton } from './skeleton';
|
||||||
import { Slider } from './slider';
|
import { Slider } from './slider';
|
||||||
import { Spin } from './spin';
|
import { Spin } from './spin';
|
||||||
|
import { Statistic } from './statistic';
|
||||||
import { Steps } from './steps/steps';
|
import { Steps } from './steps/steps';
|
||||||
import { Switch } from './switch';
|
import { Switch } from './switch';
|
||||||
import { Table } from './table/table';
|
import { Table } from './table/table';
|
||||||
|
@ -110,6 +111,7 @@ export {
|
||||||
Select,
|
Select,
|
||||||
Slider,
|
Slider,
|
||||||
Spin,
|
Spin,
|
||||||
|
Statistic,
|
||||||
Steps,
|
Steps,
|
||||||
Switch,
|
Switch,
|
||||||
Table,
|
Table,
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
// Project: https://github.com/vueComponent/ant-design-vue
|
||||||
|
// Definitions by: akki-jat <https://github.com/akki-jat>
|
||||||
|
// Definitions: https://github.com/vueComponent/ant-design-vue/types
|
||||||
|
|
||||||
|
import { AntdComponent } from './component';
|
||||||
|
|
||||||
|
export declare class Statistic extends AntdComponent {
|
||||||
|
/**
|
||||||
|
* the Icon type for an icon statistic, see Icon Component
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
icon: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the shape of statistic
|
||||||
|
* @default 'circle'
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
shape: 'circle' | 'square';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the size of the statistic
|
||||||
|
* @default 'default'
|
||||||
|
* @type number | string
|
||||||
|
*/
|
||||||
|
size: 'small' | 'large' | 'default' | number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* the address of the image for an image statistic
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
src: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* a list of sources to use for different screen resolutions
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
srcSet: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This attribute defines the alternative text describing the image
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
alt: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* handler when img load error,return false to prevent default fallback behavior
|
||||||
|
* @type
|
||||||
|
*/
|
||||||
|
loadError: () => boolean;
|
||||||
|
}
|
Loading…
Reference in New Issue