Skip to content
GitLab
Projects
Groups
Snippets
Help
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
P
pro-blocks
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Operations
Operations
Incidents
Environments
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
duanledexianxianxian
pro-blocks
Commits
278a1ce9
Commit
278a1ce9
authored
Apr 15, 2019
by
陈帅
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Analysis finish
parent
74b673e5
Changes
50
Hide whitespace changes
Inline
Side-by-side
Showing
50 changed files
with
3046 additions
and
262 deletions
+3046
-262
Analysis/package.json
Analysis/package.json
+17
-13
Analysis/src/_mock.ts
Analysis/src/_mock.ts
+4
-3
Analysis/src/components/Charts/Bar/index.tsx
Analysis/src/components/Charts/Bar/index.tsx
+131
-0
Analysis/src/components/Charts/ChartCard/index.less
Analysis/src/components/Charts/ChartCard/index.less
+75
-0
Analysis/src/components/Charts/ChartCard/index.tsx
Analysis/src/components/Charts/ChartCard/index.tsx
+98
-0
Analysis/src/components/Charts/Field/index.less
Analysis/src/components/Charts/Field/index.less
+17
-0
Analysis/src/components/Charts/Field/index.tsx
Analysis/src/components/Charts/Field/index.tsx
+18
-0
Analysis/src/components/Charts/Gauge/index.tsx
Analysis/src/components/Charts/Gauge/index.tsx
+177
-0
Analysis/src/components/Charts/MiniArea/index.tsx
Analysis/src/components/Charts/MiniArea/index.tsx
+133
-0
Analysis/src/components/Charts/MiniBar/index.tsx
Analysis/src/components/Charts/MiniBar/index.tsx
+61
-0
Analysis/src/components/Charts/MiniProgress/index.less
Analysis/src/components/Charts/MiniProgress/index.less
+37
-0
Analysis/src/components/Charts/MiniProgress/index.tsx
Analysis/src/components/Charts/MiniProgress/index.tsx
+43
-0
Analysis/src/components/Charts/Pie/index.less
Analysis/src/components/Charts/Pie/index.less
+94
-0
Analysis/src/components/Charts/Pie/index.tsx
Analysis/src/components/Charts/Pie/index.tsx
+295
-0
Analysis/src/components/Charts/Radar/index.less
Analysis/src/components/Charts/Radar/index.less
+46
-0
Analysis/src/components/Charts/Radar/index.tsx
Analysis/src/components/Charts/Radar/index.tsx
+216
-0
Analysis/src/components/Charts/TagCloud/index.less
Analysis/src/components/Charts/TagCloud/index.less
+6
-0
Analysis/src/components/Charts/TagCloud/index.tsx
Analysis/src/components/Charts/TagCloud/index.tsx
+206
-0
Analysis/src/components/Charts/TimelineChart/index.less
Analysis/src/components/Charts/TimelineChart/index.less
+3
-0
Analysis/src/components/Charts/TimelineChart/index.tsx
Analysis/src/components/Charts/TimelineChart/index.tsx
+133
-0
Analysis/src/components/Charts/WaterWave/index.less
Analysis/src/components/Charts/WaterWave/index.less
+28
-0
Analysis/src/components/Charts/WaterWave/index.tsx
Analysis/src/components/Charts/WaterWave/index.tsx
+230
-0
Analysis/src/components/Charts/autoHeight.tsx
Analysis/src/components/Charts/autoHeight.tsx
+80
-0
Analysis/src/components/Charts/bizcharts.d.ts
Analysis/src/components/Charts/bizcharts.d.ts
+3
-0
Analysis/src/components/Charts/bizcharts.tsx
Analysis/src/components/Charts/bizcharts.tsx
+3
-0
Analysis/src/components/Charts/index.less
Analysis/src/components/Charts/index.less
+19
-0
Analysis/src/components/Charts/index.tsx
Analysis/src/components/Charts/index.tsx
+48
-0
Analysis/src/components/IntroduceRow.tsx
Analysis/src/components/IntroduceRow.tsx
+163
-0
Analysis/src/components/NumberInfo/index.less
Analysis/src/components/NumberInfo/index.less
+68
-0
Analysis/src/components/NumberInfo/index.tsx
Analysis/src/components/NumberInfo/index.tsx
+61
-0
Analysis/src/components/OfflineData.tsx
Analysis/src/components/OfflineData.tsx
+80
-0
Analysis/src/components/PageLoading/index.tsx
Analysis/src/components/PageLoading/index.tsx
+0
-0
Analysis/src/components/ProportionSales.js
Analysis/src/components/ProportionSales.js
+0
-65
Analysis/src/components/ProportionSales.tsx
Analysis/src/components/ProportionSales.tsx
+77
-0
Analysis/src/components/SalesCard.js
Analysis/src/components/SalesCard.js
+0
-152
Analysis/src/components/SalesCard.tsx
Analysis/src/components/SalesCard.tsx
+162
-0
Analysis/src/components/TopSearch.tsx
Analysis/src/components/TopSearch.tsx
+22
-10
Analysis/src/components/Trend/index.less
Analysis/src/components/Trend/index.less
+37
-0
Analysis/src/components/Trend/index.tsx
Analysis/src/components/Trend/index.tsx
+42
-0
Analysis/src/data.d.ts
Analysis/src/data.d.ts
+67
-0
Analysis/src/index.tsx
Analysis/src/index.tsx
+42
-16
Analysis/src/locales/en-US.ts
Analysis/src/locales/en-US.ts
+0
-0
Analysis/src/locales/pt-BR.ts
Analysis/src/locales/pt-BR.ts
+0
-0
Analysis/src/locales/zh-CN.ts
Analysis/src/locales/zh-CN.ts
+0
-0
Analysis/src/locales/zh-TW.ts
Analysis/src/locales/zh-TW.ts
+0
-0
Analysis/src/model.tsx
Analysis/src/model.tsx
+0
-0
Analysis/src/service.tsx
Analysis/src/service.tsx
+0
-0
Analysis/src/utils/Yuan.tsx
Analysis/src/utils/Yuan.tsx
+0
-0
Analysis/src/utils/utils.ts
Analysis/src/utils/utils.ts
+3
-2
package.json
package.json
+1
-1
No files found.
Analysis/package.json
View file @
278a1ce9
...
...
@@ -2,27 +2,31 @@
"name"
:
"@umi-block/analysis"
,
"version"
:
"0.0.1"
,
"description"
:
"Analysis"
,
"main"
:
"src/index.js"
,
"scripts"
:
{
"dev"
:
"umi dev"
},
"repository"
:
{
"type"
:
"git"
,
"url"
:
"https://github.com/umijs/umi-blocks/ant-design-pro/analysis"
},
"license"
:
"ISC"
,
"main"
:
"src/index.js"
,
"scripts"
:
{
"dev"
:
"umi dev"
},
"dependencies"
:
{
"
react"
:
"^16.6.3
"
,
"
dva"
:
"^2.4.0
"
,
"
@antv/data-set"
:
"^0.10.2
"
,
"
@types/numeral"
:
"^0.0.25
"
,
"antd"
:
"^3.10.9"
,
"bizcharts"
:
"^3.5.2"
,
"bizcharts-plugin-slider"
:
"^2.1.1-beta.1"
,
"dva"
:
"^2.4.0"
,
"moment"
:
"^2.22.2"
,
"umi-request"
:
"^1.0.0"
,
"ant-design-pro"
:
"^2.1.1"
,
"numeral"
:
"^2.0.6"
"numeral"
:
"^2.0.6"
,
"react"
:
"^16.6.3"
,
"react-fittext"
:
"^1.0.0"
,
"umi-request"
:
"^1.0.0"
},
"devDependencies"
:
{
"umi"
:
"^2.6.9"
,
"umi-plugin-react"
:
"^1.7.2"
,
"umi-plugin-block-dev"
:
"^1.1.0"
},
"license"
:
"ISC"
"umi-plugin-block-dev"
:
"^1.1.0"
,
"umi-plugin-react"
:
"^1.7.2"
}
}
Analysis/src/_mock.
j
s
→
Analysis/src/_mock.
t
s
View file @
278a1ce9
import
moment
from
'
moment
'
;
import
{
IVisitData
,
IRadarData
,
IAnalysisData
}
from
'
./data
'
;
// mock data
const
visitData
=
[];
const
visitData
:
IVisitData
[]
=
[];
const
beginDay
=
new
Date
().
getTime
();
const
fakeY
=
[
7
,
5
,
4
,
2
,
4
,
7
,
5
,
6
,
5
,
9
,
6
,
3
,
1
,
5
,
3
,
6
,
5
];
...
...
@@ -158,7 +159,7 @@ const radarOriginData = [
},
];
const
radarData
=
[];
const
radarData
:
IRadarData
[]
=
[];
const
radarTitleMap
=
{
ref
:
'
引用
'
,
koubei
:
'
口碑
'
,
...
...
@@ -178,7 +179,7 @@ radarOriginData.forEach(item => {
});
});
const
getFakeChartData
=
{
const
getFakeChartData
:
IAnalysisData
=
{
visitData
,
visitData2
,
salesData
,
...
...
Analysis/src/components/Charts/Bar/index.tsx
0 → 100644
View file @
278a1ce9
import
React
,
{
Component
}
from
'
react
'
;
import
{
Chart
,
Axis
,
Tooltip
,
Geom
}
from
'
bizcharts
'
;
import
Debounce
from
'
lodash-decorators/debounce
'
;
import
Bind
from
'
lodash-decorators/bind
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
../index.less
'
;
export
interface
IBarProps
{
title
:
React
.
ReactNode
;
color
?:
string
;
padding
?:
[
number
,
number
,
number
,
number
];
height
?:
number
;
data
:
Array
<
{
x
:
string
;
y
:
number
;
}
>
;
forceFit
?:
boolean
;
autoLabel
?:
boolean
;
style
?:
React
.
CSSProperties
;
}
class
Bar
extends
Component
<
IBarProps
,
{
autoHideXLabels
:
boolean
;
}
>
{
state
=
{
autoHideXLabels
:
false
,
};
componentDidMount
()
{
window
.
addEventListener
(
'
resize
'
,
this
.
resize
,
{
passive
:
true
});
}
componentWillUnmount
()
{
window
.
removeEventListener
(
'
resize
'
,
this
.
resize
);
}
root
:
HTMLDivElement
|
undefined
;
handleRoot
=
(
n
:
HTMLDivElement
)
=>
{
this
.
root
=
n
;
};
node
:
HTMLDivElement
|
undefined
;
handleRef
=
(
n
:
HTMLDivElement
)
=>
{
this
.
node
=
n
;
};
@
Bind
()
@
Debounce
(
400
)
resize
()
{
if
(
!
this
.
node
||
!
this
.
node
.
parentNode
)
{
return
;
}
const
canvasWidth
=
(
this
.
node
.
parentNode
as
HTMLDivElement
).
clientWidth
;
const
{
data
=
[],
autoLabel
=
true
}
=
this
.
props
;
if
(
!
autoLabel
)
{
return
;
}
const
minWidth
=
data
.
length
*
30
;
const
{
autoHideXLabels
}
=
this
.
state
;
if
(
canvasWidth
<=
minWidth
)
{
if
(
!
autoHideXLabels
)
{
this
.
setState
({
autoHideXLabels
:
true
,
});
}
}
else
if
(
autoHideXLabels
)
{
this
.
setState
({
autoHideXLabels
:
false
,
});
}
}
render
()
{
const
{
height
=
1
,
title
,
forceFit
=
true
,
data
,
color
=
'
rgba(24, 144, 255, 0.85)
'
,
padding
,
}
=
this
.
props
;
const
{
autoHideXLabels
}
=
this
.
state
;
const
scale
=
{
x
:
{
type
:
'
cat
'
,
},
y
:
{
min
:
0
,
},
};
const
tooltip
:
[
string
,
(...
args
:
any
[])
=>
{
name
?:
string
;
value
:
string
}]
=
[
'
x*y
'
,
(
x
:
string
,
y
:
string
)
=>
({
name
:
x
,
value
:
y
,
}),
];
return
(
<
div
className
=
{
styles
.
chart
}
style
=
{
{
height
}
}
ref
=
{
this
.
handleRoot
}
>
<
div
ref
=
{
this
.
handleRef
}
>
{
title
&&
<
h4
style
=
{
{
marginBottom
:
20
}
}
>
{
title
}
</
h4
>
}
<
Chart
scale
=
{
scale
}
height
=
{
title
?
height
-
41
:
height
}
forceFit
=
{
forceFit
}
data
=
{
data
}
padding
=
{
padding
||
'
auto
'
}
>
<
Axis
name
=
"x"
title
=
{
false
}
label
=
{
autoHideXLabels
?
false
:
{}
}
tickLine
=
{
autoHideXLabels
?
false
:
{}
}
/>
<
Axis
name
=
"y"
min
=
{
0
}
/>
<
Tooltip
showTitle
=
{
false
}
crosshairs
=
{
false
}
/>
<
Geom
type
=
"interval"
position
=
"x*y"
color
=
{
color
}
tooltip
=
{
tooltip
}
/>
</
Chart
>
</
div
>
</
div
>
);
}
}
export
default
autoHeight
()(
Bar
);
Analysis/src/components/Charts/ChartCard/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.chartCard {
position: relative;
.chartTop {
position: relative;
width: 100%;
overflow: hidden;
}
.chartTopMargin {
margin-bottom: 12px;
}
.chartTopHasMargin {
margin-bottom: 20px;
}
.metaWrap {
float: left;
}
.avatar {
position: relative;
top: 4px;
float: left;
margin-right: 20px;
img {
border-radius: 100%;
}
}
.meta {
height: 22px;
color: @text-color-secondary;
font-size: @font-size-base;
line-height: 22px;
}
.action {
position: absolute;
top: 4px;
right: 0;
line-height: 1;
cursor: pointer;
}
.total {
height: 38px;
margin-top: 4px;
margin-bottom: 0;
overflow: hidden;
color: @heading-color;
font-size: 30px;
line-height: 38px;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
}
.content {
position: relative;
width: 100%;
margin-bottom: 12px;
}
.contentFixed {
position: absolute;
bottom: 0;
left: 0;
width: 100%;
}
.footer {
margin-top: 8px;
padding-top: 9px;
border-top: 1px solid @border-color-split;
& > * {
position: relative;
}
}
.footerMargin {
margin-top: 20px;
}
}
Analysis/src/components/Charts/ChartCard/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Card
}
from
'
antd
'
;
import
classNames
from
'
classnames
'
;
import
{
CardProps
}
from
'
antd/lib/card
'
;
import
styles
from
'
./index.less
'
;
type
totalType
=
()
=>
React
.
ReactNode
;
const
renderTotal
=
(
total
?:
number
|
totalType
|
React
.
ReactNode
)
=>
{
if
(
!
total
)
{
return
;
}
let
totalDom
;
switch
(
typeof
total
)
{
case
'
undefined
'
:
totalDom
=
null
;
break
;
case
'
function
'
:
totalDom
=
<
div
className
=
{
styles
.
total
}
>
{
total
()
}
</
div
>;
break
;
default
:
totalDom
=
<
div
className
=
{
styles
.
total
}
>
{
total
}
</
div
>;
}
return
totalDom
;
};
export
interface
IChartCardProps
extends
CardProps
{
title
:
React
.
ReactNode
;
action
?:
React
.
ReactNode
;
total
?:
React
.
ReactNode
|
number
|
(()
=>
React
.
ReactNode
|
number
);
footer
?:
React
.
ReactNode
;
contentHeight
?:
number
;
avatar
?:
React
.
ReactNode
;
style
?:
React
.
CSSProperties
;
}
class
ChartCard
extends
React
.
Component
<
IChartCardProps
>
{
renderContent
=
()
=>
{
const
{
contentHeight
,
title
,
avatar
,
action
,
total
,
footer
,
children
,
loading
}
=
this
.
props
;
if
(
loading
)
{
return
false
;
}
return
(
<
div
className
=
{
styles
.
chartCard
}
>
<
div
className
=
{
classNames
(
styles
.
chartTop
,
{
[
styles
.
chartTopMargin
]:
!
children
&&
!
footer
,
})
}
>
<
div
className
=
{
styles
.
avatar
}
>
{
avatar
}
</
div
>
<
div
className
=
{
styles
.
metaWrap
}
>
<
div
className
=
{
styles
.
meta
}
>
<
span
className
=
{
styles
.
title
}
>
{
title
}
</
span
>
<
span
className
=
{
styles
.
action
}
>
{
action
}
</
span
>
</
div
>
{
renderTotal
(
total
)
}
</
div
>
</
div
>
{
children
&&
(
<
div
className
=
{
styles
.
content
}
style
=
{
{
height
:
contentHeight
||
'
auto
'
}
}
>
<
div
className
=
{
contentHeight
&&
styles
.
contentFixed
}
>
{
children
}
</
div
>
</
div
>
)
}
{
footer
&&
(
<
div
className
=
{
classNames
(
styles
.
footer
,
{
[
styles
.
footerMargin
]:
!
children
,
})
}
>
{
footer
}
</
div
>
)
}
</
div
>
);
};
render
()
{
const
{
loading
=
false
,
contentHeight
,
title
,
avatar
,
action
,
total
,
footer
,
children
,
...
rest
}
=
this
.
props
;
return
(
<
Card
loading
=
{
loading
}
bodyStyle
=
{
{
padding
:
'
20px 24px 8px 24px
'
}
}
{
...
rest
}
>
{
this
.
renderContent
()
}
</
Card
>
);
}
}
export
default
ChartCard
;
Analysis/src/components/Charts/Field/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.field {
margin: 0;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
.label,
.number {
font-size: @font-size-base;
line-height: 22px;
}
.number {
margin-left: 8px;
color: @heading-color;
}
}
Analysis/src/components/Charts/Field/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
styles
from
'
./index.less
'
;
export
interface
IFieldProps
{
label
:
React
.
ReactNode
;
value
:
React
.
ReactNode
;
style
?:
React
.
CSSProperties
;
}
const
Field
:
React
.
SFC
<
IFieldProps
>
=
({
label
,
value
,
...
rest
})
=>
(
<
div
className
=
{
styles
.
field
}
{
...
rest
}
>
<
span
className
=
{
styles
.
label
}
>
{
label
}
</
span
>
<
span
className
=
{
styles
.
number
}
>
{
value
}
</
span
>
</
div
>
);
export
default
Field
;
Analysis/src/components/Charts/Gauge/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Chart
,
Geom
,
Axis
,
Coord
,
Guide
,
Shape
}
from
'
bizcharts
'
;
import
autoHeight
from
'
../autoHeight
'
;
const
{
Arc
,
Html
,
Line
}
=
Guide
;
export
interface
IGaugeProps
{
title
:
React
.
ReactNode
;
color
?:
string
;
height
?:
number
;
bgColor
?:
number
;
percent
:
number
;
forceFit
?:
boolean
;
style
?:
React
.
CSSProperties
;
formatter
:
(
value
:
string
)
=>
string
;
}
const
defaultFormatter
=
(
val
:
string
):
string
=>
{
switch
(
val
)
{
case
'
2
'
:
return
'
差
'
;
case
'
4
'
:
return
'
中
'
;
case
'
6
'
:
return
'
良
'
;
case
'
8
'
:
return
'
优
'
;
default
:
return
''
;
}
};
Shape
.
registerShape
!
(
'
point
'
,
'
pointer
'
,
{
drawShape
(
cfg
:
any
,
group
:
any
)
{
let
point
=
cfg
.
points
[
0
];
point
=
(
this
as
any
).
parsePoint
(
point
);
const
center
=
(
this
as
any
).
parsePoint
({
x
:
0
,
y
:
0
,
});
group
.
addShape
(
'
line
'
,
{
attrs
:
{
x1
:
center
.
x
,
y1
:
center
.
y
,
x2
:
point
.
x
,
y2
:
point
.
y
,
stroke
:
cfg
.
color
,
lineWidth
:
2
,
lineCap
:
'
round
'
,
},
});
return
group
.
addShape
(
'
circle
'
,
{
attrs
:
{
x
:
center
.
x
,
y
:
center
.
y
,
r
:
6
,
stroke
:
cfg
.
color
,
lineWidth
:
3
,
fill
:
'
#fff
'
,
},
});
},
});
class
Gauge
extends
React
.
Component
<
IGaugeProps
>
{
render
()
{
const
{
title
,
height
=
1
,
percent
,
forceFit
=
true
,
formatter
=
defaultFormatter
,
color
=
'
#2F9CFF
'
,
bgColor
=
'
#F0F2F5
'
,
}
=
this
.
props
;
const
cols
=
{
value
:
{
type
:
'
linear
'
,
min
:
0
,
max
:
10
,
tickCount
:
6
,
nice
:
true
,
},
};
const
renderHtml
=
()
=>
`
<div style="width: 300px;text-align: center;font-size: 12px!important;">
<p style="font-size: 14px; color: rgba(0,0,0,0.43);margin: 0;">
${
title
}
</p>
<p style="font-size: 24px;color: rgba(0,0,0,0.85);margin: 0;">
${(
data
[
0
].
value
*
10
).
toFixed
(
2
)}
%
</p>
</div>`
;
const
data
=
[{
value
:
percent
/
10
}];
const
textStyle
:
{
fontSize
:
number
;
fill
:
string
;
textAlign
:
'
center
'
;
}
=
{
fontSize
:
12
,
fill
:
'
rgba(0, 0, 0, 0.65)
'
,
textAlign
:
'
center
'
,
};
return
(
<
Chart
height
=
{
height
}
data
=
{
data
}
scale
=
{
cols
}
padding
=
{
[
-
16
,
0
,
16
,
0
]
}
forceFit
=
{
forceFit
}
>
<
Coord
type
=
"polar"
startAngle
=
{
-
1.25
*
Math
.
PI
}
endAngle
=
{
0.25
*
Math
.
PI
}
radius
=
{
0.8
}
/>
<
Axis
name
=
"1"
line
=
{
undefined
}
/>
<
Axis
line
=
{
undefined
}
tickLine
=
{
undefined
}
subTickLine
=
{
undefined
}
name
=
"value"
zIndex
=
{
2
}
label
=
{
{
offset
:
-
12
,
formatter
,
textStyle
:
textStyle
,
}
}
/>
<
Guide
>
<
Line
start
=
{
[
3
,
0.905
]
}
end
=
{
[
3
,
0.85
]
}
lineStyle
=
{
{
stroke
:
color
,
lineDash
:
undefined
,
lineWidth
:
2
,
}
}
/>
<
Line
start
=
{
[
5
,
0.905
]
}
end
=
{
[
5
,
0.85
]
}
lineStyle
=
{
{
stroke
:
color
,
lineDash
:
undefined
,
lineWidth
:
3
,
}
}
/>
<
Line
start
=
{
[
7
,
0.905
]
}
end
=
{
[
7
,
0.85
]
}
lineStyle
=
{
{
stroke
:
color
,
lineDash
:
undefined
,
lineWidth
:
3
,
}
}
/>
<
Arc
start
=
{
[
0
,
0.965
]
}
end
=
{
[
10
,
0.965
]
}
style
=
{
{
stroke
:
bgColor
,
lineWidth
:
10
,
}
}
/>
<
Arc
start
=
{
[
0
,
0.965
]
}
end
=
{
[
data
[
0
].
value
,
0.965
]
}
style
=
{
{
stroke
:
color
,
lineWidth
:
10
,
}
}
/>
<
Html
position
=
{
[
'
50%
'
,
'
95%
'
]
}
html
=
{
renderHtml
()
}
/>
</
Guide
>
<
Geom
line
=
{
false
}
type
=
"point"
position
=
"value*1"
shape
=
"pointer"
color
=
{
color
}
active
=
{
false
}
/>
</
Chart
>
);
}
}
export
default
autoHeight
()(
Gauge
);
Analysis/src/components/Charts/MiniArea/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Chart
,
Axis
,
Tooltip
,
Geom
}
from
'
bizcharts
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
../index.less
'
;
export
interface
IAxis
{
title
:
any
;
line
:
any
;
gridAlign
:
any
;
labels
:
any
;
tickLine
:
any
;
grid
:
any
;
}
export
interface
IMiniAreaProps
{
color
?:
string
;
height
?:
number
;
borderColor
?:
string
;
line
?:
boolean
;
animate
?:
boolean
;
xAxis
?:
IAxis
;
forceFit
?:
boolean
;
scale
?:
{
x
:
any
;
y
:
any
};
yAxis
?:
IAxis
;
borderWidth
?:
number
;
data
:
Array
<
{
x
:
number
|
string
;
y
:
number
;
}
>
;
}
class
MiniArea
extends
React
.
Component
<
IMiniAreaProps
>
{
render
()
{
const
{
height
=
1
,
data
=
[],
forceFit
=
true
,
color
=
'
rgba(24, 144, 255, 0.2)
'
,
borderColor
=
'
#1089ff
'
,
scale
=
{
x
:
{},
y
:
{}
},
borderWidth
=
2
,
line
,
xAxis
,
yAxis
,
animate
=
true
,
}
=
this
.
props
;
const
padding
:
[
number
,
number
,
number
,
number
]
=
[
36
,
5
,
30
,
5
];
const
scaleProps
=
{
x
:
{
type
:
'
cat
'
,
range
:
[
0
,
1
],
...
scale
!
.
x
,
},
y
:
{
min
:
0
,
...
scale
!
.
y
,
},
};
const
tooltip
:
[
string
,
(...
args
:
any
[])
=>
{
name
?:
string
;
value
:
string
}]
=
[
'
x*y
'
,
(
x
:
string
,
y
:
string
)
=>
({
name
:
x
,
value
:
y
,
}),
];
const
chartHeight
=
height
+
54
;
return
(
<
div
className
=
{
styles
.
miniChart
}
style
=
{
{
height
}
}
>
<
div
className
=
{
styles
.
chartContent
}
>
{
height
>
0
&&
(
<
Chart
animate
=
{
animate
}
scale
=
{
scaleProps
}
height
=
{
chartHeight
}
forceFit
=
{
forceFit
}
data
=
{
data
}
padding
=
{
padding
}
>
<
Axis
key
=
"axis-x"
name
=
"x"
label
=
{
false
}
line
=
{
false
}
tickLine
=
{
false
}
grid
=
{
false
}
{
...
xAxis
}
/>
<
Axis
key
=
"axis-y"
name
=
"y"
label
=
{
false
}
line
=
{
false
}
tickLine
=
{
false
}
grid
=
{
false
}
{
...
yAxis
}
/>
<
Tooltip
showTitle
=
{
false
}
crosshairs
=
{
false
}
/>
<
Geom
type
=
"area"
position
=
"x*y"
color
=
{
color
}
tooltip
=
{
tooltip
}
shape
=
"smooth"
style
=
{
{
fillOpacity
:
1
,
}
}
/>
{
line
?
(
<
Geom
type
=
"line"
position
=
"x*y"
shape
=
"smooth"
color
=
{
borderColor
}
size
=
{
borderWidth
}
tooltip
=
{
false
}
/>
)
:
(
<
span
style
=
{
{
display
:
'
none
'
}
}
/>
)
}
</
Chart
>
)
}
</
div
>
</
div
>
);
}
}
export
default
autoHeight
()(
MiniArea
);
Analysis/src/components/Charts/MiniBar/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Chart
,
Tooltip
,
Geom
}
from
'
bizcharts
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
../index.less
'
;
export
interface
IMiniBarProps
{
color
?:
string
;
height
?:
number
;
data
:
Array
<
{
x
:
number
|
string
;
y
:
number
;
}
>
;
forceFit
?:
boolean
;
style
?:
React
.
CSSProperties
;
}
class
MiniBar
extends
React
.
Component
<
IMiniBarProps
>
{
render
()
{
const
{
height
=
0
,
forceFit
=
true
,
color
=
'
#1890FF
'
,
data
=
[]
}
=
this
.
props
;
const
scale
=
{
x
:
{
type
:
'
cat
'
,
},
y
:
{
min
:
0
,
},
};
const
padding
:
[
number
,
number
,
number
,
number
]
=
[
36
,
5
,
30
,
5
];
const
tooltip
:
[
string
,
(...
args
:
any
[])
=>
{
name
?:
string
;
value
:
string
}]
=
[
'
x*y
'
,
(
x
:
string
,
y
:
string
)
=>
({
name
:
x
,
value
:
y
,
}),
];
// for tooltip not to be hide
const
chartHeight
=
height
+
54
;
return
(
<
div
className
=
{
styles
.
miniChart
}
style
=
{
{
height
}
}
>
<
div
className
=
{
styles
.
chartContent
}
>
<
Chart
scale
=
{
scale
}
height
=
{
chartHeight
}
forceFit
=
{
forceFit
}
data
=
{
data
}
padding
=
{
padding
}
>
<
Tooltip
showTitle
=
{
false
}
crosshairs
=
{
false
}
/>
<
Geom
type
=
"interval"
position
=
"x*y"
color
=
{
color
}
tooltip
=
{
tooltip
}
/>
</
Chart
>
</
div
>
</
div
>
);
}
}
export
default
autoHeight
()(
MiniBar
);
Analysis/src/components/Charts/MiniProgress/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.miniProgress {
position: relative;
width: 100%;
padding: 5px 0;
.progressWrap {
position: relative;
background-color: @background-color-base;
}
.progress {
width: 0;
height: 100%;
background-color: @primary-color;
border-radius: 1px 0 0 1px;
transition: all 0.4s cubic-bezier(0.08, 0.82, 0.17, 1) 0s;
}
.target {
position: absolute;
top: 0;
bottom: 0;
z-index: 9;
width: 20px;
span {
position: absolute;
top: 0;
left: 0;
width: 2px;
height: 4px;
border-radius: 100px;
}
span:last-child {
top: auto;
bottom: 0;
}
}
}
Analysis/src/components/Charts/MiniProgress/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Tooltip
}
from
'
antd
'
;
import
styles
from
'
./index.less
'
;
export
interface
IMiniProgressProps
{
target
:
number
;
targetLabel
?:
string
;
color
?:
string
;
strokeWidth
?:
number
;
percent
?:
number
;
style
?:
React
.
CSSProperties
;
}
const
MiniProgress
:
React
.
SFC
<
IMiniProgressProps
>
=
({
targetLabel
,
target
,
color
=
'
rgb(19, 194, 194)
'
,
strokeWidth
,
percent
,
})
=>
{
return
(
<
div
className
=
{
styles
.
miniProgress
}
>
<
Tooltip
title
=
{
targetLabel
}
>
<
div
className
=
{
styles
.
target
}
style
=
{
{
left
:
target
?
`
${
target
}
%`
:
undefined
}
}
>
<
span
style
=
{
{
backgroundColor
:
color
||
undefined
}
}
/>
<
span
style
=
{
{
backgroundColor
:
color
||
undefined
}
}
/>
</
div
>
</
Tooltip
>
<
div
className
=
{
styles
.
progressWrap
}
>
<
div
className
=
{
styles
.
progress
}
style
=
{
{
backgroundColor
:
color
||
undefined
,
width
:
percent
?
`
${
percent
}
%`
:
undefined
,
height
:
strokeWidth
||
undefined
,
}
}
/>
</
div
>
</
div
>
);
};
export
default
MiniProgress
;
Analysis/src/components/Charts/Pie/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.pie {
position: relative;
.chart {
position: relative;
}
&.hasLegend .chart {
width: ~'calc(100% - 240px)';
}
.legend {
position: absolute;
top: 50%;
right: 0;
min-width: 200px;
margin: 0 20px;
padding: 0;
list-style: none;
transform: translateY(-50%);
li {
height: 22px;
margin-bottom: 16px;
line-height: 22px;
cursor: pointer;
&:last-child {
margin-bottom: 0;
}
}
}
.dot {
position: relative;
top: -1px;
display: inline-block;
width: 8px;
height: 8px;
margin-right: 8px;
border-radius: 8px;
}
.line {
display: inline-block;
width: 1px;
height: 16px;
margin-right: 8px;
background-color: @border-color-split;
}
.legendTitle {
color: @text-color;
}
.percent {
color: @text-color-secondary;
}
.value {
position: absolute;
right: 0;
}
.title {
margin-bottom: 8px;
}
.total {
position: absolute;
top: 50%;
left: 50%;
max-height: 62px;
text-align: center;
transform: translate(-50%, -50%);
& > h4 {
height: 22px;
margin-bottom: 8px;
color: @text-color-secondary;
font-weight: normal;
font-size: 14px;
line-height: 22px;
}
& > p {
display: block;
height: 32px;
color: @heading-color;
font-size: 1.2em;
line-height: 32px;
white-space: nowrap;
}
}
}
.legendBlock {
&.hasLegend .chart {
width: 100%;
margin: 0 0 32px 0;
}
.legend {
position: relative;
transform: none;
}
}
Analysis/src/components/Charts/Pie/index.tsx
0 → 100644
View file @
278a1ce9
import
React
,
{
Component
}
from
'
react
'
;
import
{
Chart
,
Tooltip
,
Geom
,
Coord
}
from
'
bizcharts
'
;
import
{
DataView
}
from
'
@antv/data-set
'
;
import
{
Divider
}
from
'
antd
'
;
import
classNames
from
'
classnames
'
;
import
ReactFitText
from
'
react-fittext
'
;
import
Debounce
from
'
lodash-decorators/debounce
'
;
import
Bind
from
'
lodash-decorators/bind
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
./index.less
'
;
export
interface
IPieProps
{
animate
?:
boolean
;
color
?:
string
;
colors
?:
string
[];
selected
?:
boolean
;
height
?:
number
;
margin
?:
[
number
,
number
,
number
,
number
];
hasLegend
?:
boolean
;
padding
?:
[
number
,
number
,
number
,
number
];
percent
?:
number
;
data
?:
Array
<
{
x
:
string
|
string
;
y
:
number
;
}
>
;
inner
?:
number
;
lineWidth
?:
number
;
forceFit
?:
boolean
;
style
?:
React
.
CSSProperties
;
className
?:
string
;
total
?:
React
.
ReactNode
|
number
|
(()
=>
React
.
ReactNode
|
number
);
title
?:
React
.
ReactNode
;
tooltip
?:
boolean
;
valueFormat
?:
(
value
:
string
)
=>
string
|
React
.
ReactNode
;
subTitle
?:
React
.
ReactNode
;
}
/* eslint react/no-danger:0 */
interface
IPieState
{
legendData
:
Array
<
{
checked
:
boolean
;
x
:
string
;
color
:
string
;
percent
:
number
;
y
:
string
}
>
;
legendBlock
:
boolean
;
}
class
Pie
extends
Component
<
IPieProps
,
IPieState
>
{
state
:
IPieState
=
{
legendData
:
[],
legendBlock
:
false
,
};
requestRef
:
number
|
undefined
;
componentDidMount
()
{
window
.
addEventListener
(
'
resize
'
,
()
=>
{
this
.
requestRef
=
requestAnimationFrame
(()
=>
this
.
resize
());
},
{
passive
:
true
}
);
}
componentDidUpdate
(
preProps
:
IPieProps
)
{
const
{
data
}
=
this
.
props
;
if
(
data
!==
preProps
.
data
)
{
// because of charts data create when rendered
// so there is a trick for get rendered time
this
.
getLegendData
();
}
}
componentWillUnmount
()
{
if
(
this
.
requestRef
)
{
window
.
cancelAnimationFrame
(
this
.
requestRef
);
}
window
.
removeEventListener
(
'
resize
'
,
this
.
resize
);
(
this
.
resize
as
any
).
cancel
();
}
getG2Instance
=
(
chart
:
G2
.
Chart
)
=>
{
this
.
chart
=
chart
;
requestAnimationFrame
(()
=>
{
this
.
getLegendData
();
this
.
resize
();
});
};
root
!
:
HTMLDivElement
;
chart
:
G2
.
Chart
|
undefined
;
// for custom lengend view
getLegendData
=
()
=>
{
if
(
!
this
.
chart
)
return
;
const
geom
=
this
.
chart
.
getAllGeoms
()[
0
];
// 获取所有的图形
if
(
!
geom
)
return
;
const
items
=
geom
.
get
(
'
dataArray
'
)
||
[];
// 获取图形对应的
const
legendData
=
items
.
map
((
item
:
Array
<
any
>
)
=>
{
/* eslint no-underscore-dangle:0 */
const
origin
=
item
[
0
].
_origin
;
origin
.
color
=
item
[
0
].
color
;
origin
.
checked
=
true
;
return
origin
;
});
this
.
setState
({
legendData
,
});
};
handleRoot
=
(
n
:
HTMLDivElement
)
=>
{
this
.
root
=
n
;
};
handleLegendClick
=
(
item
:
any
,
i
:
string
|
number
)
=>
{
const
newItem
=
item
;
newItem
.
checked
=
!
newItem
.
checked
;
const
{
legendData
}
=
this
.
state
;
legendData
[
i
]
=
newItem
;
const
filteredLegendData
=
legendData
.
filter
(
l
=>
l
.
checked
).
map
(
l
=>
l
.
x
);
if
(
this
.
chart
)
{
this
.
chart
.
filter
(
'
x
'
,
val
=>
filteredLegendData
.
indexOf
(
val
+
''
)
>
-
1
);
}
this
.
setState
({
legendData
,
});
};
// for window resize auto responsive legend
@
Bind
()
@
Debounce
(
300
)
resize
()
{
const
{
hasLegend
}
=
this
.
props
;
const
{
legendBlock
}
=
this
.
state
;
if
(
!
hasLegend
||
!
this
.
root
)
{
window
.
removeEventListener
(
'
resize
'
,
this
.
resize
);
return
;
}
if
((
this
.
root
!
.
parentNode
!
as
HTMLDivElement
).
clientWidth
<=
380
)
{
if
(
!
legendBlock
)
{
this
.
setState
({
legendBlock
:
true
,
});
}
}
else
if
(
legendBlock
)
{
this
.
setState
({
legendBlock
:
false
,
});
}
}
render
()
{
const
{
valueFormat
,
subTitle
,
total
,
hasLegend
=
false
,
className
,
style
,
height
=
1
,
forceFit
=
true
,
percent
,
inner
=
0.75
,
animate
=
true
,
lineWidth
=
1
,
}
=
this
.
props
;
const
{
legendData
,
legendBlock
}
=
this
.
state
;
const
pieClassName
=
classNames
(
styles
.
pie
,
className
,
{
[
styles
.
hasLegend
]:
!!
hasLegend
,
[
styles
.
legendBlock
]:
legendBlock
,
});
const
{
data
:
propsData
,
selected
:
propsSelected
=
true
,
tooltip
:
propsTooltip
=
true
,
}
=
this
.
props
;
let
data
=
propsData
||
[];
let
selected
=
propsSelected
;
let
tooltip
=
propsTooltip
;
data
=
data
||
[];
selected
=
selected
||
true
;
tooltip
=
tooltip
||
true
;
const
scale
=
{
x
:
{
type
:
'
cat
'
,
range
:
[
0
,
1
],
},
y
:
{
min
:
0
,
},
};
if
(
percent
||
percent
===
0
)
{
selected
=
false
;
tooltip
=
false
;
data
=
[
{
x
:
'
占比
'
,
y
:
parseFloat
(
percent
+
''
),
},
{
x
:
'
反比
'
,
y
:
100
-
parseFloat
(
percent
+
''
),
},
];
}
const
tooltipFormat
:
[
string
,
(...
args
:
any
[])
=>
{
name
?:
string
;
value
:
string
}]
=
[
'
x*percent
'
,
(
x
:
string
,
p
:
number
)
=>
({
name
:
x
,
value
:
`
${(
p
*
100
).
toFixed
(
2
)}
%`
,
}),
];
const
padding
:
[
number
,
number
,
number
,
number
]
=
[
12
,
0
,
12
,
0
];
const
dv
=
new
DataView
();
dv
.
source
(
data
).
transform
({
type
:
'
percent
'
,
field
:
'
y
'
,
dimension
:
'
x
'
,
as
:
'
percent
'
,
});
return
(
<
div
ref
=
{
this
.
handleRoot
}
className
=
{
pieClassName
}
style
=
{
style
}
>
<
ReactFitText
maxFontSize
=
{
25
}
>
<
div
className
=
{
styles
.
chart
}
>
<
Chart
scale
=
{
scale
}
height
=
{
height
}
forceFit
=
{
forceFit
}
data
=
{
dv
}
padding
=
{
padding
}
animate
=
{
animate
}
onGetG2Instance
=
{
this
.
getG2Instance
}
>
{
!!
tooltip
&&
<
Tooltip
showTitle
=
{
false
}
/>
}
<
Coord
type
=
"theta"
innerRadius
=
{
inner
}
/>
<
Geom
style
=
{
{
lineWidth
,
stroke
:
'
#fff
'
}
}
tooltip
=
{
tooltip
&&
tooltipFormat
}
type
=
"intervalStack"
position
=
"percent"
selected
=
{
selected
}
/>
</
Chart
>
{
(
subTitle
||
total
)
&&
(
<
div
className
=
{
styles
.
total
}
>
{
subTitle
&&
<
h4
className
=
"pie-sub-title"
>
{
subTitle
}
</
h4
>
}
{
/* eslint-disable-next-line */
}
{
total
&&
(
<
div
className
=
"pie-stat"
>
{
typeof
total
===
'
function
'
?
total
()
:
total
}
</
div
>
)
}
</
div
>
)
}
</
div
>
</
ReactFitText
>
{
hasLegend
&&
(
<
ul
className
=
{
styles
.
legend
}
>
{
legendData
.
map
((
item
,
i
)
=>
(
<
li
key
=
{
item
.
x
}
onClick
=
{
()
=>
this
.
handleLegendClick
(
item
,
i
)
}
>
<
span
className
=
{
styles
.
dot
}
style
=
{
{
backgroundColor
:
!
item
.
checked
?
'
#aaa
'
:
item
.
color
,
}
}
/>
<
span
className
=
{
styles
.
legendTitle
}
>
{
item
.
x
}
</
span
>
<
Divider
type
=
"vertical"
/>
<
span
className
=
{
styles
.
percent
}
>
{
`
${(
Number
.
isNaN
(
item
.
percent
)
?
0
:
item
.
percent
*
100
).
toFixed
(
2
)}
%`
}
</
span
>
<
span
className
=
{
styles
.
value
}
>
{
valueFormat
?
valueFormat
(
item
.
y
)
:
item
.
y
}
</
span
>
</
li
>
))
}
</
ul
>
)
}
</
div
>
);
}
}
export
default
autoHeight
()(
Pie
);
Analysis/src/components/Charts/Radar/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.radar {
.legend {
margin-top: 16px;
.legendItem {
position: relative;
color: @text-color-secondary;
line-height: 22px;
text-align: center;
cursor: pointer;
p {
margin: 0;
}
h6 {
margin-top: 4px;
margin-bottom: 0;
padding-left: 16px;
color: @heading-color;
font-size: 24px;
line-height: 32px;
}
&::after {
position: absolute;
top: 8px;
right: 0;
width: 1px;
height: 40px;
background-color: @border-color-split;
content: '';
}
}
> :last-child .legendItem::after {
display: none;
}
.dot {
position: relative;
top: -1px;
display: inline-block;
width: 6px;
height: 6px;
margin-right: 6px;
border-radius: 6px;
}
}
}
Analysis/src/components/Charts/Radar/index.tsx
0 → 100644
View file @
278a1ce9
import
React
,
{
Component
}
from
'
react
'
;
import
{
Chart
,
G2
,
Tooltip
,
Geom
,
Coord
,
Axis
}
from
'
bizcharts
'
;
import
{
Row
,
Col
}
from
'
antd
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
./index.less
'
;
export
interface
IRadarProps
{
title
?:
React
.
ReactNode
;
height
?:
number
;
padding
?:
[
number
,
number
,
number
,
number
];
hasLegend
?:
boolean
;
data
:
Array
<
{
name
:
string
;
label
:
string
;
value
:
string
;
}
>
;
colors
?:
string
[];
animate
?:
boolean
;
forceFit
?:
boolean
;
tickCount
?:
number
;
style
?:
React
.
CSSProperties
;
}
interface
IRadarState
{
legendData
:
Array
<
{
checked
:
boolean
;
name
:
string
;
color
:
string
;
percent
:
number
;
value
:
string
;
}
>
;
}
/* eslint react/no-danger:0 */
class
Radar
extends
Component
<
IRadarProps
,
IRadarState
>
{
state
:
IRadarState
=
{
legendData
:
[],
};
componentDidMount
()
{
this
.
getLegendData
();
}
componentDidUpdate
(
preProps
:
IRadarProps
)
{
const
{
data
}
=
this
.
props
;
if
(
data
!==
preProps
.
data
)
{
this
.
getLegendData
();
}
}
chart
:
G2
.
Chart
|
undefined
;
getG2Instance
=
(
chart
:
G2
.
Chart
)
=>
{
this
.
chart
=
chart
;
};
// for custom lengend view
getLegendData
=
()
=>
{
if
(
!
this
.
chart
)
return
;
const
geom
=
this
.
chart
.
getAllGeoms
()[
0
];
// 获取所有的图形
if
(
!
geom
)
return
;
const
items
=
geom
.
get
(
'
dataArray
'
)
||
[];
// 获取图形对应的
const
legendData
=
items
.
map
((
item
:
{
color
:
any
;
_origin
:
any
}[])
=>
{
// eslint-disable-next-line
const
origins
=
item
.
map
(
t
=>
t
.
_origin
);
const
result
=
{
name
:
origins
[
0
].
name
,
color
:
item
[
0
].
color
,
checked
:
true
,
value
:
origins
.
reduce
((
p
,
n
)
=>
p
+
n
.
value
,
0
),
};
return
result
;
});
this
.
setState
({
legendData
,
});
};
node
:
HTMLDivElement
|
undefined
;
handleRef
=
(
n
:
HTMLDivElement
)
=>
{
this
.
node
=
n
;
};
handleLegendClick
=
(
item
:
{
checked
:
boolean
;
name
:
string
;
},
i
:
string
|
number
)
=>
{
const
newItem
=
item
;
newItem
.
checked
=
!
newItem
.
checked
;
const
{
legendData
}
=
this
.
state
;
legendData
[
i
]
=
newItem
;
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
({
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
=
{
value
:
{
min
:
0
,
tickCount
,
},
};
const
chartHeight
=
height
-
(
hasLegend
?
80
:
22
);
return
(
<
div
className
=
{
styles
.
radar
}
style
=
{
{
height
}
}
>
{
title
&&
<
h4
>
{
title
}
</
h4
>
}
<
Chart
scale
=
{
scale
}
height
=
{
chartHeight
}
forceFit
=
{
forceFit
}
data
=
{
data
}
padding
=
{
padding
}
animate
=
{
animate
}
onGetG2Instance
=
{
this
.
getG2Instance
}
>
<
Tooltip
/>
<
Coord
type
=
"polar"
/>
<
Axis
name
=
"label"
line
=
{
undefined
}
tickLine
=
{
undefined
}
grid
=
{
{
lineStyle
:
{
lineDash
:
undefined
,
},
hideFirstLine
:
false
,
}
}
/>
<
Axis
name
=
"value"
grid
=
{
{
type
:
'
polygon
'
,
lineStyle
:
{
lineDash
:
undefined
,
},
}
}
/>
<
Geom
type
=
"line"
position
=
"label*value"
color
=
{
[
'
name
'
,
colors
]
}
size
=
{
1
}
/>
<
Geom
type
=
"point"
position
=
"label*value"
color
=
{
[
'
name
'
,
colors
]
}
shape
=
"circle"
size
=
{
3
}
/>
</
Chart
>
{
hasLegend
&&
(
<
Row
className
=
{
styles
.
legend
}
>
{
legendData
.
map
((
item
,
i
)
=>
(
<
Col
span
=
{
24
/
legendData
.
length
}
key
=
{
item
.
name
}
onClick
=
{
()
=>
this
.
handleLegendClick
(
item
,
i
)
}
>
<
div
className
=
{
styles
.
legendItem
}
>
<
p
>
<
span
className
=
{
styles
.
dot
}
style
=
{
{
backgroundColor
:
!
item
.
checked
?
'
#aaa
'
:
item
.
color
,
}
}
/>
<
span
>
{
item
.
name
}
</
span
>
</
p
>
<
h6
>
{
item
.
value
}
</
h6
>
</
div
>
</
Col
>
))
}
</
Row
>
)
}
</
div
>
);
}
}
export
default
autoHeight
()(
Radar
);
Analysis/src/components/Charts/TagCloud/index.less
0 → 100644
View file @
278a1ce9
.tagCloud {
overflow: hidden;
canvas {
transform-origin: 0 0;
}
}
Analysis/src/components/Charts/TagCloud/index.tsx
0 → 100644
View file @
278a1ce9
import
React
,
{
Component
}
from
'
react
'
;
import
{
Chart
,
Geom
,
Coord
,
Shape
,
Tooltip
}
from
'
bizcharts
'
;
import
DataSet
from
'
@antv/data-set
'
;
import
Debounce
from
'
lodash-decorators/debounce
'
;
import
Bind
from
'
lodash-decorators/bind
'
;
import
classNames
from
'
classnames
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
./index.less
'
;
/* eslint no-underscore-dangle: 0 */
/* eslint no-param-reassign: 0 */
const
imgUrl
=
'
https://gw.alipayobjects.com/zos/rmsportal/gWyeGLCdFFRavBGIDzWk.png
'
;
export
interface
ITagCloudProps
{
data
:
Array
<
{
name
:
string
;
value
:
number
;
}
>
;
height
?:
number
;
className
?:
string
;
style
?:
React
.
CSSProperties
;
}
interface
ITagCloudState
{
dv
:
any
;
height
?:
number
;
width
:
number
;
}
class
TagCloud
extends
Component
<
ITagCloudProps
,
ITagCloudState
>
{
state
=
{
dv
:
null
,
height
:
0
,
width
:
0
,
};
componentDidMount
()
{
requestAnimationFrame
(()
=>
{
this
.
initTagCloud
();
this
.
renderChart
(
this
.
props
);
});
window
.
addEventListener
(
'
resize
'
,
this
.
resize
,
{
passive
:
true
});
}
componentDidUpdate
(
preProps
?:
ITagCloudProps
)
{
const
{
data
}
=
this
.
props
;
if
(
preProps
&&
JSON
.
stringify
(
preProps
.
data
)
!==
JSON
.
stringify
(
data
))
{
this
.
renderChart
(
this
.
props
);
}
}
isUnmount
!
:
boolean
;
componentWillUnmount
()
{
this
.
isUnmount
=
true
;
window
.
cancelAnimationFrame
(
this
.
requestRef
);
window
.
removeEventListener
(
'
resize
'
,
this
.
resize
);
}
requestRef
!
:
number
;
resize
=
()
=>
{
this
.
requestRef
=
requestAnimationFrame
(()
=>
{
this
.
renderChart
(
this
.
props
);
});
};
root
:
HTMLDivElement
|
undefined
;
saveRootRef
=
(
node
:
HTMLDivElement
)
=>
{
this
.
root
=
node
;
};
initTagCloud
=
()
=>
{
function
getTextAttrs
(
cfg
:
{
x
?:
any
;
y
?:
any
;
style
?:
any
;
opacity
?:
any
;
origin
?:
any
;
color
?:
any
;
})
{
return
Object
.
assign
({},
cfg
.
style
,
{
fillOpacity
:
cfg
.
opacity
,
fontSize
:
cfg
.
origin
.
_origin
.
size
,
rotate
:
cfg
.
origin
.
_origin
.
rotate
,
text
:
cfg
.
origin
.
_origin
.
text
,
textAlign
:
'
center
'
,
fontFamily
:
cfg
.
origin
.
_origin
.
font
,
fill
:
cfg
.
color
,
textBaseline
:
'
Alphabetic
'
,
});
}
(
Shape
as
any
).
registerShape
(
'
point
'
,
'
cloud
'
,
{
drawShape
(
cfg
:
{
x
:
any
;
y
:
any
},
container
:
{
addShape
:
(
arg0
:
string
,
arg1
:
{
attrs
:
any
})
=>
void
}
)
{
const
attrs
=
getTextAttrs
(
cfg
);
return
container
.
addShape
(
'
text
'
,
{
attrs
:
Object
.
assign
(
attrs
,
{
x
:
cfg
.
x
,
y
:
cfg
.
y
,
}),
});
},
});
};
imageMask
:
HTMLImageElement
|
undefined
;
@
Bind
()
@
Debounce
(
500
)
renderChart
(
nextProps
:
ITagCloudProps
)
{
// const colors = ['#1890FF', '#41D9C7', '#2FC25B', '#FACC14', '#9AE65C'];
const
{
data
,
height
}
=
nextProps
||
this
.
props
;
if
(
data
.
length
<
1
||
!
this
.
root
)
{
return
;
}
const
h
=
height
;
const
w
=
this
.
root
.
offsetWidth
;
const
onload
=
()
=>
{
const
dv
=
new
DataSet
.
View
().
source
(
data
);
const
range
=
dv
.
range
(
'
value
'
);
const
[
min
,
max
]
=
range
;
dv
.
transform
({
type
:
'
tag-cloud
'
,
fields
:
[
'
name
'
,
'
value
'
],
imageMask
:
this
.
imageMask
,
font
:
'
Verdana
'
,
size
:
[
w
,
h
],
// 宽高设置最好根据 imageMask 做调整
padding
:
0
,
timeInterval
:
5000
,
// max execute time
rotate
()
{
return
0
;
},
fontSize
(
d
:
{
value
:
number
})
{
// eslint-disable-next-line
return
Math
.
pow
((
d
.
value
-
min
)
/
(
max
-
min
),
2
)
*
(
17.5
-
5
)
+
5
;
},
});
if
(
this
.
isUnmount
)
{
return
;
}
this
.
setState
({
dv
,
width
:
w
,
height
:
h
,
});
};
if
(
!
this
.
imageMask
)
{
this
.
imageMask
=
new
Image
();
this
.
imageMask
.
crossOrigin
=
''
;
this
.
imageMask
.
src
=
imgUrl
;
this
.
imageMask
.
onload
=
onload
;
}
else
{
onload
();
}
}
render
()
{
const
{
className
,
height
}
=
this
.
props
;
const
{
dv
,
width
,
height
:
stateHeight
}
=
this
.
state
;
return
(
<
div
className
=
{
classNames
(
styles
.
tagCloud
,
className
)
}
style
=
{
{
width
:
'
100%
'
,
height
}
}
ref
=
{
this
.
saveRootRef
}
>
{
dv
&&
(
<
Chart
width
=
{
width
}
height
=
{
stateHeight
}
data
=
{
dv
}
padding
=
{
0
}
scale
=
{
{
x
:
{
nice
:
false
},
y
:
{
nice
:
false
},
}
}
>
<
Tooltip
showTitle
=
{
false
}
/>
<
Coord
reflect
=
"y"
/>
<
Geom
type
=
"point"
position
=
"x*y"
color
=
"text"
shape
=
"cloud"
tooltip
=
{
[
'
text*value
'
,
function
trans
(
text
,
value
)
{
return
{
name
:
text
,
value
};
},
]
}
/>
</
Chart
>
)
}
</
div
>
);
}
}
export
default
autoHeight
()(
TagCloud
);
Analysis/src/components/Charts/TimelineChart/index.less
0 → 100644
View file @
278a1ce9
.timelineChart {
background: #fff;
}
Analysis/src/components/Charts/TimelineChart/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Chart
,
Tooltip
,
Geom
,
Legend
,
Axis
}
from
'
bizcharts
'
;
import
DataSet
from
'
@antv/data-set
'
;
import
Slider
from
'
bizcharts-plugin-slider
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
./index.less
'
;
export
interface
ITimelineChartProps
{
data
:
Array
<
{
x
:
number
;
y1
:
number
;
y2
:
number
;
}
>
;
title
?:
string
;
titleMap
:
{
y1
:
string
;
y2
:
string
};
padding
?:
[
number
,
number
,
number
,
number
];
height
?:
number
;
style
?:
React
.
CSSProperties
;
borderWidth
?:
number
;
}
class
TimelineChart
extends
React
.
Component
<
ITimelineChartProps
>
{
render
()
{
const
{
title
,
height
=
400
,
padding
=
[
60
,
20
,
40
,
40
]
as
[
number
,
number
,
number
,
number
],
titleMap
=
{
y1
:
'
y1
'
,
y2
:
'
y2
'
,
},
borderWidth
=
2
,
data
:
sourceData
,
}
=
this
.
props
;
const
data
=
Array
.
isArray
(
sourceData
)
?
sourceData
:
[{
x
:
0
,
y1
:
0
,
y2
:
0
}];
data
.
sort
((
a
,
b
)
=>
a
.
x
-
b
.
x
);
let
max
;
if
(
data
[
0
]
&&
data
[
0
].
y1
&&
data
[
0
].
y2
)
{
max
=
Math
.
max
(
[...
data
].
sort
((
a
,
b
)
=>
b
.
y1
-
a
.
y1
)[
0
].
y1
,
[...
data
].
sort
((
a
,
b
)
=>
b
.
y2
-
a
.
y2
)[
0
].
y2
);
}
const
ds
=
new
DataSet
({
state
:
{
start
:
data
[
0
].
x
,
end
:
data
[
data
.
length
-
1
].
x
,
},
});
const
dv
=
ds
.
createView
();
dv
.
source
(
data
)
.
transform
({
type
:
'
filter
'
,
callback
:
(
obj
:
{
x
:
string
})
=>
{
const
date
=
obj
.
x
;
return
date
<=
ds
.
state
.
end
&&
date
>=
ds
.
state
.
start
;
},
})
.
transform
({
type
:
'
map
'
,
callback
(
row
:
{
y1
:
string
;
y2
:
string
})
{
const
newRow
=
{
...
row
};
newRow
[
titleMap
.
y1
]
=
row
.
y1
;
newRow
[
titleMap
.
y2
]
=
row
.
y2
;
return
newRow
;
},
})
.
transform
({
type
:
'
fold
'
,
fields
:
[
titleMap
.
y1
,
titleMap
.
y2
],
// 展开字段集
key
:
'
key
'
,
// key字段
value
:
'
value
'
,
// value字段
});
const
timeScale
=
{
type
:
'
time
'
,
tickInterval
:
60
*
60
*
1000
,
mask
:
'
HH:mm
'
,
range
:
[
0
,
1
],
};
const
cols
=
{
x
:
timeScale
,
value
:
{
max
,
min
:
0
,
},
};
const
SliderGen
=
()
=>
(
<
Slider
padding
=
{
[
0
,
padding
[
1
]
+
20
,
0
,
padding
[
3
]]
}
width
=
"auto"
height
=
{
26
}
xAxis
=
"x"
yAxis
=
"y1"
scales
=
{
{
x
:
timeScale
}
}
data
=
{
data
}
start
=
{
ds
.
state
.
start
}
end
=
{
ds
.
state
.
end
}
backgroundChart
=
{
{
type
:
'
line
'
}
}
onChange
=
{
({
startValue
,
endValue
}:
{
startValue
:
string
;
endValue
:
string
})
=>
{
ds
.
setState
(
'
start
'
,
startValue
);
ds
.
setState
(
'
end
'
,
endValue
);
}
}
/>
);
return
(
<
div
className
=
{
styles
.
timelineChart
}
style
=
{
{
height
:
height
+
30
}
}
>
<
div
>
{
title
&&
<
h4
>
{
title
}
</
h4
>
}
<
Chart
height
=
{
height
}
padding
=
{
padding
}
data
=
{
dv
}
scale
=
{
cols
}
forceFit
>
<
Axis
name
=
"x"
/>
<
Tooltip
/>
<
Legend
name
=
"key"
position
=
"top"
/>
<
Geom
type
=
"line"
position
=
"x*value"
size
=
{
borderWidth
}
color
=
"key"
/>
</
Chart
>
<
div
style
=
{
{
marginRight
:
-
20
}
}
>
<
SliderGen
/>
</
div
>
</
div
>
</
div
>
);
}
}
export
default
autoHeight
()(
TimelineChart
);
Analysis/src/components/Charts/WaterWave/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.waterWave {
position: relative;
display: inline-block;
transform-origin: left;
.text {
position: absolute;
top: 32px;
left: 0;
width: 100%;
text-align: center;
span {
color: @text-color-secondary;
font-size: 14px;
line-height: 22px;
}
h4 {
color: @heading-color;
font-size: 24px;
line-height: 32px;
}
}
.waterWaveCanvasWrapper {
transform: scale(0.5);
transform-origin: 0 0;
}
}
Analysis/src/components/Charts/WaterWave/index.tsx
0 → 100644
View file @
278a1ce9
import
React
,
{
Component
}
from
'
react
'
;
import
autoHeight
from
'
../autoHeight
'
;
import
styles
from
'
./index.less
'
;
/* eslint no-return-assign: 0 */
/* eslint no-mixed-operators: 0 */
// riddle: https://riddle.alibaba-inc.com/riddles/2d9a4b90
export
interface
IWaterWaveProps
{
title
:
React
.
ReactNode
;
color
?:
string
;
height
?:
number
;
percent
:
number
;
style
?:
React
.
CSSProperties
;
}
class
WaterWave
extends
Component
<
IWaterWaveProps
>
{
state
=
{
radio
:
1
,
};
componentDidMount
()
{
this
.
renderChart
();
this
.
resize
();
window
.
addEventListener
(
'
resize
'
,
()
=>
{
requestAnimationFrame
(()
=>
this
.
resize
());
},
{
passive
:
true
}
);
}
componentDidUpdate
(
props
:
IWaterWaveProps
)
{
const
{
percent
}
=
this
.
props
;
if
(
props
.
percent
!==
percent
)
{
// 不加这个会造成绘制缓慢
this
.
renderChart
(
'
update
'
);
}
}
componentWillUnmount
()
{
cancelAnimationFrame
(
this
.
timer
);
if
(
this
.
node
)
{
this
.
node
.
innerHTML
=
''
;
}
window
.
removeEventListener
(
'
resize
'
,
this
.
resize
);
}
resize
=
()
=>
{
if
(
this
.
root
)
{
const
{
height
=
1
}
=
this
.
props
;
const
{
offsetWidth
}
=
this
.
root
.
parentNode
as
HTMLElement
;
this
.
setState
({
radio
:
offsetWidth
<
height
?
offsetWidth
/
height
:
1
,
});
}
};
timer
:
number
=
0
;
renderChart
(
type
?:
string
)
{
const
{
percent
,
color
=
'
#1890FF
'
}
=
this
.
props
;
const
data
=
percent
/
100
;
const
self
=
this
;
cancelAnimationFrame
(
this
.
timer
);
if
(
!
this
.
node
||
(
data
!==
0
&&
!
data
))
{
return
;
}
const
canvas
=
this
.
node
;
const
ctx
=
canvas
.
getContext
(
'
2d
'
);
if
(
!
ctx
)
{
return
;
}
const
canvasWidth
=
canvas
.
width
;
const
canvasHeight
=
canvas
.
height
;
const
radius
=
canvasWidth
/
2
;
const
lineWidth
=
2
;
const
cR
=
radius
-
lineWidth
;
ctx
.
beginPath
();
ctx
.
lineWidth
=
lineWidth
*
2
;
const
axisLength
=
canvasWidth
-
lineWidth
;
const
unit
=
axisLength
/
8
;
const
range
=
0.2
;
// 振幅
let
currRange
=
range
;
const
xOffset
=
lineWidth
;
let
sp
=
0
;
// 周期偏移量
let
currData
=
0
;
const
waveupsp
=
0.005
;
// 水波上涨速度
let
arcStack
:
number
[][]
=
[];
const
bR
=
radius
-
lineWidth
;
const
circleOffset
=
-
(
Math
.
PI
/
2
);
let
circleLock
=
true
;
for
(
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
)]);
}
const
cStartPoint
=
arcStack
.
shift
()
as
number
[];
ctx
.
strokeStyle
=
color
;
ctx
.
moveTo
(
cStartPoint
[
0
],
cStartPoint
[
1
]);
function
drawSin
()
{
if
(
!
ctx
)
{
return
;
}
ctx
.
beginPath
();
ctx
.
save
();
const
sinStack
=
[];
for
(
let
i
=
xOffset
;
i
<=
xOffset
+
axisLength
;
i
+=
20
/
axisLength
)
{
const
x
=
sp
+
(
xOffset
+
i
)
/
unit
;
const
y
=
Math
.
sin
(
x
)
*
currRange
;
const
dx
=
i
;
const
dy
=
2
*
cR
*
(
1
-
currData
)
+
(
radius
-
cR
)
-
unit
*
y
;
ctx
.
lineTo
(
dx
,
dy
);
sinStack
.
push
([
dx
,
dy
]);
}
const
startPoint
=
sinStack
.
shift
()
as
number
[];
ctx
.
lineTo
(
xOffset
+
axisLength
,
canvasHeight
);
ctx
.
lineTo
(
xOffset
,
canvasHeight
);
ctx
.
lineTo
(
startPoint
[
0
],
startPoint
[
1
]);
const
gradient
=
ctx
.
createLinearGradient
(
0
,
0
,
0
,
canvasHeight
);
gradient
.
addColorStop
(
0
,
'
#ffffff
'
);
gradient
.
addColorStop
(
1
,
color
);
ctx
.
fillStyle
=
gradient
;
ctx
.
fill
();
ctx
.
restore
();
}
function
render
()
{
if
(
!
ctx
)
{
return
;
}
ctx
.
clearRect
(
0
,
0
,
canvasWidth
,
canvasHeight
);
if
(
circleLock
&&
type
!==
'
update
'
)
{
if
(
arcStack
.
length
)
{
const
temp
=
arcStack
.
shift
()
as
number
[];
ctx
.
lineTo
(
temp
[
0
],
temp
[
1
]);
ctx
.
stroke
();
}
else
{
circleLock
=
false
;
ctx
.
lineTo
(
cStartPoint
[
0
],
cStartPoint
[
1
]);
ctx
.
stroke
();
arcStack
=
[];
ctx
.
globalCompositeOperation
=
'
destination-over
'
;
ctx
.
beginPath
();
ctx
.
lineWidth
=
lineWidth
;
ctx
.
arc
(
radius
,
radius
,
bR
,
0
,
2
*
Math
.
PI
,
true
);
ctx
.
beginPath
();
ctx
.
save
();
ctx
.
arc
(
radius
,
radius
,
radius
-
3
*
lineWidth
,
0
,
2
*
Math
.
PI
,
true
);
ctx
.
restore
();
ctx
.
clip
();
ctx
.
fillStyle
=
color
;
}
}
else
{
if
(
data
>=
0.85
)
{
if
(
currRange
>
range
/
4
)
{
const
t
=
range
*
0.01
;
currRange
-=
t
;
}
}
else
if
(
data
<=
0.1
)
{
if
(
currRange
<
range
*
1.5
)
{
const
t
=
range
*
0.01
;
currRange
+=
t
;
}
}
else
{
if
(
currRange
<=
range
)
{
const
t
=
range
*
0.01
;
currRange
+=
t
;
}
if
(
currRange
>=
range
)
{
const
t
=
range
*
0.01
;
currRange
-=
t
;
}
}
if
(
data
-
currData
>
0
)
{
currData
+=
waveupsp
;
}
if
(
data
-
currData
<
0
)
{
currData
-=
waveupsp
;
}
sp
+=
0.07
;
drawSin
();
}
self
.
timer
=
requestAnimationFrame
(
render
);
}
render
();
}
root
:
HTMLDivElement
|
undefined
|
null
;
node
:
HTMLCanvasElement
|
undefined
|
null
;
render
()
{
const
{
radio
}
=
this
.
state
;
const
{
percent
,
title
,
height
=
1
}
=
this
.
props
;
return
(
<
div
className
=
{
styles
.
waterWave
}
ref
=
{
n
=>
(
this
.
root
=
n
)
}
style
=
{
{
transform
:
`scale(
${
radio
}
)`
}
}
>
<
div
style
=
{
{
width
:
height
,
height
,
overflow
:
'
hidden
'
}
}
>
<
canvas
className
=
{
styles
.
waterWaveCanvasWrapper
}
ref
=
{
n
=>
(
this
.
node
=
n
)
}
width
=
{
height
*
2
}
height
=
{
height
*
2
}
/>
</
div
>
<
div
className
=
{
styles
.
text
}
style
=
{
{
width
:
height
}
}
>
{
title
&&
<
span
>
{
title
}
</
span
>
}
<
h4
>
{
percent
}
%
</
h4
>
</
div
>
</
div
>
);
}
}
export
default
autoHeight
()(
WaterWave
);
Analysis/src/components/Charts/autoHeight.tsx
0 → 100644
View file @
278a1ce9
/* eslint eqeqeq: 0 */
import
React
from
'
react
'
;
export
type
IReactComponent
<
P
=
any
>
=
|
React
.
StatelessComponent
<
P
>
|
React
.
ComponentClass
<
P
>
|
React
.
ClassicComponentClass
<
P
>
;
function
computeHeight
(
node
:
HTMLDivElement
)
{
const
totalHeight
=
parseInt
(
getComputedStyle
(
node
).
height
+
''
,
10
);
const
padding
=
parseInt
(
getComputedStyle
(
node
).
paddingTop
+
''
,
10
)
+
parseInt
(
getComputedStyle
(
node
).
paddingBottom
+
''
,
10
);
return
totalHeight
-
padding
;
}
function
getAutoHeight
(
n
:
HTMLDivElement
)
{
if
(
!
n
)
{
return
0
;
}
let
node
=
n
;
let
height
=
computeHeight
(
node
);
while
(
!
height
)
{
const
parentNode
=
node
.
parentNode
as
HTMLDivElement
;
if
(
parentNode
)
{
height
=
computeHeight
(
parentNode
);
}
else
{
break
;
}
}
return
height
;
}
interface
IAutoHeightProps
{
height
?:
number
;
}
function
autoHeight
()
{
return
function
<
P
extends
IAutoHeightProps
>
(
WrappedComponent: React.ComponentClass
<
P
>
| React.SFC
<
P
>
): React.ComponentClass
<
P
>
{
class
AutoHeightComponent
extends
React
.
Component
<
P
&
IAutoHeightProps
>
{
state
=
{
computedHeight
:
0
,
};
root
!
:
HTMLDivElement
;
componentDidMount
()
{
const
{
height
}
=
this
.
props
;
if
(
!
height
)
{
const
h
=
getAutoHeight
(
this
.
root
);
// eslint-disable-next-line
this
.
setState
({
computedHeight
:
h
});
if
(
h
<
1
)
{
const
h
=
getAutoHeight
(
this
.
root
);
this
.
setState
({
computedHeight
:
h
});
}
}
}
handleRoot
=
(
node
:
HTMLDivElement
)
=>
{
this
.
root
=
node
;
};
render
()
{
const
{
height
}
=
this
.
props
;
const
{
computedHeight
}
=
this
.
state
;
const
h
=
height
||
computedHeight
;
return
(
<
div
ref
=
{
this
.
handleRoot
}
>
{
h
>
0
&&
<
WrappedComponent
{
...
this
.
props
}
height
=
{
h
}
/>
}
</
div
>
);
}
}
return
AutoHeightComponent
;
}
;
}
export default autoHeight;
Analysis/src/components/Charts/bizcharts.d.ts
0 → 100644
View file @
278a1ce9
import
*
as
BizChart
from
'
bizcharts
'
;
export
=
BizChart
;
Analysis/src/components/Charts/bizcharts.tsx
0 → 100644
View file @
278a1ce9
import
*
as
BizChart
from
'
bizcharts
'
;
export
default
BizChart
;
Analysis/src/components/Charts/index.less
0 → 100644
View file @
278a1ce9
.miniChart {
position: relative;
width: 100%;
.chartContent {
position: absolute;
bottom: -28px;
width: 100%;
> div {
margin: 0 -5px;
overflow: hidden;
}
}
.chartLoading {
position: absolute;
top: 16px;
left: 50%;
margin-left: -7px;
}
}
Analysis/src/components/Charts/index.tsx
0 → 100644
View file @
278a1ce9
import
numeral
from
'
numeral
'
;
import
ChartCard
from
'
./ChartCard
'
;
import
Field
from
'
./Field
'
;
import
Bar
from
'
./Bar
'
;
import
Pie
from
'
./Pie
'
;
import
Radar
from
'
./Radar
'
;
import
Gauge
from
'
./Gauge
'
;
import
MiniArea
from
'
./MiniArea
'
;
import
MiniBar
from
'
./MiniBar
'
;
import
MiniProgress
from
'
./MiniProgress
'
;
import
WaterWave
from
'
./WaterWave
'
;
import
TagCloud
from
'
./TagCloud
'
;
import
TimelineChart
from
'
./TimelineChart
'
;
const
yuan
=
(
val
:
number
|
string
)
=>
`¥
${
numeral
(
val
).
format
(
'
0,0
'
)}
`
;
const
Charts
=
{
yuan
,
Bar
,
Pie
,
Gauge
,
Radar
,
MiniBar
,
MiniArea
,
MiniProgress
,
ChartCard
,
Field
,
WaterWave
,
TagCloud
,
TimelineChart
,
};
export
{
Charts
as
default
,
yuan
,
Bar
,
Pie
,
Gauge
,
Radar
,
MiniBar
,
MiniArea
,
MiniProgress
,
ChartCard
,
Field
,
WaterWave
,
TagCloud
,
TimelineChart
,
};
Analysis/src/components/IntroduceRow.
js
→
Analysis/src/components/IntroduceRow.
tsx
View file @
278a1ce9
import
React
,
{
memo
}
from
'
react
'
;
import
React
from
'
react
'
;
import
{
Row
,
Col
,
Icon
,
Tooltip
}
from
'
antd
'
;
import
{
FormattedMessage
}
from
'
umi-plugin-react/locale
'
;
import
{
Charts
,
Trend
}
from
'
ant-design-pro
'
;
import
Charts
from
'
./Charts
'
;
import
numeral
from
'
numeral
'
;
import
styles
from
'
../style.less
'
;
import
Yuan
from
'
../utils/Yuan
'
;
import
Trend
from
'
./Trend
'
;
import
{
IVisitData
}
from
'
../data.d
'
;
const
{
ChartCard
,
MiniArea
,
MiniBar
,
MiniProgress
,
Field
}
=
Charts
;
const
topColResponsiveProps
=
{
...
...
@@ -17,47 +18,48 @@ const topColResponsiveProps = {
style
:
{
marginBottom
:
24
},
};
const
IntroduceRow
=
memo
(({
loading
,
visitData
})
=>
(
<
Row
gutter
=
{
24
}
>
<
Col
{...
topColResponsiveProps
}
>
<
ChartCard
bordered
=
{
false
}
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.total-sales
"
defaultMessage
=
"
Total Sales
"
/>
}
action
=
{
<
Tooltip
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.introduce
"
defaultMessage
=
"
Introduce
"
/>
}
>
<
Icon
type
=
"
info-circle-o
"
/>
<
/Tooltip
>
}
loading
=
{
loading
}
total
=
{()
=>
<
Yuan
>
126560
<
/Yuan>
}
footer
=
{
<
Field
label
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.day-sales
"
defaultMessage
=
"
Daily Sales
"
/>
}
value
=
{
`¥
${
numeral
(
12423
).
format
(
'
0,0
'
)}
`
}
/
>
}
contentHeight
=
{
46
}
>
<
Trend
flag
=
"
up
"
style
=
{{
marginRight
:
16
}}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.week
"
defaultMessage
=
"
Weekly Changes
"
/>
<
span
className
=
{
styles
.
trendText
}
>
12
%<
/span
>
<
/Trend
>
<
Trend
flag
=
"
down
"
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.day
"
defaultMessage
=
"
Daily Changes
"
/>
<
span
className
=
{
styles
.
trendText
}
>
11
%<
/span
>
<
/Trend
>
<
/ChartCard
>
<
/Col
>
const
IntroduceRow
=
({
loading
,
visitData
}:
{
loading
:
boolean
;
visitData
:
IVisitData
[]
})
=>
{
return
(
<
Row
gutter
=
{
24
}
>
<
Col
{
...
topColResponsiveProps
}
>
<
ChartCard
bordered
=
{
false
}
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.total-sales"
defaultMessage
=
"Total Sales"
/>
}
action
=
{
<
Tooltip
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.introduce"
defaultMessage
=
"Introduce"
/>
}
>
<
Icon
type
=
"info-circle-o"
/>
</
Tooltip
>
}
loading
=
{
loading
}
total
=
{
()
=>
<
Yuan
>
126560
</
Yuan
>
}
footer
=
{
<
Field
label
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.day-sales"
defaultMessage
=
"Daily Sales"
/>
}
value
=
{
`¥
${
numeral
(
12423
).
format
(
'
0,0
'
)}
`
}
/>
}
contentHeight
=
{
46
}
>
<
Trend
flag
=
"up"
style
=
{
{
marginRight
:
16
}
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.week"
defaultMessage
=
"Weekly Changes"
/>
<
span
className
=
{
styles
.
trendText
}
>
12%
</
span
>
</
Trend
>
<
Trend
flag
=
"down"
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.day"
defaultMessage
=
"Daily Changes"
/>
<
span
className
=
{
styles
.
trendText
}
>
11%
</
span
>
</
Trend
>
</
ChartCard
>
</
Col
>
<
Col
{...
topColResponsiveProps
}
>
{
/*
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
...
...
@@ -84,8 +86,9 @@ const IntroduceRow = memo(({ loading, visitData }) => (
>
<MiniArea color="#975FE4" data={visitData} />
</ChartCard>
<
/Col
>
<
Col
{...
topColResponsiveProps
}
>
</Col> */
}
{
/* <Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
...
...
@@ -115,45 +118,46 @@ const IntroduceRow = memo(({ loading, visitData }) => (
>
<MiniBar data={visitData} />
</ChartCard>
<
/Col
>
<
Col
{...
topColResponsiveProps
}
>
<
ChartCard
loading
=
{
loading
}
bordered
=
{
false
}
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.operational-effect
"
defaultMessage
=
"
Operational Effect
"
/>
}
action
=
{
<
Tooltip
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.introduce
"
defaultMessage
=
"
Introduce
"
/>
}
>
<
Icon
type
=
"
info-circle-o
"
/>
<
/Tooltip
>
}
total
=
"
78%
"
footer
=
{
<
div
style
=
{{
whiteSpace
:
'
nowrap
'
,
overflow
:
'
hidden
'
}}
>
<
Trend
flag
=
"
up
"
style
=
{{
marginRight
:
16
}}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.week
"
defaultMessage
=
"
Weekly Changes
"
/>
<
span
className
=
{
styles
.
trendText
}
>
12
%<
/span
>
<
/Trend
>
<
Trend
flag
=
"
down
"
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.day
"
defaultMessage
=
"
Weekly Changes
"
/>
<
span
className
=
{
styles
.
trendText
}
>
11
%<
/span
>
<
/Trend
>
<
/div
>
}
contentHeight
=
{
46
}
>
<
MiniProgress
percent
=
{
78
}
strokeWidth
=
{
8
}
target
=
{
80
}
color
=
"
#13C2C2
"
/>
<
/ChartCard
>
<
/Col
>
<
/Row
>
));
</Col> */
}
<
Col
{
...
topColResponsiveProps
}
>
<
ChartCard
loading
=
{
loading
}
bordered
=
{
false
}
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.operational-effect"
defaultMessage
=
"Operational Effect"
/>
}
action
=
{
<
Tooltip
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.introduce"
defaultMessage
=
"Introduce"
/>
}
>
<
Icon
type
=
"info-circle-o"
/>
</
Tooltip
>
}
total
=
"78%"
footer
=
{
<
div
style
=
{
{
whiteSpace
:
'
nowrap
'
,
overflow
:
'
hidden
'
}
}
>
<
Trend
flag
=
"up"
style
=
{
{
marginRight
:
16
}
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.week"
defaultMessage
=
"Weekly Changes"
/>
<
span
className
=
{
styles
.
trendText
}
>
12%
</
span
>
</
Trend
>
<
Trend
flag
=
"down"
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.day"
defaultMessage
=
"Weekly Changes"
/>
<
span
className
=
{
styles
.
trendText
}
>
11%
</
span
>
</
Trend
>
</
div
>
}
contentHeight
=
{
46
}
>
<
MiniProgress
percent
=
{
78
}
strokeWidth
=
{
8
}
target
=
{
80
}
color
=
"#13C2C2"
/>
</
ChartCard
>
</
Col
>
</
Row
>
);
};
export
default
IntroduceRow
;
Analysis/src/components/NumberInfo/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.numberInfo {
.suffix {
margin-left: 4px;
color: @text-color;
font-size: 16px;
font-style: normal;
}
.numberInfoTitle {
margin-bottom: 16px;
color: @text-color;
font-size: @font-size-lg;
transition: all 0.3s;
}
.numberInfoSubTitle {
height: 22px;
overflow: hidden;
color: @text-color-secondary;
font-size: @font-size-base;
line-height: 22px;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
}
.numberInfoValue {
margin-top: 4px;
overflow: hidden;
font-size: 0;
white-space: nowrap;
text-overflow: ellipsis;
word-break: break-all;
& > span {
display: inline-block;
height: 32px;
margin-right: 32px;
color: @heading-color;
font-size: 24px;
line-height: 32px;
}
.subTotal {
margin-right: 0;
color: @text-color-secondary;
font-size: @font-size-lg;
vertical-align: top;
i {
margin-left: 4px;
font-size: 12px;
transform: scale(0.82);
}
:global {
.anticon-caret-up {
color: @red-6;
}
.anticon-caret-down {
color: @green-6;
}
}
}
}
}
.numberInfolight {
.numberInfoValue {
& > span {
color: @text-color;
}
}
}
Analysis/src/components/NumberInfo/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Icon
}
from
'
antd
'
;
import
classNames
from
'
classnames
'
;
import
styles
from
'
./index.less
'
;
export
interface
NumberInfoProps
{
title
?:
React
.
ReactNode
|
string
;
subTitle
?:
React
.
ReactNode
|
string
;
total
?:
React
.
ReactNode
|
string
;
status
?:
'
up
'
|
'
down
'
;
theme
?:
string
;
gap
?:
number
;
subTotal
?:
number
;
suffix
?:
string
;
style
?:
React
.
CSSProperties
;
}
const
NumberInfo
:
React
.
SFC
<
NumberInfoProps
>
=
({
theme
,
title
,
subTitle
,
total
,
subTotal
,
status
,
suffix
,
gap
,
...
rest
})
=>
(
<
div
className
=
{
classNames
(
styles
.
numberInfo
,
{
[
styles
[
`numberInfo
${
theme
}
`
]]:
theme
,
})
}
{
...
rest
}
>
{
title
&&
(
<
div
className
=
{
styles
.
numberInfoTitle
}
title
=
{
typeof
title
===
'
string
'
?
title
:
''
}
>
{
title
}
</
div
>
)
}
{
subTitle
&&
(
<
div
className
=
{
styles
.
numberInfoSubTitle
}
title
=
{
typeof
subTitle
===
'
string
'
?
subTitle
:
''
}
>
{
subTitle
}
</
div
>
)
}
<
div
className
=
{
styles
.
numberInfoValue
}
style
=
{
gap
?
{
marginTop
:
gap
}
:
{}
}
>
<
span
>
{
total
}
{
suffix
&&
<
em
className
=
{
styles
.
suffix
}
>
{
suffix
}
</
em
>
}
</
span
>
{
(
status
||
subTotal
)
&&
(
<
span
className
=
{
styles
.
subTotal
}
>
{
subTotal
}
{
status
&&
<
Icon
type
=
{
`caret-
${
status
}
`
}
/>
}
</
span
>
)
}
</
div
>
</
div
>
);
export
default
NumberInfo
;
Analysis/src/components/OfflineData.
js
→
Analysis/src/components/OfflineData.
tsx
View file @
278a1ce9
import
React
,
{
memo
}
from
'
react
'
;
import
React
from
'
react
'
;
import
{
Card
,
Tabs
,
Row
,
Col
}
from
'
antd
'
;
import
{
formatMessage
,
FormattedMessage
}
from
'
umi-plugin-react/locale
'
;
import
{
Charts
,
NumberInfo
}
from
'
ant-design-pro
'
;
import
Charts
from
'
./Charts
'
;
import
styles
from
'
../style.less
'
;
import
NumberInfo
from
'
./NumberInfo
'
;
import
{
IOfflineData
,
IOfflineChartData
}
from
'
../data
'
;
const
{
TimelineChart
,
Pie
}
=
Charts
;
const
CustomTab
=
({
data
,
currentTabKey
:
currentKey
})
=>
(
const
CustomTab
=
({
data
,
currentTabKey
:
currentKey
,
}:
{
data
:
IOfflineData
;
currentTabKey
:
string
;
})
=>
(
<
Row
gutter
=
{
8
}
style
=
{
{
width
:
138
,
margin
:
'
8px 0
'
}
}
>
<
Col
span
=
{
12
}
>
<
NumberInfo
...
...
@@ -19,13 +26,12 @@ const CustomTab = ({ data, currentTabKey: currentKey }) => (
}
gap
=
{
2
}
total
=
{
`
${
data
.
cvr
*
100
}
%`
}
theme
=
{
currentKey
!==
data
.
name
&&
'
light
'
}
theme
=
{
currentKey
!==
data
.
name
?
'
light
'
:
undefined
}
/>
</
Col
>
<
Col
span
=
{
12
}
style
=
{
{
paddingTop
:
36
}
}
>
<
Pie
animate
=
{
false
}
color
=
{
currentKey
!==
data
.
name
&&
'
#BDE4FF
'
}
inner
=
{
0.55
}
tooltip
=
{
false
}
margin
=
{
[
0
,
0
,
0
,
0
]
}
...
...
@@ -38,32 +44,37 @@ const CustomTab = ({ data, currentTabKey: currentKey }) => (
const
{
TabPane
}
=
Tabs
;
const
OfflineData
=
memo
(
({
activeKey
,
loading
,
offlineData
,
offlineChartData
,
handleTabChange
})
=>
(
<
Card
loading
=
{
loading
}
className
=
{
styles
.
offlineCard
}
bordered
=
{
false
}
style
=
{{
marginTop
:
32
}}
>
<
Tabs
activeKey
=
{
activeKey
}
onChange
=
{
handleTabChange
}
>
{
offlineData
.
map
(
shop
=>
(
<
TabPane
tab
=
{
<
CustomTab
data
=
{
shop
}
currentTabKey
=
{
activeKey
}
/>} key={shop.name}
>
<
div
style
=
{{
padding
:
'
0 24px
'
}}
>
<
TimelineChart
height
=
{
400
}
data
=
{
offlineChartData
}
titleMap
=
{{
y1
:
formatMessage
({
id
:
'
BLOCK_NAME.analysis.traffic
'
}),
y2
:
formatMessage
({
id
:
'
BLOCK_NAME.analysis.payments
'
}),
}}
/
>
<
/div
>
<
/TabPane
>
))}
<
/Tabs
>
<
/Card
>
)
const
OfflineData
=
({
activeKey
,
loading
,
offlineData
,
offlineChartData
,
handleTabChange
,
}:
{
activeKey
:
string
;
loading
:
boolean
;
offlineData
:
IOfflineData
[];
offlineChartData
:
IOfflineChartData
[];
handleTabChange
:
(
activeKey
:
string
)
=>
void
;
})
=>
(
<
Card
loading
=
{
loading
}
className
=
{
styles
.
offlineCard
}
bordered
=
{
false
}
style
=
{
{
marginTop
:
32
}
}
>
<
Tabs
activeKey
=
{
activeKey
}
onChange
=
{
handleTabChange
}
>
{
offlineData
.
map
(
shop
=>
(
<
TabPane
tab
=
{
<
CustomTab
data
=
{
shop
}
currentTabKey
=
{
activeKey
}
/>
}
key
=
{
shop
.
name
}
>
<
div
style
=
{
{
padding
:
'
0 24px
'
}
}
>
<
TimelineChart
height
=
{
400
}
data
=
{
offlineChartData
}
titleMap
=
{
{
y1
:
formatMessage
({
id
:
'
BLOCK_NAME.analysis.traffic
'
}),
y2
:
formatMessage
({
id
:
'
BLOCK_NAME.analysis.payments
'
}),
}
}
/>
</
div
>
</
TabPane
>
))
}
</
Tabs
>
</
Card
>
);
export
default
OfflineData
;
Analysis/src/components/PageLoading/index.
js
→
Analysis/src/components/PageLoading/index.
tsx
View file @
278a1ce9
File moved
Analysis/src/components/ProportionSales.js
deleted
100755 → 0
View file @
74b673e5
import
React
,
{
memo
}
from
'
react
'
;
import
{
Card
,
Radio
}
from
'
antd
'
;
import
{
Charts
}
from
'
ant-design-pro
'
;
import
{
FormattedMessage
}
from
'
umi-plugin-react/locale
'
;
import
styles
from
'
../style.less
'
;
import
Yuan
from
'
../utils/Yuan
'
;
const
{
Pie
}
=
Charts
;
const
ProportionSales
=
memo
(
({
dropdownGroup
,
salesType
,
loading
,
salesPieData
,
handleChangeSalesType
})
=>
(
<
Card
loading
=
{
loading
}
className
=
{
styles
.
salesCard
}
bordered
=
{
false
}
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.the-proportion-of-sales
"
defaultMessage
=
"
The Proportion of Sales
"
/>
}
bodyStyle
=
{{
padding
:
24
}}
extra
=
{
<
div
className
=
{
styles
.
salesCardExtra
}
>
{
dropdownGroup
}
<
div
className
=
{
styles
.
salesTypeRadio
}
>
<
Radio
.
Group
value
=
{
salesType
}
onChange
=
{
handleChangeSalesType
}
>
<
Radio
.
Button
value
=
"
all
"
>
<
FormattedMessage
id
=
"
BLOCK_NAME.channel.all
"
defaultMessage
=
"
ALL
"
/>
<
/Radio.Button
>
<
Radio
.
Button
value
=
"
online
"
>
<
FormattedMessage
id
=
"
BLOCK_NAME.channel.online
"
defaultMessage
=
"
Online
"
/>
<
/Radio.Button
>
<
Radio
.
Button
value
=
"
stores
"
>
<
FormattedMessage
id
=
"
BLOCK_NAME.channel.stores
"
defaultMessage
=
"
Stores
"
/>
<
/Radio.Button
>
<
/Radio.Group
>
<
/div
>
<
/div
>
}
style
=
{{
marginTop
:
24
}}
>
<
div
style
=
{{
minHeight
:
380
,
}}
>
<
h4
style
=
{{
marginTop
:
8
,
marginBottom
:
32
}}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.sales
"
defaultMessage
=
"
Sales
"
/>
<
/h4
>
<
Pie
hasLegend
subTitle
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.sales
"
defaultMessage
=
"
Sales
"
/>
}
total
=
{()
=>
<
Yuan
>
{
salesPieData
.
reduce
((
pre
,
now
)
=>
now
.
y
+
pre
,
0
)}
<
/Yuan>
}
data
=
{
salesPieData
}
valueFormat
=
{
value
=>
<
Yuan
>
{
value
}
<
/Yuan>
}
height
=
{
248
}
lineWidth
=
{
4
}
/
>
<
/div
>
<
/Card
>
)
);
export
default
ProportionSales
;
Analysis/src/components/ProportionSales.tsx
0 → 100755
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Card
,
Radio
}
from
'
antd
'
;
import
Charts
from
'
./Charts
'
;
import
{
FormattedMessage
}
from
'
umi-plugin-react/locale
'
;
import
styles
from
'
../style.less
'
;
import
Yuan
from
'
../utils/Yuan
'
;
import
{
RadioChangeEvent
}
from
'
antd/lib/radio
'
;
import
{
ISalesData
}
from
'
../data
'
;
const
{
Pie
}
=
Charts
;
const
ProportionSales
=
({
dropdownGroup
,
salesType
,
loading
,
salesPieData
,
handleChangeSalesType
,
}:
{
loading
:
boolean
;
dropdownGroup
:
React
.
ReactNode
;
salesType
:
'
all
'
|
'
online
'
|
'
stores
'
;
salesPieData
:
ISalesData
[];
handleChangeSalesType
?:
(
e
:
RadioChangeEvent
)
=>
void
;
})
=>
(
<
Card
loading
=
{
loading
}
className
=
{
styles
.
salesCard
}
bordered
=
{
false
}
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.the-proportion-of-sales"
defaultMessage
=
"The Proportion of Sales"
/>
}
bodyStyle
=
{
{
padding
:
24
}
}
extra
=
{
<
div
className
=
{
styles
.
salesCardExtra
}
>
{
dropdownGroup
}
<
div
className
=
{
styles
.
salesTypeRadio
}
>
<
Radio
.
Group
value
=
{
salesType
}
onChange
=
{
handleChangeSalesType
}
>
<
Radio
.
Button
value
=
"all"
>
<
FormattedMessage
id
=
"BLOCK_NAME.channel.all"
defaultMessage
=
"ALL"
/>
</
Radio
.
Button
>
<
Radio
.
Button
value
=
"online"
>
<
FormattedMessage
id
=
"BLOCK_NAME.channel.online"
defaultMessage
=
"Online"
/>
</
Radio
.
Button
>
<
Radio
.
Button
value
=
"stores"
>
<
FormattedMessage
id
=
"BLOCK_NAME.channel.stores"
defaultMessage
=
"Stores"
/>
</
Radio
.
Button
>
</
Radio
.
Group
>
</
div
>
</
div
>
}
style
=
{
{
marginTop
:
24
}
}
>
<
div
style
=
{
{
minHeight
:
380
,
}
}
>
<
h4
style
=
{
{
marginTop
:
8
,
marginBottom
:
32
}
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.sales"
defaultMessage
=
"Sales"
/>
</
h4
>
<
Pie
hasLegend
subTitle
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.sales"
defaultMessage
=
"Sales"
/>
}
total
=
{
()
=>
<
Yuan
>
{
salesPieData
.
reduce
((
pre
,
now
)
=>
now
.
y
+
pre
,
0
)
}
</
Yuan
>
}
data
=
{
salesPieData
}
valueFormat
=
{
value
=>
<
Yuan
>
{
value
}
</
Yuan
>
}
height
=
{
248
}
lineWidth
=
{
4
}
/>
</
div
>
</
Card
>
);
export
default
ProportionSales
;
Analysis/src/components/SalesCard.js
deleted
100755 → 0
View file @
74b673e5
import
React
,
{
memo
}
from
'
react
'
;
import
{
Row
,
Col
,
Card
,
Tabs
,
DatePicker
}
from
'
antd
'
;
import
{
FormattedMessage
,
formatMessage
}
from
'
umi-plugin-react/locale
'
;
import
numeral
from
'
numeral
'
;
import
{
Charts
}
from
'
ant-design-pro
'
;
import
styles
from
'
../style.less
'
;
const
{
Bar
}
=
Charts
;
const
{
RangePicker
}
=
DatePicker
;
const
{
TabPane
}
=
Tabs
;
const
rankingListData
=
[];
for
(
let
i
=
0
;
i
<
7
;
i
+=
1
)
{
rankingListData
.
push
({
title
:
formatMessage
({
id
:
'
BLOCK_NAME.analysis.test
'
},
{
no
:
i
}),
total
:
323234
,
});
}
const
SalesCard
=
memo
(
({
rangePickerValue
,
salesData
,
isActive
,
handleRangePickerChange
,
loading
,
selectDate
})
=>
(
<
Card
loading
=
{
loading
}
bordered
=
{
false
}
bodyStyle
=
{{
padding
:
0
}}
>
<
div
className
=
{
styles
.
salesCard
}
>
<
Tabs
tabBarExtraContent
=
{
<
div
className
=
{
styles
.
salesExtraWrap
}
>
<
div
className
=
{
styles
.
salesExtra
}
>
<
a
className
=
{
isActive
(
'
today
'
)}
onClick
=
{()
=>
selectDate
(
'
today
'
)}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.all-day
"
defaultMessage
=
"
All Day
"
/>
<
/a
>
<
a
className
=
{
isActive
(
'
week
'
)}
onClick
=
{()
=>
selectDate
(
'
week
'
)}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.all-week
"
defaultMessage
=
"
All Week
"
/>
<
/a
>
<
a
className
=
{
isActive
(
'
month
'
)}
onClick
=
{()
=>
selectDate
(
'
month
'
)}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.all-month
"
defaultMessage
=
"
All Month
"
/>
<
/a
>
<
a
className
=
{
isActive
(
'
year
'
)}
onClick
=
{()
=>
selectDate
(
'
year
'
)}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.all-year
"
defaultMessage
=
"
All Year
"
/>
<
/a
>
<
/div
>
<
RangePicker
value
=
{
rangePickerValue
}
onChange
=
{
handleRangePickerChange
}
style
=
{{
width
:
256
}}
/
>
<
/div
>
}
size
=
"
large
"
tabBarStyle
=
{{
marginBottom
:
24
}}
>
<
TabPane
tab
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.sales
"
defaultMessage
=
"
Sales
"
/>
}
key
=
"
sales
"
>
<
Row
>
<
Col
xl
=
{
16
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesBar
}
>
<
Bar
height
=
{
295
}
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.sales-trend
"
defaultMessage
=
"
Sales Trend
"
/>
}
data
=
{
salesData
}
/
>
<
/div
>
<
/Col
>
<
Col
xl
=
{
8
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesRank
}
>
<
h4
className
=
{
styles
.
rankingTitle
}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.sales-ranking
"
defaultMessage
=
"
Sales Ranking
"
/>
<
/h4
>
<
ul
className
=
{
styles
.
rankingList
}
>
{
rankingListData
.
map
((
item
,
i
)
=>
(
<
li
key
=
{
item
.
title
}
>
<
span
className
=
{
`
${
styles
.
rankingItemNumber
}
${
i
<
3
?
styles
.
active
:
''
}
`
}
>
{
i
+
1
}
<
/span
>
<
span
className
=
{
styles
.
rankingItemTitle
}
title
=
{
item
.
title
}
>
{
item
.
title
}
<
/span
>
<
span
className
=
{
styles
.
rankingItemValue
}
>
{
numeral
(
item
.
total
).
format
(
'
0,0
'
)}
<
/span
>
<
/li
>
))}
<
/ul
>
<
/div
>
<
/Col
>
<
/Row
>
<
/TabPane
>
<
TabPane
tab
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.visits
"
defaultMessage
=
"
Visits
"
/>
}
key
=
"
views
"
>
<
Row
>
<
Col
xl
=
{
16
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesBar
}
>
<
Bar
height
=
{
292
}
title
=
{
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.visits-trend
"
defaultMessage
=
"
Visits Trend
"
/>
}
data
=
{
salesData
}
/
>
<
/div
>
<
/Col
>
<
Col
xl
=
{
8
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesRank
}
>
<
h4
className
=
{
styles
.
rankingTitle
}
>
<
FormattedMessage
id
=
"
BLOCK_NAME.analysis.visits-ranking
"
defaultMessage
=
"
Visits Ranking
"
/>
<
/h4
>
<
ul
className
=
{
styles
.
rankingList
}
>
{
rankingListData
.
map
((
item
,
i
)
=>
(
<
li
key
=
{
item
.
title
}
>
<
span
className
=
{
`
${
styles
.
rankingItemNumber
}
${
i
<
3
?
styles
.
active
:
''
}
`
}
>
{
i
+
1
}
<
/span
>
<
span
className
=
{
styles
.
rankingItemTitle
}
title
=
{
item
.
title
}
>
{
item
.
title
}
<
/span
>
<
span
>
{
numeral
(
item
.
total
).
format
(
'
0,0
'
)}
<
/span
>
<
/li
>
))}
<
/ul
>
<
/div
>
<
/Col
>
<
/Row
>
<
/TabPane
>
<
/Tabs
>
<
/div
>
<
/Card
>
)
);
export
default
SalesCard
;
Analysis/src/components/SalesCard.tsx
0 → 100755
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Row
,
Col
,
Card
,
Tabs
,
DatePicker
}
from
'
antd
'
;
import
{
FormattedMessage
,
formatMessage
}
from
'
umi-plugin-react/locale
'
;
import
numeral
from
'
numeral
'
;
import
Charts
from
'
./Charts
'
;
import
{
RangePickerValue
}
from
'
antd/lib/date-picker/interface
'
;
import
{
ISalesData
}
from
'
../data
'
;
import
styles
from
'
../style.less
'
;
const
{
Bar
}
=
Charts
;
const
{
RangePicker
}
=
DatePicker
;
const
{
TabPane
}
=
Tabs
;
const
rankingListData
:
{
title
:
string
;
total
:
number
}[]
=
[];
for
(
let
i
=
0
;
i
<
7
;
i
+=
1
)
{
rankingListData
.
push
({
title
:
formatMessage
({
id
:
'
BLOCK_NAME.analysis.test
'
},
{
no
:
i
}),
total
:
323234
,
});
}
const
SalesCard
=
({
rangePickerValue
,
salesData
,
isActive
,
handleRangePickerChange
,
loading
,
selectDate
,
}:
{
rangePickerValue
:
RangePickerValue
;
isActive
:
(
key
:
'
today
'
|
'
week
'
|
'
month
'
|
'
year
'
)
=>
string
;
salesData
:
ISalesData
[];
loading
:
boolean
;
handleRangePickerChange
:
(
dates
:
RangePickerValue
,
dateStrings
:
[
string
,
string
])
=>
void
;
selectDate
:
(
key
:
'
today
'
|
'
week
'
|
'
month
'
|
'
year
'
)
=>
void
;
})
=>
(
<
Card
loading
=
{
loading
}
bordered
=
{
false
}
bodyStyle
=
{
{
padding
:
0
}
}
>
<
div
className
=
{
styles
.
salesCard
}
>
<
Tabs
tabBarExtraContent
=
{
<
div
className
=
{
styles
.
salesExtraWrap
}
>
<
div
className
=
{
styles
.
salesExtra
}
>
<
a
className
=
{
isActive
(
'
today
'
)
}
onClick
=
{
()
=>
selectDate
(
'
today
'
)
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.all-day"
defaultMessage
=
"All Day"
/>
</
a
>
<
a
className
=
{
isActive
(
'
week
'
)
}
onClick
=
{
()
=>
selectDate
(
'
week
'
)
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.all-week"
defaultMessage
=
"All Week"
/>
</
a
>
<
a
className
=
{
isActive
(
'
month
'
)
}
onClick
=
{
()
=>
selectDate
(
'
month
'
)
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.all-month"
defaultMessage
=
"All Month"
/>
</
a
>
<
a
className
=
{
isActive
(
'
year
'
)
}
onClick
=
{
()
=>
selectDate
(
'
year
'
)
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.all-year"
defaultMessage
=
"All Year"
/>
</
a
>
</
div
>
<
RangePicker
value
=
{
rangePickerValue
}
onChange
=
{
handleRangePickerChange
}
style
=
{
{
width
:
256
}
}
/>
</
div
>
}
size
=
"large"
tabBarStyle
=
{
{
marginBottom
:
24
}
}
>
<
TabPane
tab
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.sales"
defaultMessage
=
"Sales"
/>
}
key
=
"sales"
>
<
Row
>
<
Col
xl
=
{
16
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesBar
}
>
<
Bar
height
=
{
295
}
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.sales-trend"
defaultMessage
=
"Sales Trend"
/>
}
data
=
{
salesData
}
/>
</
div
>
</
Col
>
<
Col
xl
=
{
8
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesRank
}
>
<
h4
className
=
{
styles
.
rankingTitle
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.sales-ranking"
defaultMessage
=
"Sales Ranking"
/>
</
h4
>
<
ul
className
=
{
styles
.
rankingList
}
>
{
rankingListData
.
map
((
item
,
i
)
=>
(
<
li
key
=
{
item
.
title
}
>
<
span
className
=
{
`
${
styles
.
rankingItemNumber
}
${
i
<
3
?
styles
.
active
:
''
}
`
}
>
{
i
+
1
}
</
span
>
<
span
className
=
{
styles
.
rankingItemTitle
}
title
=
{
item
.
title
}
>
{
item
.
title
}
</
span
>
<
span
className
=
{
styles
.
rankingItemValue
}
>
{
numeral
(
item
.
total
).
format
(
'
0,0
'
)
}
</
span
>
</
li
>
))
}
</
ul
>
</
div
>
</
Col
>
</
Row
>
</
TabPane
>
<
TabPane
tab
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.visits"
defaultMessage
=
"Visits"
/>
}
key
=
"views"
>
<
Row
>
<
Col
xl
=
{
16
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesBar
}
>
<
Bar
height
=
{
292
}
title
=
{
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.visits-trend"
defaultMessage
=
"Visits Trend"
/>
}
data
=
{
salesData
}
/>
</
div
>
</
Col
>
<
Col
xl
=
{
8
}
lg
=
{
12
}
md
=
{
12
}
sm
=
{
24
}
xs
=
{
24
}
>
<
div
className
=
{
styles
.
salesRank
}
>
<
h4
className
=
{
styles
.
rankingTitle
}
>
<
FormattedMessage
id
=
"BLOCK_NAME.analysis.visits-ranking"
defaultMessage
=
"Visits Ranking"
/>
</
h4
>
<
ul
className
=
{
styles
.
rankingList
}
>
{
rankingListData
.
map
((
item
,
i
)
=>
(
<
li
key
=
{
item
.
title
}
>
<
span
className
=
{
`
${
styles
.
rankingItemNumber
}
${
i
<
3
?
styles
.
active
:
''
}
`
}
>
{
i
+
1
}
</
span
>
<
span
className
=
{
styles
.
rankingItemTitle
}
title
=
{
item
.
title
}
>
{
item
.
title
}
</
span
>
<
span
>
{
numeral
(
item
.
total
).
format
(
'
0,0
'
)
}
</
span
>
</
li
>
))
}
</
ul
>
</
div
>
</
Col
>
</
Row
>
</
TabPane
>
</
Tabs
>
</
div
>
</
Card
>
);
export
default
SalesCard
;
Analysis/src/components/TopSearch.
js
→
Analysis/src/components/TopSearch.
tsx
View file @
278a1ce9
import
React
,
{
memo
}
from
'
react
'
;
import
React
from
'
react
'
;
import
{
Row
,
Col
,
Table
,
Tooltip
,
Card
,
Icon
}
from
'
antd
'
;
import
{
FormattedMessage
}
from
'
umi-plugin-react/locale
'
;
import
{
Trend
,
NumberInfo
,
Charts
}
from
'
ant-design-pro
'
;
import
Charts
from
'
./Charts
'
;
import
Trend
from
'
./Trend
'
;
import
NumberInfo
from
'
./NumberInfo
'
;
import
numeral
from
'
numeral
'
;
import
styles
from
'
../style.less
'
;
import
{
ISearchData
,
IVisitData2
}
from
'
../data
'
;
const
{
MiniArea
}
=
Charts
;
...
...
@@ -19,30 +22,39 @@ const columns = [
),
dataIndex
:
'
keyword
'
,
key
:
'
keyword
'
,
render
:
text
=>
<
a
href
=
"
/
"
>
{
text
}
<
/a>
,
render
:
(
text
:
React
.
ReactNode
)
=>
<
a
href
=
"/"
>
{
text
}
</
a
>,
},
{
title
:
<
FormattedMessage
id
=
"BLOCK_NAME.table.users"
defaultMessage
=
"Users"
/>,
dataIndex
:
'
count
'
,
key
:
'
count
'
,
sorter
:
(
a
,
b
)
=>
a
.
count
-
b
.
count
,
sorter
:
(
a
:
{
count
:
number
},
b
:
{
count
:
number
}
)
=>
a
.
count
-
b
.
count
,
className
:
styles
.
alignRight
,
},
{
title
:
<
FormattedMessage
id
=
"BLOCK_NAME.table.weekly-range"
defaultMessage
=
"Weekly Range"
/>,
dataIndex
:
'
range
'
,
key
:
'
range
'
,
sorter
:
(
a
,
b
)
=>
a
.
range
-
b
.
range
,
render
:
(
text
,
record
)
=>
(
sorter
:
(
a
:
{
range
:
number
},
b
:
{
range
:
number
}
)
=>
a
.
range
-
b
.
range
,
render
:
(
text
:
React
.
ReactNode
,
record
:
{
status
:
number
}
)
=>
(
<
Trend
flag
=
{
record
.
status
===
1
?
'
down
'
:
'
up
'
}
>
<
span
style
=
{
{
marginRight
:
4
}
}
>
{
text
}
%
</
span
>
</
Trend
>
),
align
:
'
right
'
,
},
];
const
TopSearch
=
memo
(({
loading
,
visitData2
,
searchData
,
dropdownGroup
})
=>
(
const
TopSearch
=
({
loading
,
visitData2
,
searchData
,
dropdownGroup
,
}:
{
loading
:
boolean
;
visitData2
:
IVisitData2
[];
dropdownGroup
:
React
.
ReactNode
;
searchData
:
ISearchData
[];
})
=>
(
<
Card
loading
=
{
loading
}
bordered
=
{
false
}
...
...
@@ -105,7 +117,7 @@ const TopSearch = memo(({ loading, visitData2, searchData, dropdownGroup }) => (
<
MiniArea
line
height
=
{
45
}
data
=
{
visitData2
}
/>
</
Col
>
</
Row
>
<
Table
<
Table
<
any
>
rowKey=
{
record
=>
record
.
index
}
size="small"
columns=
{
columns
}
...
...
@@ -116,6 +128,6 @@ const TopSearch = memo(({ loading, visitData2, searchData, dropdownGroup }) => (
}
}
/>
</
Card
>
)
)
;
);
export default TopSearch;
Analysis/src/components/Trend/index.less
0 → 100644
View file @
278a1ce9
@import '~antd/lib/style/themes/default.less';
.trendItem {
display: inline-block;
font-size: @font-size-base;
line-height: 22px;
.up,
.down {
position: relative;
top: 1px;
margin-left: 4px;
i {
font-size: 12px;
transform: scale(0.83);
}
}
.up {
color: @red-6;
}
.down {
top: -1px;
color: @green-6;
}
&.trendItemGrey .up,
&.trendItemGrey .down {
color: @text-color;
}
&.reverseColor .up {
color: @green-6;
}
&.reverseColor .down {
color: @red-6;
}
}
Analysis/src/components/Trend/index.tsx
0 → 100644
View file @
278a1ce9
import
React
from
'
react
'
;
import
{
Icon
}
from
'
antd
'
;
import
classNames
from
'
classnames
'
;
import
styles
from
'
./index.less
'
;
export
interface
ITrendProps
{
colorful
?:
boolean
;
flag
:
'
up
'
|
'
down
'
;
style
?:
React
.
CSSProperties
;
reverseColor
?:
boolean
;
className
?:
string
;
}
const
Trend
:
React
.
SFC
<
ITrendProps
>
=
({
colorful
=
true
,
reverseColor
=
false
,
flag
,
children
,
className
,
...
rest
})
=>
{
const
classString
=
classNames
(
styles
.
trendItem
,
{
[
styles
.
trendItemGrey
]:
!
colorful
,
[
styles
.
reverseColor
]:
reverseColor
&&
colorful
,
},
className
);
return
(
<
div
{
...
rest
}
className
=
{
classString
}
title
=
{
typeof
children
===
'
string
'
?
children
:
''
}
>
<
span
>
{
children
}
</
span
>
{
flag
&&
(
<
span
className
=
{
styles
[
flag
]
}
>
<
Icon
type
=
{
`caret-
${
flag
}
`
}
/>
</
span
>
)
}
</
div
>
);
};
export
default
Trend
;
Analysis/src/data.d.ts
0 → 100644
View file @
278a1ce9
export
interface
IVisitData
{
x
:
string
;
y
:
number
;
}
export
interface
IVisitData2
{
x
:
string
;
y
:
number
;
}
export
interface
ISalesData
{
x
:
string
;
y
:
number
;
}
export
interface
ISearchData
{
index
:
number
;
keyword
:
string
;
count
:
number
;
range
:
number
;
status
:
number
;
}
export
interface
IOfflineData
{
name
:
string
;
cvr
:
number
;
}
export
interface
IOfflineChartData
{
x
:
any
;
y1
:
number
;
y2
:
number
;
}
export
interface
ISalesTypeData
{
x
:
string
;
y
:
number
;
}
export
interface
ISalesTypeDataOnline
{
x
:
string
;
y
:
number
;
}
export
interface
ISalesTypeDataOffline
{
x
:
string
;
y
:
number
;
}
export
interface
IRadarData
{
name
:
string
;
label
:
string
;
value
:
number
;
}
export
interface
IAnalysisData
{
visitData
:
IVisitData
[];
visitData2
:
IVisitData2
[];
salesData
:
ISalesData
[];
searchData
:
ISearchData
[];
offlineData
:
IOfflineData
[];
offlineChartData
:
IOfflineChartData
[];
salesTypeData
:
ISalesTypeData
[];
salesTypeDataOnline
:
ISalesTypeDataOnline
[];
salesTypeDataOffline
:
ISalesTypeDataOffline
[];
radarData
:
IRadarData
[];
}
Analysis/src/index.
js
→
Analysis/src/index.
tsx
View file @
278a1ce9
import
React
,
{
Component
,
Suspense
}
from
'
react
'
;
import
{
connect
}
from
'
dva
'
;
import
{
Row
,
Col
,
Icon
,
Menu
,
Dropdown
}
from
'
antd
'
;
import
{
RangePickerValue
}
from
'
antd/lib/date-picker/interface
'
;
import
{
getTimeDistance
}
from
'
./utils/utils
'
;
import
styles
from
'
./style.less
'
;
import
PageLoading
from
'
./components/PageLoading
'
;
import
{
Dispatch
}
from
'
redux
'
;
import
{
IAnalysisData
}
from
'
./data.d
'
;
import
{
RadioChangeEvent
}
from
'
antd/lib/radio
'
;
const
IntroduceRow
=
React
.
lazy
(()
=>
import
(
'
./components/IntroduceRow
'
));
const
SalesCard
=
React
.
lazy
(()
=>
import
(
'
./components/SalesCard
'
));
...
...
@@ -13,17 +15,43 @@ const TopSearch = React.lazy(() => import('./components/TopSearch'));
const
ProportionSales
=
React
.
lazy
(()
=>
import
(
'
./components/ProportionSales
'
));
const
OfflineData
=
React
.
lazy
(()
=>
import
(
'
./components/OfflineData
'
));
@
connect
(({
BLOCK_NAME_CAMEL_CASE
,
loading
})
=>
({
BLOCK_NAME_CAMEL_CASE
,
loading
:
loading
.
effects
[
'
BLOCK_NAME_CAMEL_CASE/fetch
'
],
}))
class
PAGE_NAME_UPPER_CAMEL_CASE
extends
Component
{
state
=
{
interface
BLOCK_NAME_CAMEL_CASEProps
{
BLOCK_NAME_CAMEL_CASE
:
IAnalysisData
;
dispatch
:
Dispatch
;
loading
:
boolean
;
}
interface
BLOCK_NAME_CAMEL_CASEState
{
salesType
:
'
all
'
|
'
online
'
|
'
stores
'
;
currentTabKey
:
string
;
rangePickerValue
:
RangePickerValue
;
}
@
connect
(
({
BLOCK_NAME_CAMEL_CASE
,
loading
,
}:
{
BLOCK_NAME_CAMEL_CASE
:
any
;
loading
:
{
effects
:
{
[
key
:
string
]:
boolean
};
};
})
=>
({
BLOCK_NAME_CAMEL_CASE
,
loading
:
loading
.
effects
[
'
BLOCK_NAME_CAMEL_CASE/fetch
'
],
})
)
class
PAGE_NAME_UPPER_CAMEL_CASE
extends
Component
<
BLOCK_NAME_CAMEL_CASEProps
,
BLOCK_NAME_CAMEL_CASEState
>
{
state
:
BLOCK_NAME_CAMEL_CASEState
=
{
salesType
:
'
all
'
,
currentTabKey
:
''
,
rangePickerValue
:
getTimeDistance
(
'
year
'
),
};
reqRef
!
:
number
;
timeoutId
!
:
number
;
componentDidMount
()
{
const
{
dispatch
}
=
this
.
props
;
this
.
reqRef
=
requestAnimationFrame
(()
=>
{
...
...
@@ -42,19 +70,19 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
clearTimeout
(
this
.
timeoutId
);
}
handleChangeSalesType
=
e
=>
{
handleChangeSalesType
=
(
e
:
RadioChangeEvent
)
=>
{
this
.
setState
({
salesType
:
e
.
target
.
value
,
});
};
handleTabChange
=
key
=>
{
handleTabChange
=
(
key
:
string
)
=>
{
this
.
setState
({
currentTabKey
:
key
,
});
};
handleRangePickerChange
=
rangePickerValue
=>
{
handleRangePickerChange
=
(
rangePickerValue
:
RangePickerValue
)
=>
{
const
{
dispatch
}
=
this
.
props
;
this
.
setState
({
rangePickerValue
,
...
...
@@ -65,7 +93,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
});
};
selectDate
=
type
=>
{
selectDate
=
(
type
:
'
today
'
|
'
week
'
|
'
month
'
|
'
year
'
)
=>
{
const
{
dispatch
}
=
this
.
props
;
this
.
setState
({
rangePickerValue
:
getTimeDistance
(
type
),
...
...
@@ -76,7 +104,7 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
});
};
isActive
=
type
=>
{
isActive
=
(
type
:
'
today
'
|
'
week
'
|
'
month
'
|
'
year
'
)
=>
{
const
{
rangePickerValue
}
=
this
.
state
;
const
value
=
getTimeDistance
(
type
);
if
(
!
rangePickerValue
[
0
]
||
!
rangePickerValue
[
1
])
{
...
...
@@ -127,7 +155,6 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
);
const
activeKey
=
currentTabKey
||
(
offlineData
[
0
]
&&
offlineData
[
0
].
name
);
return
(
<
React
.
Fragment
>
<
Suspense
fallback
=
{
<
PageLoading
/>
}
>
...
...
@@ -149,7 +176,6 @@ class PAGE_NAME_UPPER_CAMEL_CASE extends Component {
<
TopSearch
loading
=
{
loading
}
visitData2
=
{
visitData2
}
selectDate
=
{
this
.
selectDate
}
searchData
=
{
searchData
}
dropdownGroup
=
{
dropdownGroup
}
/>
...
...
Analysis/src/locales/en-US.
j
s
→
Analysis/src/locales/en-US.
t
s
View file @
278a1ce9
File moved
Analysis/src/locales/pt-BR.
j
s
→
Analysis/src/locales/pt-BR.
t
s
View file @
278a1ce9
File moved
Analysis/src/locales/zh-CN.
j
s
→
Analysis/src/locales/zh-CN.
t
s
View file @
278a1ce9
File moved
Analysis/src/locales/zh-TW.
j
s
→
Analysis/src/locales/zh-TW.
t
s
View file @
278a1ce9
File moved
Analysis/src/model.
js
→
Analysis/src/model.
tsx
View file @
278a1ce9
File moved
Analysis/src/service.
js
→
Analysis/src/service.
tsx
View file @
278a1ce9
File moved
Analysis/src/utils/Yuan.
js
→
Analysis/src/utils/Yuan.
tsx
View file @
278a1ce9
File moved
Analysis/src/utils/utils.
j
s
→
Analysis/src/utils/utils.
t
s
View file @
278a1ce9
import
moment
from
'
moment
'
;
import
{
RangePickerValue
}
from
'
antd/lib/date-picker/interface
'
;
export
function
fixedZero
(
val
)
{
export
function
fixedZero
(
val
:
number
)
{
return
val
*
1
<
10
?
`0
${
val
}
`
:
val
;
}
export
function
getTimeDistance
(
type
)
{
export
function
getTimeDistance
(
type
:
'
today
'
|
'
week
'
|
'
month
'
|
'
year
'
):
RangePickerValue
{
const
now
=
new
Date
();
const
oneDay
=
1000
*
60
*
60
*
24
;
...
...
package.json
View file @
278a1ce9
{
"private"
:
true
,
"scripts"
:
{
"dev"
:
"cross-env PAGES_PATH='A
dvancedProfile
/src' umi dev"
,
"dev"
:
"cross-env PAGES_PATH='A
nalysis
/src' umi dev"
,
"lint:style"
:
"stylelint
\"
src/**/*.less
\"
--syntax less"
,
"lint"
:
"eslint --ext .js src mock tests && npm run lint:style"
,
"lint:fix"
:
"eslint --fix --ext .js src mock tests && npm run lint:style"
,
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment