Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
N
najiu-admin-template
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
najiu-frontend
najiu-admin-template
Commits
52ee35c4
Commit
52ee35c4
authored
Oct 11, 2020
by
vben
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(analysis): add analysis page
parent
1cd75fcf
Changes
36
Hide whitespace changes
Inline
Side-by-side
Showing
36 changed files
with
1518 additions
and
191 deletions
+1518
-191
README.md
README.md
+3
-3
analysis-down.svg
src/assets/svg/dashboard/analysis-down.svg
+20
-0
analysis-icon1.svg
src/assets/svg/dashboard/analysis-icon1.svg
+21
-0
analysis-icon2.svg
src/assets/svg/dashboard/analysis-icon2.svg
+21
-0
analysis-icon3.svg
src/assets/svg/dashboard/analysis-icon3.svg
+21
-0
analysis-icon4.svg
src/assets/svg/dashboard/analysis-icon4.svg
+21
-0
analysis-rise.svg
src/assets/svg/dashboard/analysis-rise.svg
+20
-0
index.ts
src/components/CountTo/index.ts
+2
-0
index.vue
src/components/CountTo/src/index.vue
+159
-0
props.ts
src/components/CountTo/src/props.ts
+62
-0
FullLoading.vue
src/components/Loading/FullLoading.vue
+2
-1
global.less
src/design/global.less
+28
-1
fade.less
src/design/transition/fade.less
+1
-82
index.less
src/design/transition/index.less
+1
-0
scale.less
src/design/transition/scale.less
+0
-1
scroll.less
src/design/transition/scroll.less
+4
-12
slide.less
src/design/transition/slide.less
+0
-4
zoom.less
src/design/transition/zoom.less
+27
-0
useRaf.ts
src/hooks/event/useRaf.ts
+10
-9
useApexCharts.ts
src/hooks/web/useApexCharts.ts
+8
-8
useECharts.ts
src/hooks/web/useECharts.ts
+12
-12
dashboard.ts
src/router/menus/modules/demo/dashboard.ts
+4
-0
dashboard.ts
src/router/routes/modules/demo/dashboard.ts
+8
-0
AnalysisBar.vue
src/views/dashboard/analysis/components/AnalysisBar.vue
+91
-0
AnalysisLine.vue
src/views/dashboard/analysis/components/AnalysisLine.vue
+101
-0
AnalysisPie.vue
src/views/dashboard/analysis/components/AnalysisPie.vue
+79
-0
FlowAnalysis.tsx
src/views/dashboard/analysis/components/FlowAnalysis.tsx
+82
-0
GrowCard.vue
src/views/dashboard/analysis/components/GrowCard.vue
+116
-0
TaskCard.vue
src/views/dashboard/analysis/components/TaskCard.vue
+154
-0
TrendLine.vue
src/views/dashboard/analysis/components/TrendLine.vue
+105
-0
flow-ana.less
src/views/dashboard/analysis/components/flow-ana.less
+56
-0
props.ts
src/views/dashboard/analysis/components/props.ts
+16
-0
data.tsx
src/views/dashboard/analysis/data.tsx
+107
-0
index.vue
src/views/dashboard/analysis/index.vue
+83
-0
types.ts
src/views/dashboard/analysis/types.ts
+16
-0
Week.vue
src/views/dashboard/workbench/components/Week.vue
+57
-58
No files found.
README.md
View file @
52ee35c4
...
...
@@ -216,17 +216,17 @@ yarn clean:lib # 删除node_modules,兼容window系统
-
[
x
]
图片预览组件
-
[
x
]
表格组件
-
[
x
]
图表库
-
[
x
]
数字动画
## 正在开发的功能
-
[
]
数字动画
-
[
]
主题配置
-
[
]
富文本组件
-
[
]
首屏加载等待动画
-
[
]
上传组件
-
[
]
富文本组件
-
[
]
数据导入导出
-
[
]
黑暗主题
-
[
]
全局错误处理
-
[
]
首屏加载等待动画
-
[
]
打包 Gzip
-
[
]
抽取生产环境配置文件
-
[
]
系统性能优化
...
...
src/assets/svg/dashboard/analysis-down.svg
0 → 100644
View file @
52ee35c4
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"20px"
height=
"12px"
viewBox=
"0 0 20 12"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>
下跌-24px
</title>
<desc>
Created with Sketch.
</desc>
<g
id=
"页面-2"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"系统首页"
transform=
"translate(-850.000000, -241.000000)"
>
<g
id=
"1"
transform=
"translate(234.000000, 120.000000)"
>
<g
id=
"Total-Sales"
transform=
"translate(598.000000, 0.000000)"
>
<g
id=
"8.5%-Up-from-yesterday"
transform=
"translate(16.000000, 114.000000)"
>
<g
id=
"下跌-24px"
transform=
"translate(0.000000, 1.000000)"
>
<polygon
id=
"Path"
points=
"0 0 24 0 24 24 0 24"
></polygon>
<polygon
id=
"Path"
fill=
"#ED6F6F"
fill-rule=
"nonzero"
points=
"16 18 18.29 15.71 13.41 10.83 9.41 14.83 2 7.41 3.41 6 9.41 12 13.41 8 19.71 14.29 22 12 22 18"
></polygon>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
src/assets/svg/dashboard/analysis-icon1.svg
0 → 100644
View file @
52ee35c4
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"60px"
height=
"60px"
viewBox=
"0 0 60 60"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>
Icon1@3x
</title>
<desc>
Created with Sketch.
</desc>
<g
id=
"页面-2"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"系统首页"
transform=
"translate(-419.000000, -136.000000)"
fill=
"#0593FF"
>
<g
id=
"1"
transform=
"translate(234.000000, 120.000000)"
>
<g
id=
"Total-Users"
>
<g
id=
"Icon1"
transform=
"translate(185.000000, 16.000000)"
>
<path
d=
"M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z"
id=
"Circle-2"
opacity=
"0.209999993"
></path>
<g
id=
"Group"
transform=
"translate(14.000000, 18.000000)"
fill-rule=
"nonzero"
>
<path
d=
"M24,6.66666667 C26.209139,6.66666667 28,8.45752767 28,10.6666667 C28,12.8758057 26.209139,14.6666667 24,14.6666667 C21.790861,14.6666667 20,12.8758057 20,10.6666667 C20,8.45752767 21.790861,6.66666667 24,6.66666667 Z M12,0 C14.9455187,0 17.3333333,2.38781467 17.3333333,5.33333333 C17.3333333,8.278852 14.9455187,10.6666667 12,10.6666667 C9.05448133,10.6666667 6.66666667,8.278852 6.66666667,5.33333333 C6.66666667,2.38781467 9.05448133,0 12,0 Z"
id=
"Combined-Shape"
opacity=
"0.587820871"
></path>
<path
d=
"M23.4686027,16.0012776 L23.3172917,16 C27.927838,16 31.7158139,18.2931929 31.9979916,23.2 C32.0092328,23.3954741 31.9979916,24 31.2745999,24 L26.1333333,24 L26.1333333,24 C26.1333333,20.9989578 25.1418595,18.2294867 23.4686027,16.0012776 Z M11.9777884,13.3333333 C18.3616218,13.3333333 23.6065116,16.3909238 23.9972191,22.9333333 C24.0127839,23.1939654 23.9972191,24 22.9955999,24 L0.97000297,24 L0.97000297,24 C0.635616207,24 -0.027282334,23.2789066 0.000868912387,22.932274 C0.517678033,16.5686878 5.6825498,13.3333333 11.9777884,13.3333333 Z"
id=
"Combined-Shape"
></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
src/assets/svg/dashboard/analysis-icon2.svg
0 → 100644
View file @
52ee35c4
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"60px"
height=
"60px"
viewBox=
"0 0 60 60"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>
Icon2@3x
</title>
<desc>
Created with Sketch.
</desc>
<g
id=
"页面-2"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"系统首页"
transform=
"translate(-719.000000, -136.000000)"
>
<g
id=
"1"
transform=
"translate(234.000000, 120.000000)"
>
<g
id=
"Total-Order"
transform=
"translate(299.000000, 0.000000)"
>
<g
id=
"Icon2"
transform=
"translate(186.000000, 16.000000)"
>
<path
d=
"M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z"
id=
"Circle-2"
fill=
"#FFD164"
opacity=
"0.209999993"
></path>
<g
id=
"icon"
transform=
"translate(15.000000, 13.000000)"
>
<path
d=
"M0,11.3164701 L12.9004912,18.7645722 C13.0394036,18.8447733 13.1850623,18.9027046 13.3333333,18.9394739 L13.3333333,33.3847054 L0.920064885,26.0385088 C0.349783865,25.7010154 0,25.0875659 0,24.4249029 L0,11.3164701 Z M30,11.1184665 L30,24.4249029 C30,25.0875659 29.6502161,25.7010154 29.0799351,26.0385088 L16.6666667,33.3847054 L16.6666667,18.8129235 C16.6969108,18.7978151 16.7268876,18.7817016 16.7565565,18.7645722 L30,11.1184665 L30,11.1184665 Z"
id=
"Combined-Shape"
fill=
"#FFC741"
></path>
<path
d=
"M0.405221909,7.70142332 C0.562796988,7.50243849 0.761684783,7.33426405 0.993563997,7.21076013 L14.118564,0.220099528 C14.6695479,-0.0733665093 15.3304521,-0.0733665093 15.881436,0.220099528 L29.006436,7.21076013 C29.1851826,7.30596446 29.3443248,7.42771319 29.480051,7.56965747 L15.0898899,15.8778209 C14.9952678,15.9324509 14.9080291,15.9949583 14.8285239,16.0640363 C14.7490186,15.9949583 14.66178,15.9324509 14.5671579,15.8778209 L0.405221909,7.70142332 Z"
id=
"Path"
fill=
"#FFD164"
opacity=
"0.659481957"
></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
src/assets/svg/dashboard/analysis-icon3.svg
0 → 100644
View file @
52ee35c4
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"60px"
height=
"60px"
viewBox=
"0 0 60 60"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>
Icon3@3x
</title>
<desc>
Created with Sketch.
</desc>
<g
id=
"页面-2"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"系统首页"
transform=
"translate(-1018.000000, -136.000000)"
fill=
"#55D187"
>
<g
id=
"1"
transform=
"translate(234.000000, 120.000000)"
>
<g
id=
"Total-Sales"
transform=
"translate(598.000000, 0.000000)"
>
<g
id=
"Icon3"
transform=
"translate(186.000000, 16.000000)"
>
<path
d=
"M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z"
id=
"Circle-2"
opacity=
"0.209999993"
></path>
<g
id=
"icon"
transform=
"translate(16.000000, 16.000000)"
fill-rule=
"nonzero"
>
<path
d=
"M3.11111111,24.8888889 L26.4444444,24.8888889 C27.3035541,24.8888889 28,25.5853348 28,26.4444444 C28,27.3035541 27.3035541,28 26.4444444,28 L1.55555556,28 C0.696445945,28 0,27.3035541 0,26.4444444 L0,1.55555556 C0,0.696445945 0.696445945,0 1.55555556,0 C2.41466517,0 3.11111111,0.696445945 3.11111111,1.55555556 L3.11111111,24.8888889 Z"
id=
"Path-95"
></path>
<path
d=
"M8.91261343,18.1750195 C8.32503303,18.801772 7.34062178,18.8335272 6.71386936,18.2459468 C6.08711693,17.6583664 6.05536173,16.6739551 6.64294213,16.0472027 L12.4762755,9.82498047 C13.044535,9.21883699 13.9888279,9.16627114 14.6208522,9.70559855 L19.2248856,13.6343737 L25.2235157,6.03610888 C25.7558581,5.36180856 26.7340352,5.24672889 27.4083356,5.77907125 C28.0826359,6.31141362 28.1977156,7.28959079 27.6653732,7.96389112 L20.6653732,16.8305578 C20.118618,17.5231144 19.1059101,17.6227201 18.4347034,17.049957 L13.7306235,13.0358088 L8.91261343,18.1750195 Z"
id=
"Path-97"
opacity=
"0.657133557"
></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
src/assets/svg/dashboard/analysis-icon4.svg
0 → 100644
View file @
52ee35c4
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"60px"
height=
"60px"
viewBox=
"0 0 60 60"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>
Icon
</title>
<desc>
Created with Sketch.
</desc>
<g
id=
"页面-2"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"系统首页"
transform=
"translate(-1317.000000, -136.000000)"
fill=
"#FF9066"
>
<g
id=
"1"
transform=
"translate(234.000000, 120.000000)"
>
<g
id=
"Order-Pending"
transform=
"translate(897.000000, 0.000000)"
>
<g
id=
"Icon"
transform=
"translate(186.000000, 16.000000)"
>
<path
d=
"M23,60 C10.2974508,60 1.55561363e-15,49.7025492 0,37 L0,23 C-1.55561363e-15,10.2974508 10.2974508,2.33342044e-15 23,0 L37,0 C49.7025492,-2.33342044e-15 60,10.2974508 60,23 L60,37 C60,49.7025492 49.7025492,60 37,60 L23,60 Z"
id=
"Circle-2"
opacity=
"0.3"
></path>
<g
id=
"icon"
transform=
"translate(16.000000, 15.000000)"
>
<path
d=
"M13.1296822,8.34718934 L13.5475062,8.34718934 C13.8043819,8.34718934 14.0194647,8.54183658 14.0450248,8.79743748 L14.6666667,15.013856 L14.6666667,15.013856 L19.0814028,17.5365624 C19.2371903,17.6255838 19.3333333,17.7912555 19.3333333,17.9706839 L19.3333333,18.3592308 C19.3333333,18.6353732 19.1094757,18.8592308 18.8333333,18.8592308 C18.7888923,18.8592308 18.7446497,18.8533059 18.7017746,18.8416127 L12.3986612,17.1225818 C12.1672824,17.0594785 12.0132986,16.8409746 12.0316926,16.6018516 L12.631155,8.80884109 C12.6511933,8.54834251 12.8684141,8.34718934 13.1296822,8.34718934 Z"
id=
"Path-107"
opacity=
"0.779999971"
></path>
<path
d=
"M6.01733907,-0.0772351689 C6.2288764,-0.254736066 6.5442542,-0.227144084 6.72175509,-0.0156067521 L6.72175509,-0.0156067521 L8.51913552,2.1273858 C10.2024553,1.41052645 12.054904,1.01385601 14,1.01385601 C21.7319865,1.01385601 28,7.28186951 28,15.013856 C28,22.6413562 21.9002476,28.8441829 14.312645,29.010434 L14,29.013856 L14,29.013856 C6.2680135,29.013856 0,22.7458425 0,15.013856 C0,13.7006992 0.180792724,12.4297687 0.518844853,11.224598 L3.08641434,11.944805 C2.80896017,12.9339413 2.66666667,13.9630912 2.66666667,15.013856 C2.66666667,21.2730832 7.74077284,26.3471893 14,26.3471893 C20.2592272,26.3471893 25.3333333,21.2730832 25.3333333,15.013856 C25.3333333,8.85242927 20.4165542,3.83937783 14.2925184,3.68422422 L14,3.68052267 L14,3.68052267 C12.7318949,3.68052267 11.4968995,3.88835566 10.3322213,4.2862714 L12.1330448,6.43331723 C12.2023675,6.51593278 12.24312,6.61874811 12.2492217,6.7264223 C12.2648452,7.00212236 12.0540114,7.23828671 11.7783113,7.25391015 L11.7783113,7.25391015 L4.73355552,7.65312407 C4.68508783,7.65587065 4.6364785,7.65154413 4.58925778,7.64028071 C4.32065092,7.57621071 4.15484104,7.30652283 4.21891104,7.03791597 L4.21891104,7.03791597 L5.85237713,0.189778054 C5.87728008,0.0853749765 5.93511798,-0.00824348388 6.01733907,-0.0772351689 Z"
id=
"Combined-Shape"
opacity=
"0.901274182"
></path>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
src/assets/svg/dashboard/analysis-rise.svg
0 → 100644
View file @
52ee35c4
<?xml version="1.0" encoding="UTF-8"?>
<svg
width=
"20px"
height=
"12px"
viewBox=
"0 0 20 12"
version=
"1.1"
xmlns=
"http://www.w3.org/2000/svg"
xmlns:xlink=
"http://www.w3.org/1999/xlink"
>
<!-- Generator: Sketch 61 (89581) - https://sketch.com -->
<title>
上涨-24px
</title>
<desc>
Created with Sketch.
</desc>
<g
id=
"页面-2"
stroke=
"none"
stroke-width=
"1"
fill=
"none"
fill-rule=
"evenodd"
>
<g
id=
"系统首页"
transform=
"translate(-551.000000, -240.000000)"
>
<g
id=
"1"
transform=
"translate(234.000000, 120.000000)"
>
<g
id=
"Total-Order"
transform=
"translate(299.000000, 0.000000)"
>
<g
id=
"8.5%-Up-from-yesterday"
transform=
"translate(16.000000, 114.000000)"
>
<g
id=
"上涨-24px"
>
<polygon
id=
"Path"
points=
"0 0 24 0 24 24 0 24"
></polygon>
<polygon
id=
"Path"
fill=
"#55D187"
fill-rule=
"nonzero"
points=
"16 6 18.29 8.29 13.41 13.17 9.41 9.17 2 16.59 3.41 18 9.41 12 13.41 16 19.71 9.71 22 12 22 6"
></polygon>
</g>
</g>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
src/components/CountTo/index.ts
0 → 100644
View file @
52ee35c4
// 对vue-count-to进行改造成支持vue3版本
export
{
default
as
CountTo
}
from
'
./src/index.vue
'
;
src/components/CountTo/src/index.vue
0 → 100644
View file @
52ee35c4
<
template
>
<span>
{{
displayValue
}}
</span>
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
reactive
,
computed
,
watch
,
onMounted
,
unref
,
toRef
}
from
'
vue
'
;
import
{
countToProps
}
from
'
./props
'
;
import
{
useRaf
}
from
'
/@/hooks/event/useRaf
'
;
import
{
isNumber
}
from
'
/@/utils/is
'
;
export
default
defineComponent
({
name
:
'
CountTo
'
,
props
:
countToProps
,
emits
:
[
'
mounted
'
,
'
callback
'
],
setup
(
props
,
{
emit
})
{
const
{
requestAnimationFrame
,
cancelAnimationFrame
}
=
useRaf
();
const
state
=
reactive
<
{
localStartVal
:
number
;
printVal
:
number
|
null
;
displayValue
:
string
;
paused
:
boolean
;
localDuration
:
number
|
null
;
startTime
:
number
|
null
;
timestamp
:
number
|
null
;
rAF
:
any
;
remaining
:
number
|
null
;
}
>
({
localStartVal
:
props
.
startVal
,
displayValue
:
formatNumber
(
props
.
startVal
),
printVal
:
null
,
paused
:
false
,
localDuration
:
props
.
duration
,
startTime
:
null
,
timestamp
:
null
,
remaining
:
null
,
rAF
:
null
,
});
onMounted
(()
=>
{
if
(
props
.
autoplay
)
{
start
();
}
emit
(
'
mounted
'
);
});
const
getCountDown
=
computed
(()
=>
{
return
props
.
startVal
>
props
.
endVal
;
});
watch
([()
=>
props
.
startVal
,
()
=>
props
.
endVal
],
()
=>
{
if
(
props
.
autoplay
)
{
start
();
}
});
function
start
()
{
const
{
startVal
,
duration
}
=
props
;
state
.
localStartVal
=
startVal
;
state
.
startTime
=
null
;
state
.
localDuration
=
duration
;
state
.
paused
=
false
;
state
.
rAF
=
requestAnimationFrame
(
count
);
}
function
pauseResume
()
{
if
(
state
.
paused
)
{
resume
();
state
.
paused
=
false
;
}
else
{
pause
();
state
.
paused
=
true
;
}
}
function
pause
()
{
cancelAnimationFrame
(
state
.
rAF
);
}
function
resume
()
{
state
.
startTime
=
null
;
state
.
localDuration
=
+
(
state
.
remaining
as
number
);
state
.
localStartVal
=
+
(
state
.
printVal
as
number
);
requestAnimationFrame
(
count
);
}
function
reset
()
{
state
.
startTime
=
null
;
cancelAnimationFrame
(
state
.
rAF
);
state
.
displayValue
=
formatNumber
(
props
.
startVal
);
}
function
count
(
timestamp
:
number
)
{
const
{
useEasing
,
easingFn
,
endVal
}
=
props
;
if
(
!
state
.
startTime
)
state
.
startTime
=
timestamp
;
state
.
timestamp
=
timestamp
;
const
progress
=
timestamp
-
state
.
startTime
;
state
.
remaining
=
(
state
.
localDuration
as
number
)
-
progress
;
if
(
useEasing
)
{
if
(
unref
(
getCountDown
))
{
state
.
printVal
=
state
.
localStartVal
-
easingFn
(
progress
,
0
,
state
.
localStartVal
-
endVal
,
state
.
localDuration
as
number
);
}
else
{
state
.
printVal
=
easingFn
(
progress
,
state
.
localStartVal
,
endVal
-
state
.
localStartVal
,
state
.
localDuration
as
number
);
}
}
else
{
if
(
unref
(
getCountDown
))
{
state
.
printVal
=
state
.
localStartVal
-
(
state
.
localStartVal
-
endVal
)
*
(
progress
/
(
state
.
localDuration
as
number
));
}
else
{
state
.
printVal
=
state
.
localStartVal
+
(
endVal
-
state
.
localStartVal
)
*
(
progress
/
(
state
.
localDuration
as
number
));
}
}
if
(
unref
(
getCountDown
))
{
state
.
printVal
=
state
.
printVal
<
endVal
?
endVal
:
state
.
printVal
;
}
else
{
state
.
printVal
=
state
.
printVal
>
endVal
?
endVal
:
state
.
printVal
;
}
state
.
displayValue
=
formatNumber
(
state
.
printVal
);
if
(
progress
<
(
state
.
localDuration
as
number
))
{
state
.
rAF
=
requestAnimationFrame
(
count
);
}
else
{
emit
(
'
callback
'
);
}
}
function
formatNumber
(
num
:
number
|
string
)
{
const
{
decimals
,
decimal
,
separator
,
suffix
,
prefix
}
=
props
;
num
=
Number
(
num
).
toFixed
(
decimals
);
num
+=
''
;
const
x
=
num
.
split
(
'
.
'
);
let
x1
=
x
[
0
];
const
x2
=
x
.
length
>
1
?
decimal
+
x
[
1
]
:
''
;
const
rgx
=
/
(\d
+
)(\d{3})
/
;
if
(
separator
&&
!
isNumber
(
separator
))
{
while
(
rgx
.
test
(
x1
))
{
x1
=
x1
.
replace
(
rgx
,
'
$1
'
+
separator
+
'
$2
'
);
}
}
return
prefix
+
x1
+
x2
+
suffix
;
}
return
{
count
,
reset
,
resume
,
start
,
pauseResume
,
displayValue
:
toRef
(
state
,
'
displayValue
'
),
};
},
});
</
script
>
src/components/CountTo/src/props.ts
0 → 100644
View file @
52ee35c4
import
{
PropType
}
from
'
vue
'
;
export
const
countToProps
=
{
startVal
:
{
type
:
Number
as
PropType
<
number
>
,
required
:
false
,
default
:
0
,
},
endVal
:
{
type
:
Number
as
PropType
<
number
>
,
required
:
false
,
default
:
2017
,
},
duration
:
{
type
:
Number
as
PropType
<
number
>
,
required
:
false
,
default
:
3000
,
},
autoplay
:
{
type
:
Boolean
as
PropType
<
boolean
>
,
required
:
false
,
default
:
true
,
},
decimals
:
{
type
:
Number
as
PropType
<
number
>
,
required
:
false
,
default
:
0
,
validator
(
value
:
number
)
{
return
value
>=
0
;
},
},
decimal
:
{
type
:
String
as
PropType
<
string
>
,
required
:
false
,
default
:
'
.
'
,
},
separator
:
{
type
:
String
as
PropType
<
string
>
,
required
:
false
,
default
:
'
,
'
,
},
prefix
:
{
type
:
String
as
PropType
<
string
>
,
required
:
false
,
default
:
''
,
},
suffix
:
{
type
:
String
as
PropType
<
string
>
,
required
:
false
,
default
:
''
,
},
useEasing
:
{
type
:
Boolean
as
PropType
<
boolean
>
,
required
:
false
,
default
:
true
,
},
easingFn
:
{
type
:
Function
as
PropType
<
(
t
:
number
,
b
:
number
,
c
:
number
,
d
:
number
)
=>
number
>
,
default
(
t
:
number
,
b
:
number
,
c
:
number
,
d
:
number
)
{
return
(
c
*
(
-
Math
.
pow
(
2
,
(
-
10
*
t
)
/
d
)
+
1
)
*
1024
)
/
1023
+
b
;
},
},
};
src/components/Loading/FullLoading.vue
View file @
52ee35c4
...
...
@@ -41,7 +41,8 @@
display: flex;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.3);
// background: rgba(255, 255, 255, 0.5);
background: #f1f1f63d;
justify-content: center;
align-items: center;
}
...
...
src/design/global.less
View file @
52ee35c4
@import './helper/distance.less';
// 生成样式
.distance();
.hidden {
...
...
@@ -10,6 +9,10 @@
display: flex;
}
.align-middle {
vertical-align: middle;
}
.flex-wrap {
flex-wrap: wrap;
}
...
...
@@ -48,3 +51,27 @@
word-wrap: normal;
white-space: nowrap;
}
.shadow-xs {
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.05);
}
.shadow-sm {
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
}
.shadow {
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06);
}
.shadow-md {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.shadow-xl {
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
}
src/design/transition/fade.less
View file @
52ee35c4
...
...
@@ -4,27 +4,10 @@
}
.fade-enter-from,
.fade-enter,
.fade-leave-to {
opacity: 0;
}
/* fade-transform */
// .fade-transform-leave-active,
// .fade-transform-enter-active {
// transition: all 0.5s;
// }
// .fade-transform-enter {
// opacity: 0;
// transform: translateX(-30px);
// }
// .fade-transform-leave-to {
// opacity: 0;
// transform: translateX(30px);
// }
// side-fade
.slide-fade-enter-active,
.slide-fade-leave-active {
...
...
@@ -42,35 +25,6 @@
transform: translateX(30%);
}
// zoom-out
.zoom-out-enter-active,
.zoom-out-leave-active {
transition: opacity 0.35s ease-in-out, transform 0.45s ease-out;
}
.zoom-out-enter-from,
.zoom-out-enter,
.zoom-out-leave-to {
opacity: 0;
transform: scale(0);
}
// zoom-fade
.zoom-fade-enter-active,
.zoom-fade-leave-active {
transition: transform 0.35s, opacity 0.35s ease-out;
}
.zoom-fade-enter-from {
opacity: 0;
transform: scale(0.97);
}
.zoom-fade-leave-to {
opacity: 0;
transform: scale(1.03);
}
// ///////////////////////////////////////////////
// Fade Bottom
// ///////////////////////////////////////////////
...
...
@@ -92,23 +46,6 @@
transform: translateY(8%);
}
// Speed: 2x
.fade-bottom-2x-enter-active,
.fade-bottom-2x-leave-active {
transition: opacity 0.2s, transform 0.25s;
}
.fade-bottom-2x-enter-from,
.fade-bottom-2x-enter {
opacity: 0;
transform: translateY(-4%);
}
.fade-bottom-2x-leave-to {
opacity: 0;
transform: translateY(4%);
}
// ///////////////////////////////////////////////
// Fade Top
// ///////////////////////////////////////////////
...
...
@@ -119,8 +56,7 @@
transition: opacity 0.3s, transform 0.35s;
}
.fade-top-enter-from,
.fade-top-enter {
.fade-top-enter-from {
opacity: 0;
transform: translateY(8%);
}
...
...
@@ -129,20 +65,3 @@
opacity: 0;
transform: translateY(-8%);
}
// Speed: 2x
.fade-top-2x-enter-active,
.fade-top-2x-leave-active {
transition: opacity 0.2s, transform 0.25s;
}
.fade-top-2x-enter-from,
.fade-top-2x-enter {
opacity: 0;
transform: translateY(4%);
}
.fade-top-2x-leave-to {
opacity: 0;
transform: translateY(-4%);
}
src/design/transition/index.less
View file @
52ee35c4
...
...
@@ -3,4 +3,5 @@
@import './scale.less';
@import './slide.less';
@import './scroll.less';
@import './zoom.less';
@import './breadcrumb.less';
src/design/transition/scale.less
View file @
52ee35c4
...
...
@@ -12,7 +12,6 @@
.scale-rotate-transition {
.transition-default();
&-enter,
&-enter-from,
&-leave,
&-leave-to {
...
...
src/design/transition/scroll.less
View file @
52ee35c4
.scroll-y-transition {
.transition-default();
&-enter,
&-enter-from,
&-leave-to {
opacity: 0;
}
&-enter-from,
&-enter {
&-enter-from {
transform: translateY(-15px);
}
...
...
@@ -20,14 +18,12 @@
.scroll-y-reverse-transition {
.transition-default();
&-enter,
&-enter-from,
&-leave-to {
opacity: 0;
}
&-enter-from,
&-enter {
&-enter-from {
transform: translateY(15px);
}
...
...
@@ -39,14 +35,12 @@
.scroll-x-transition {
.transition-default();
&-enter,
&-enter-from,
&-leave-to {
opacity: 0;
}
&-enter-from,
&-enter {
&-enter-from {
transform: translateX(-15px);
}
...
...
@@ -58,14 +52,12 @@
.scroll-x-reverse-transition {
.transition-default();
&-enter,
&-enter-from,
&-leave-to {
opacity: 0;
}
&-enter-from,
&-enter {
&-enter-from {
transform: translateX(15px);
}
...
...
src/design/transition/slide.less
View file @
52ee35c4
...
...
@@ -2,7 +2,6 @@
.transition-default();
&-enter-from,
&-enter,
&-leave-to {
opacity: 0;
transform: translateY(-15px);
...
...
@@ -13,7 +12,6 @@
.transition-default();
&-enter-from,
&-enter,
&-leave-to {
opacity: 0;
transform: translateY(15px);
...
...
@@ -24,7 +22,6 @@
.transition-default();
&-enter-from,
&-enter,
&-leave-to {
opacity: 0;
transform: translateX(-15px);
...
...
@@ -35,7 +32,6 @@
.transition-default();
&-enter-from,
&-enter,
&-leave-to {
opacity: 0;
transform: translateX(15px);
...
...
src/design/transition/zoom.less
0 → 100644
View file @
52ee35c4
// zoom-out
.zoom-out-enter-active,
.zoom-out-leave-active {
transition: opacity 0.35s ease-in-out, transform 0.45s ease-out;
}
.zoom-out-enter-from,
.zoom-out-leave-to {
opacity: 0;
transform: scale(0);
}
// zoom-fade
.zoom-fade-enter-active,
.zoom-fade-leave-active {
transition: transform 0.35s, opacity 0.35s ease-out;
}
.zoom-fade-enter-from {
opacity: 0;
transform: scale(0.97);
}
.zoom-fade-leave-to {
opacity: 0;
transform: scale(1.03);
}
src/hooks/event/useRaf.ts
View file @
52ee35c4
...
...
@@ -51,22 +51,23 @@ if (isServer) {
}
export
function
useRaf
()
{
if
(
getCurrentInstance
())
{
onUnmounted
(()
=>
{
cancelAnimationFrame
();
});
}
return
{
requestAnimationFrame
};
//
if (getCurrentInstance()) {
//
onUnmounted(() => {
//
cancelAnimationFrame();
//
});
//
}
return
{
requestAnimationFrame
,
cancelAnimationFrame
};
}
export
function
useRafFn
(
fn
:
()
=>
any
,
options
:
{
immediate
?:
boolean
}
=
{})
{
export
function
useRafFn
(
fn
:
(
...
arg
:
any
)
=>
any
,
options
:
{
immediate
?:
boolean
}
=
{})
{
const
{
immediate
=
false
}
=
options
;
let
started
=
false
;
let
id
:
ReturnType
<
typeof
window
.
requestAnimationFrame
>
;
function
loop
()
{
if
(
!
started
)
return
;
fn
();
requestAnimationFrame
(
loop
);
id
=
requestAnimationFrame
(
loop
);
}
function
start
()
{
...
...
@@ -86,7 +87,7 @@ export function useRafFn(fn: () => any, options: { immediate?: boolean } = {}) {
if
(
getCurrentInstance
())
{
onUnmounted
(()
=>
{
cancelAnimationFrame
();
cancelAnimationFrame
(
id
);
stop
();
});
}
...
...
src/hooks/web/useApexCharts.ts
View file @
52ee35c4
...
...
@@ -8,17 +8,17 @@ export function useApexCharts(elRef: Ref<HTMLDivElement>) {
const
chartInstanceRef
=
ref
<
Nullable
<
ApexCharts
>>
(
null
);
function
setOptions
(
options
:
any
)
{
const
el
=
unref
(
elRef
);
nextTick
(()
=>
{
useTimeout
(()
=>
{
const
el
=
unref
(
elRef
);
if
(
!
el
||
!
unref
(
el
))
{
return
;
}
chartInstanceRef
.
value
=
new
ApexCharts
(
el
,
options
);
if
(
!
el
||
!
unref
(
el
))
{
return
;
}
chartInstanceRef
.
value
=
new
ApexCharts
(
el
,
options
);
const
chartInstance
=
unref
(
chartInstanceRef
);
const
chartInstance
=
unref
(
chartInstanceRef
);
nextTick
(()
=>
{
useTimeout
(()
=>
{
chartInstance
&&
chartInstance
.
render
();
},
30
);
});
...
...
src/hooks/web/useECharts.ts
View file @
52ee35c4
...
...
@@ -34,24 +34,24 @@ export function useECharts(
if
(
unref
(
widthRef
)
<=
screenEnum
.
MD
)
{
useTimeout
(()
=>
{
resizeFn
();
},
0
);
},
3
0
);
}
}
function
setOptions
(
options
:
any
,
clear
=
true
)
{
// function setOptions(options: EChartOption, clear = true) {
let
chartInstance
=
unref
(
chartInstanceRef
);
if
(
!
chartInstance
)
{
init
();
chartInstance
=
chartInstance
=
unref
(
chartInstanceRef
);
if
(
!
chartInstance
)
{
return
;
}
}
clear
&&
chartInstance
.
clear
();
nextTick
(()
=>
{
useTimeout
(()
=>
{
let
chartInstance
=
unref
(
chartInstanceRef
);
if
(
!
chartInstance
)
{
init
();
chartInstance
=
chartInstance
=
unref
(
chartInstanceRef
);
if
(
!
chartInstance
)
{
return
;
}
}
clear
&&
chartInstance
.
clear
();
chartInstance
&&
chartInstance
.
setOption
(
options
);
},
30
);
});
...
...
src/router/menus/modules/demo/dashboard.ts
View file @
52ee35c4
...
...
@@ -9,6 +9,10 @@ const menu: MenuModule = {
path
:
'
/workbench
'
,
name
:
'
工作台
'
,
},
{
path
:
'
/analysis
'
,
name
:
'
分析页
'
,
},
{
path
:
'
/welcome
'
,
name
:
'
首页
'
,
...
...
src/router/routes/modules/demo/dashboard.ts
View file @
52ee35c4
...
...
@@ -32,5 +32,13 @@ export default {
affix
:
true
,
},
},
{
path
:
'
/analysis
'
,
name
:
'
Analysis
'
,
component
:
()
=>
import
(
'
/@/views/dashboard/analysis/index.vue
'
),
meta
:
{
title
:
'
分析页
'
,
},
},
],
}
as
AppRouteModule
;
src/views/dashboard/analysis/components/AnalysisBar.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
ref=
"chartRef"
:style=
"
{ height, width }" />
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
onMounted
,
ref
,
Ref
}
from
'
vue
'
;
import
{
useECharts
}
from
'
/@/hooks/web/useECharts
'
;
import
{
basicProps
}
from
'
./props
'
;
export
default
defineComponent
({
name
:
'
AnalysisLine
'
,
props
:
basicProps
,
setup
()
{
const
chartRef
=
ref
<
HTMLDivElement
|
null
>
(
null
);
const
{
setOptions
}
=
useECharts
(
chartRef
as
Ref
<
HTMLDivElement
>
);
onMounted
(()
=>
{
setOptions
({
tooltip
:
{
trigger
:
'
axis
'
,
backgroundColor
:
'
rgba(0, 0, 0, .6)
'
,
axisPointer
:
{
// 坐标轴指示器,坐标轴触发有效
type
:
'
shadow
'
,
// 默认为直线,可选为:'line' | 'shadow'
},
},
legend
:
{
itemWidth
:
15
,
right
:
10
,
data
:
[
'
产品一
'
,
'
产品二
'
,
'
产品三
'
],
},
grid
:
{
left
:
'
3%
'
,
right
:
'
4%
'
,
bottom
:
'
3%
'
,
containLabel
:
true
,
},
xAxis
:
[
{
type
:
'
category
'
,
axisTick
:
{
inside
:
true
,
// 刻度朝内
},
data
:
[
'
付费用户
'
,
'
免费用户
'
,
'
自主
'
],
},
],
yAxis
:
[
{
type
:
'
value
'
,
axisTick
:
{
inside
:
true
,
// 刻度朝内
},
},
],
series
:
[
{
name
:
'
产品一
'
,
type
:
'
bar
'
,
itemStyle
:
{
color
:
'
#3ca0f6
'
,
},
data
:
[
3200
,
3320
,
3010
],
animationDuration
:
4000
,
},
{
name
:
'
产品二
'
,
type
:
'
bar
'
,
itemStyle
:
{
color
:
'
#7dd9b9
'
,
},
data
:
[
1200
,
2600
,
1010
],
animationDuration
:
4000
,
},
{
name
:
'
产品三
'
,
type
:
'
bar
'
,
itemStyle
:
{
color
:
'
#e6a23c
'
,
},
data
:
[
862
,
2500
,
964
],
animationDuration
:
4000
,
},
],
});
});
return
{
chartRef
};
},
});
</
script
>
src/views/dashboard/analysis/components/AnalysisLine.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
ref=
"chartRef"
:style=
"
{ height, width }" />
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
onMounted
,
ref
,
Ref
}
from
'
vue
'
;
import
{
useECharts
}
from
'
/@/hooks/web/useECharts
'
;
import
{
basicProps
}
from
'
./props
'
;
export
default
defineComponent
({
name
:
'
AnalysisLine
'
,
props
:
basicProps
,
setup
()
{
const
chartRef
=
ref
<
HTMLDivElement
|
null
>
(
null
);
const
{
setOptions
}
=
useECharts
(
chartRef
as
Ref
<
HTMLDivElement
>
);
onMounted
(()
=>
{
setOptions
({
// title: {
// text: '产品成交额',
// },
tooltip
:
{
trigger
:
'
axis
'
,
padding
:
3
,
backgroundColor
:
'
rgba(0, 0, 0, .6)
'
,
borderColor
:
'
#777
'
,
borderWidth
:
1
,
},
legend
:
{
icon
:
'
rect
'
,
itemWidth
:
15
,
itemHeight
:
4
,
left
:
80
,
top
:
0
,
orient
:
'
horizontal
'
,
data
:
[
'
产品一
'
,
'
产品二
'
],
},
grid
:
{
left
:
'
3%
'
,
right
:
'
4%
'
,
bottom
:
'
3%
'
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'
category
'
,
boundaryGap
:
false
,
axisTick
:
{
inside
:
true
,
// 刻度朝内
},
data
:
[
'
一月
'
,
'
二月
'
,
'
三月
'
,
'
四月
'
,
'
五月
'
,
'
六月
'
,
'
七月
'
,
'
八月
'
,
'
九月
'
,
'
十月
'
,
'
十一月
'
,
'
十二月
'
,
],
},
yAxis
:
{
type
:
'
value
'
,
axisTick
:
{
inside
:
true
,
// 刻度朝内
},
},
series
:
[
{
name
:
'
产品一
'
,
type
:
'
line
'
,
itemStyle
:
{
normal
:
{
color
:
'
#5B8FF9
'
,
},
},
// areaStyle: {},
data
:
[
330
,
132
,
101
,
134
,
90
,
230
,
210
,
150
,
232
,
234
,
230
,
400
],
animationDuration
:
4000
,
},
{
name
:
'
产品二
'
,
type
:
'
line
'
,
itemStyle
:
{
normal
:
{
color
:
'
#55D187
'
,
},
},
data
:
[
220
,
182
,
191
,
234
,
290
,
330
,
310
,
330
,
232
,
201
,
330
,
190
],
animationDuration
:
4000
,
},
],
});
});
return
{
chartRef
};
},
});
</
script
>
src/views/dashboard/analysis/components/AnalysisPie.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
ref=
"chartRef"
:style=
"
{ height, width }" />
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
onMounted
,
ref
,
Ref
}
from
'
vue
'
;
import
{
useECharts
}
from
'
/@/hooks/web/useECharts
'
;
import
{
basicProps
}
from
'
./props
'
;
const
m2R2Data
=
[
{
value
:
335
,
name
:
'
移动设备
'
,
itemStyle
:
{
color
:
'
#1b65b9
'
}
},
{
value
:
310
,
name
:
'
网页端
'
,
itemStyle
:
{
color
:
'
#3ca0f6
'
}
},
{
value
:
234
,
name
:
'
手表
'
,
itemStyle
:
{
color
:
'
#2dc0c0
'
}
},
{
value
:
234
,
name
:
'
其他
'
,
itemStyle
:
{
color
:
'
#7dd9b9
'
}
},
];
export
default
defineComponent
({
name
:
'
AnalysisLine
'
,
props
:
basicProps
,
setup
()
{
const
chartRef
=
ref
<
HTMLDivElement
|
null
>
(
null
);
const
{
setOptions
}
=
useECharts
(
chartRef
as
Ref
<
HTMLDivElement
>
);
onMounted
(()
=>
{
setOptions
({
title
:
[
{
text
:
'
总设备
'
,
subtext
:
'
1,430
'
,
textStyle
:
{
fontSize
:
12
,
color
:
'
#4B535E85
'
,
},
subtextStyle
:
{
fontSize
:
24
,
color
:
'
black
'
,
},
textAlign
:
'
center
'
,
// @ts-ignore
x
:
'
34.5%
'
,
y
:
'
40%
'
,
},
],
tooltip
:
{
trigger
:
'
item
'
,
backgroundColor
:
'
rgba(0, 0, 0, .6)
'
,
},
legend
:
{
icon
:
'
circle
'
,
itemHeight
:
10
,
type
:
'
scroll
'
,
orient
:
'
vertical
'
,
left
:
'
70%
'
,
align
:
'
left
'
,
top
:
'
middle
'
,
textStyle
:
{
color
:
'
#8C8C8C
'
,
},
height
:
250
,
},
series
:
[
{
name
:
'
成交额
'
,
type
:
'
pie
'
,
center
:
[
'
35%
'
,
'
50%
'
],
radius
:
[
'
45%
'
,
'
65%
'
],
label
:
{
show
:
false
,
},
data
:
m2R2Data
,
animationDuration
:
3000
,
},
],
});
});
return
{
chartRef
};
},
});
</
script
>
src/views/dashboard/analysis/components/FlowAnalysis.tsx
0 → 100644
View file @
52ee35c4
import
{
defineComponent
}
from
'
vue
'
;
import
{
Tabs
,
Row
,
Col
,
Progress
,
Divider
}
from
'
ant-design-vue
'
;
import
{
CollapseContainer
}
from
'
/@/components/Container/index
'
;
import
TrendLine
from
'
./TrendLine.vue
'
;
import
'
./flow-ana.less
'
;
const
prefixCls
=
'
flow-analysis
'
;
export
default
defineComponent
({
name
:
'
AnalysisFLow
'
,
setup
()
{
const
renderContent
=
()
=>
{
return
(
<
Row
>
{
()
=>
(
<>
<
Col
md=
{
24
}
lg=
{
8
}
>
{
()
=>
(
<
CollapseContainer
title=
"整体流量评分"
canExpan=
{
false
}
class=
{
`${prefixCls}__left`
}
>
{
()
=>
(
<>
<
div
class=
{
`${prefixCls}__score`
}
>
86.2
<
span
>
分
</
span
>
</
div
>
<
div
class=
{
`${prefixCls}__rank`
}
>
排名
<
span
>
前20%
</
span
>
</
div
>
<
Progress
percent=
{
70
}
showInfo=
{
false
}
status=
"active"
/>
<
Divider
/>
<
ul
class=
{
`${prefixCls}__rs`
}
>
<
li
>
<
span
>
平均分
</
span
>
<
span
>
77.5
</
span
>
</
li
>
<
li
>
<
span
>
最高分
</
span
>
<
span
>
99.5
</
span
>
</
li
>
<
li
>
<
span
>
最低分
</
span
>
<
span
>
56.5
</
span
>
</
li
>
</
ul
>
</>
)
}
</
CollapseContainer
>
)
}
</
Col
>
<
Col
md=
{
24
}
lg=
{
16
}
>
{
()
=>
(
<
CollapseContainer
title=
"整体流量趋势"
canExpan=
{
false
}
>
{
()
=>
<
TrendLine
/>
}
</
CollapseContainer
>
)
}
</
Col
>
</>
)
}
</
Row
>
);
};
return
()
=>
(
<
Tabs
class=
{
prefixCls
}
default
-
active
-
key=
"1"
>
{
()
=>
(
<>
<
Tabs
.
TabPane
key=
"1"
tab=
"产品一"
>
{
()
=>
renderContent
()
}
</
Tabs
.
TabPane
>
<
Tabs
.
TabPane
key=
"2"
tab=
"产品二"
>
{
()
=>
renderContent
()
}
</
Tabs
.
TabPane
>
<
Tabs
.
TabPane
key=
"3"
tab=
"产品三"
>
{
()
=>
renderContent
()
}
</
Tabs
.
TabPane
>
</>
)
}
</
Tabs
>
);
},
});
src/views/dashboard/analysis/components/GrowCard.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
class=
"grow-card"
>
<div
class=
"grow-card-header"
>
<div
class=
"grow-card__info"
>
<p
class=
"grow-card__title"
>
{{
info
.
title
}}
</p>
<CountTo
prefix=
"$"
:startVal=
"1"
:endVal=
"info.price"
/>
</div>
<img
:src=
"info.icon"
/>
</div>
<div
class=
"grow-card-footer"
:class=
"
{ 'is-up': info.up }">
<Statistic
:value=
"info.percent"
>
<template
#prefix
>
<img
:src=
"info.up ? riseSvg : downSvg"
/>
</
template
>
</Statistic>
<span
class=
"grow-card__mom"
>
{{ info.mom }}
</span>
</div>
</div>
</template>
<
script
lang=
"ts"
>
import
{
defineComponent
,
PropType
}
from
'
vue
'
;
import
{
Statistic
}
from
'
ant-design-vue
'
;
import
{
CountTo
}
from
'
/@/components/CountTo/index
'
;
import
riseSvg
from
'
/@/assets/svg/dashboard/analysis-rise.svg
'
;
import
downSvg
from
'
/@/assets/svg/dashboard/analysis-down.svg
'
;
import
{
GrowCardItem
}
from
'
../types
'
;
export
default
defineComponent
({
components
:
{
Statistic
,
CountTo
},
props
:
{
info
:
{
type
:
Object
as
PropType
<
GrowCardItem
>
,
default
:
null
,
},
},
setup
()
{
return
{
riseSvg
,
downSvg
,
};
},
});
</
script
>
<
style
lang=
"less"
>
@import (reference) '../../../../design/index.less';
.grow-card {
display: flex;
width: calc(100% - 12px);
height: 158px;
padding: 16px 16px 12px 16px;
// margin: 0 12px 12px 12px;
cursor: pointer;
background: @white;
border-radius: 4px;
box-shadow: 6px 6px 54px 0 rgba(0, 0, 0, 0.05);
flex-direction: column;
&:hover {
box-shadow: 6px 6px 54px 0 rgba(0, 0, 0, 0.1);
}
&-header {
display: flex;
width: 100%;
justify-content: space-between;
}
&__title {
font-family: PingFangSC-Regular;
font-size: 16px;
letter-spacing: 0;
color: #2c3a61;
opacity: 0.7;
}
&__info {
span {
font-family: NeoSans;
font-size: 26px;
line-height: 38px;
}
}
&-footer {
display: flex;
width: 100%;
margin-top: 24px;
align-items: center;
.ant-statistic-content-value {
color: @error-color;
}
.ant-statistic-content-prefix svg {
width: 0.98rem !important;
height: 0.98rem !important;
}
&.is-up {
.ant-statistic-content-value {
color: @success-color;
}
}
}
&__mom {
display: inline-block;
padding-left: 10px;
font-family: PingFangSC-Regular;
font-size: 12px;
line-height: 22px;
letter-spacing: 0;
color: #606060;
}
}
</
style
>
src/views/dashboard/analysis/components/TaskCard.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
:class=
"prefixCls"
>
<div
:class=
"`$
{prefixCls}-header`">
<div
:class=
"`$
{prefixCls}__info`">
<span
:class=
"`$
{prefixCls}__title`">
{{
info
.
title
}}
</span>
<span
:class=
"`$
{prefixCls}__desc`">
{{
info
.
desc
}}
</span>
</div>
<span
:class=
"`$
{prefixCls}__tag ${info.status}`">
{{
info
.
text
}}
</span>
</div>
<div
:class=
"`$
{prefixCls}-body mt-5`">
<div
:class=
"`$
{prefixCls}__process-nfo`">
<span>
进度
</span>
<span>
{{
info
.
percent
}}
%
</span>
</div>
<Progress
:percent=
"info.percent"
:showInfo=
"false"
:status=
"info.status"
/>
</div>
<div
:class=
"`$
{prefixCls}-footer`">
<span
:class=
"`$
{prefixCls}__date`">
更新日期:
<span>
{{
info
.
updateTime
}}
</span>
</span>
<div
:class=
"`$
{prefixCls}__avatar`">
<Avatar
src=
"https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
<Avatar
src=
"https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png"
/>
<Avatar>
+3
</Avatar>
</div>
</div>
</div>
</
template
>
<
script
lang=
"ts"
>
import
{
computed
,
defineComponent
,
PropType
}
from
'
vue
'
;
import
{
Progress
,
Avatar
}
from
'
ant-design-vue
'
;
import
{
TaskItem
}
from
'
../types
'
;
export
default
defineComponent
({
name
:
'
GrowCard
'
,
components
:
{
Progress
,
Avatar
},
props
:
{
info
:
{
type
:
Object
as
PropType
<
TaskItem
>
,
default
:
null
,
},
},
setup
(
props
)
{
return
{
prefixCls
:
'
task-card
'
,
text
:
computed
(()
=>
{
const
{
status
}
=
props
.
info
||
{};
return
status
===
'
active
'
?
'
进度正常
'
:
status
===
'
exception
'
?
'
进度滞后
'
:
'
项目完成
'
;
}),
};
},
});
</
script
>
<
style
lang=
"less"
scoped
>
.task-card {
display: flex;
width: calc(100% - 24px);
height: 199px;
padding: 24px 20px 12px 16px;
margin: 0 12px 12px 12px;
background: #fff;
border: 1px solid #ececf2;
border-radius: 12px;
flex-direction: column;
&-header {
display: flex;
width: 100%;
justify-content: space-between;
align-items: center;
}
&__tag {
display: inline-block;
padding: 4px 6px;
font-family: PingFangSC-Regular;
font-size: 12px;
border-radius: 6px;
&.success {
color: #55d187;
background: rgba(85, 209, 135, 0.16);
}
&.warn {
color: #ffa07d;
background: #ffd16416;
}
&.done {
color: #0593ff;
background: #0593ff16;
}
}
&__info {
display: flex;
flex-direction: column;
}
&__title {
font-family: PingFangSC-Medium;
font-size: 16px;
line-height: 24px;
color: #2c3a61;
}
&__desc {
font-family: PingFangSC-Regular;
font-size: 12px;
line-height: 21px;
color: #8181a5;
}
&__process-nfo {
display: flex;
justify-content: space-between;
span {
font-size: 14px;
line-height: 21px;
color: #8181a5;
}
}
&-footer {
display: flex;
width: 100%;
margin-top: 16px;
align-items: center;
justify-content: space-between;
}
&__date {
font-size: 12px;
line-height: 21px;
color: #2c3a61;
span {
color: #7c8087;
}
}
&__avatar {
display: flex;
}
}
</
style
>
src/views/dashboard/analysis/components/TrendLine.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
ref=
"chartRef"
:style=
"
{ height, width }" />
</
template
>
<
script
lang=
"ts"
>
import
{
defineComponent
,
onMounted
,
ref
,
Ref
}
from
'
vue
'
;
import
{
useECharts
}
from
'
/@/hooks/web/useECharts
'
;
import
{
basicProps
}
from
'
./props
'
;
export
default
defineComponent
({
name
:
'
AnalysisLine
'
,
props
:
basicProps
,
setup
()
{
const
chartRef
=
ref
<
HTMLDivElement
|
null
>
(
null
);
const
{
setOptions
,
echarts
}
=
useECharts
(
chartRef
as
Ref
<
HTMLDivElement
>
);
onMounted
(()
=>
{
setOptions
({
// title: {
// text: '产品成交额',
// },
tooltip
:
{
trigger
:
'
axis
'
,
padding
:
3
,
backgroundColor
:
'
rgba(0, 0, 0, .6)
'
,
borderColor
:
'
#777
'
,
borderWidth
:
1
,
},
legend
:
{
show
:
false
,
},
grid
:
{
left
:
'
3%
'
,
right
:
'
4%
'
,
bottom
:
'
3%
'
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'
category
'
,
boundaryGap
:
false
,
axisTick
:
{
inside
:
true
,
// 刻度朝内
},
data
:
[
'
一月
'
,
'
二月
'
,
'
三月
'
,
'
四月
'
,
'
五月
'
,
'
六月
'
,
'
七月
'
,
'
八月
'
,
'
九月
'
,
'
十月
'
,
'
十一月
'
,
'
十二月
'
,
],
},
yAxis
:
{
type
:
'
value
'
,
axisTick
:
{
inside
:
true
,
// 刻度朝内
},
},
series
:
[
{
name
:
'
产品一
'
,
type
:
'
line
'
,
itemStyle
:
{
color
:
'
#5B8FF9
'
,
},
areaStyle
:
{
// 线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
// @ts-ignore
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'
#5B8FF9
'
,
},
{
offset
:
1
,
color
:
'
rgba(118,168,248, 0)
'
,
},
],
false
),
shadowColor
:
'
rgba(118,168,248, 0.9)
'
,
// 阴影颜色
shadowBlur
:
20
,
// shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
},
// areaStyle: {},
data
:
[
134
,
330
,
132
,
101
,
90
,
230
,
210
,
150
,
230
,
400
,
232
,
234
],
animationDuration
:
3000
,
},
],
});
});
return
{
chartRef
};
},
});
</
script
>
src/views/dashboard/analysis/components/flow-ana.less
0 → 100644
View file @
52ee35c4
.flow-analysis {
width: 100%;
background: #fff;
&__left {
padding: 10px 20px !important;
border-right: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 0;
}
&__score {
margin-top: 20px;
font-size: 30px;
line-height: 38px;
color: rgba(0, 0, 0, 0.85);
span {
font-size: 20px;
line-height: 28px;
color: rgba(0, 0, 0, 0.85);
}
}
&__rank {
margin: 16px 0;
font-size: 12px;
line-height: 20px;
color: #7c8087;
span {
display: inline-block;
margin-left: 10px;
color: #1c1d21;
}
}
&__rs {
li {
display: flex;
line-height: 28px;
justify-content: space-between;
span {
&:nth-child(1) {
font-size: 14px;
color: #1c1d21;
}
&:nth-child(2) {
font-size: 16px;
color: #1c1d21;
}
}
}
}
}
src/views/dashboard/analysis/components/props.ts
0 → 100644
View file @
52ee35c4
import
{
PropType
}
from
'
vue
'
;
export
interface
BasicProps
{
width
:
string
;
height
:
string
;
}
export
const
basicProps
=
{
width
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
100%
'
,
},
height
:
{
type
:
String
as
PropType
<
string
>
,
default
:
'
280px
'
,
},
};
src/views/dashboard/analysis/data.tsx
0 → 100644
View file @
52ee35c4
import
{
GrowCardItem
,
TaskItem
}
from
'
./types
'
;
import
iconSvg1
from
'
/@/assets/svg/dashboard/analysis-icon1.svg
'
;
import
iconSvg2
from
'
/@/assets/svg/dashboard/analysis-icon2.svg
'
;
import
iconSvg3
from
'
/@/assets/svg/dashboard/analysis-icon3.svg
'
;
import
iconSvg4
from
'
/@/assets/svg/dashboard/analysis-icon4.svg
'
;
export
const
taskList
:
TaskItem
[]
=
[
{
percent
:
50
,
title
:
'
开发任务一
'
,
updateTime
:
'
2020.7.12
'
,
desc
:
'
开发任务一简介
'
,
status
:
'
active
'
,
},
{
percent
:
67
,
title
:
'
开发任务二
'
,
updateTime
:
'
2020.3.12
'
,
desc
:
'
开发任务二简介
'
,
status
:
'
exception
'
,
},
{
percent
:
100
,
title
:
'
开发任务三
'
,
updateTime
:
'
2020.4.12
'
,
desc
:
'
开发任务三简介
'
,
status
:
'
success
'
,
},
];
export
const
growCardList
:
GrowCardItem
[]
=
[
{
title
:
'
总用户数
'
,
icon
:
iconSvg1
,
price
:
80000
,
up
:
true
,
mom
:
'
环比增长
'
,
percent
:
2.5
,
},
{
title
:
'
产品数量
'
,
icon
:
iconSvg2
,
price
:
4000
,
up
:
true
,
mom
:
'
同比增长
'
,
percent
:
3
,
},
{
title
:
'
总营业额
'
,
icon
:
iconSvg3
,
price
:
3000000
,
up
:
false
,
mom
:
'
环比降低
'
,
percent
:
2
,
},
{
title
:
'
总任务数
'
,
icon
:
iconSvg4
,
price
:
10000
,
up
:
false
,
mom
:
'
同比降低
'
,
percent
:
1
,
},
];
export
const
randomizeArray
=
function
(
arg
:
any
)
{
const
array
=
arg
.
slice
();
let
currentIndex
=
array
.
length
,
temporaryValue
,
randomIndex
;
while
(
0
!==
currentIndex
)
{
randomIndex
=
Math
.
floor
(
Math
.
random
()
*
currentIndex
);
currentIndex
-=
1
;
temporaryValue
=
array
[
currentIndex
];
array
[
currentIndex
]
=
array
[
randomIndex
];
array
[
randomIndex
]
=
temporaryValue
;
}
return
array
;
};
export
const
sparklineData
=
[
47
,
45
,
54
,
38
,
56
,
24
,
65
,
31
,
37
,
39
,
62
,
51
,
35
,
41
,
35
,
27
,
93
,
53
,
61
,
27
,
54
,
43
,
19
,
46
,
];
src/views/dashboard/analysis/index.vue
0 → 100644
View file @
52ee35c4
<
template
>
<div
class=
"analysis p-4"
>
<Row
class=
"pl-2"
>
<template
v-for=
"item in growCardList"
:key=
"item.title"
>
<ACol
:sm=
"24"
:md=
"12"
:lg=
"6"
>
<GrowCard
:info=
"item"
/>
</ACol>
</
template
>
</Row>
<Row>
<ACol
:md=
"24"
:lg=
"17"
class=
"my-3"
>
<CollapseContainer
class=
"mr-3"
title=
"产品成交额"
:canExpan=
"false"
>
<AnalysisLine
/>
</CollapseContainer>
<Row
class=
"mt-3"
>
<ACol
:md=
"24"
:lg=
"12"
class=
"product-total"
>
<CollapseContainer
class=
"mr-3"
title=
"产品成交额"
:canExpan=
"false"
>
<AnalysisPie
/>
</CollapseContainer>
</ACol>
<ACol
:md=
"24"
:lg=
"12"
>
<CollapseContainer
class=
"mr-3"
title=
"用户来源"
:canExpan=
"false"
>
<AnalysisBar
/>
</CollapseContainer>
</ACol>
</Row>
</ACol>
<ACol
:md=
"24"
:lg=
"7"
>
<CollapseContainer
class=
"mt-3"
title=
"项目进度"
:canExpan=
"false"
>
<
template
v-for=
"item in taskList"
:key=
"item.title"
>
<TaskCard
:info=
"item"
/>
</
template
>
</CollapseContainer>
</ACol>
</Row>
<Row>
<FlowAnalysis
/>
</Row>
</div>
</template>
<
script
lang=
"ts"
>
import
{
defineComponent
}
from
'
vue
'
;
import
GrowCard
from
'
./components/GrowCard.vue
'
;
import
TrendLine
from
'
./components/TrendLine.vue
'
;
import
AnalysisLine
from
'
./components/AnalysisLine.vue
'
;
import
AnalysisPie
from
'
./components/AnalysisPie.vue
'
;
import
AnalysisBar
from
'
./components/AnalysisBar.vue
'
;
import
TaskCard
from
'
./components/TaskCard.vue
'
;
import
FlowAnalysis
from
'
./components/FlowAnalysis
'
;
import
{
Row
,
Col
}
from
'
ant-design-vue
'
;
import
{
CollapseContainer
}
from
'
/@/components/Container/index
'
;
import
{
growCardList
,
taskList
}
from
'
./data
'
;
export
default
defineComponent
({
components
:
{
Row
,
ACol
:
Col
,
GrowCard
,
CollapseContainer
,
TrendLine
,
AnalysisLine
,
AnalysisPie
,
AnalysisBar
,
TaskCard
,
FlowAnalysis
,
},
setup
()
{
return
{
growCardList
,
taskList
};
},
});
</
script
>
<
style
lang=
"less"
scoped
>
@import (reference) '../../../design/index.less';
.analysis {
width: 100%;
.product-total {
.respond-to(small-and-medium, {padding-right: 0;margin-bottom: 24px;});
}
}
</
style
>
src/views/dashboard/analysis/types.ts
0 → 100644
View file @
52ee35c4
export
interface
GrowCardItem
{
icon
:
string
;
title
:
string
;
price
:
number
;
up
:
boolean
;
mom
:
string
;
percent
:
number
;
}
export
interface
TaskItem
{
percent
:
number
;
status
:
'
success
'
|
'
exception
'
|
'
active
'
;
updateTime
:
string
;
title
:
string
;
desc
:
string
;
}
src/views/dashboard/workbench/components/Week.vue
View file @
52ee35c4
<
template
>
<CollapseContainer
title=
"
任务安排
"
:canExpan=
"false"
>
<CollapseContainer
title=
"
销售统计
"
:canExpan=
"false"
>
<div
ref=
"chartRef"
:style=
"
{ width: '100%' }" />
</CollapseContainer>
</
template
>
...
...
@@ -9,7 +9,6 @@
import
{
CollapseContainer
}
from
'
/@/components/Container/index
'
;
import
{
useApexCharts
}
from
'
/@/hooks/web/useApexCharts
'
;
import
moment
from
'
moment
'
;
export
default
defineComponent
({
components
:
{
CollapseContainer
},
setup
()
{
...
...
@@ -18,72 +17,72 @@
onMounted
(()
=>
{
setOptions
({
series
:
[
{
data
:
[
{
x
:
'
Analysis
'
,
y
:
[
new
Date
(
'
2019-02-27
'
).
getTime
(),
new
Date
(
'
2019-03-04
'
).
getTime
()],
fillColor
:
'
#008FFB
'
,
},
{
x
:
'
Design
'
,
y
:
[
new
Date
(
'
2019-03-04
'
).
getTime
(),
new
Date
(
'
2019-03-08
'
).
getTime
()],
fillColor
:
'
#00E396
'
,
},
{
x
:
'
Coding
'
,
y
:
[
new
Date
(
'
2019-03-07
'
).
getTime
(),
new
Date
(
'
2019-03-10
'
).
getTime
()],
fillColor
:
'
#775DD0
'
,
},
{
x
:
'
Testing
'
,
y
:
[
new
Date
(
'
2019-03-08
'
).
getTime
(),
new
Date
(
'
2019-03-12
'
).
getTime
()],
fillColor
:
'
#FEB019
'
,
},
{
x
:
'
Deployment
'
,
y
:
[
new
Date
(
'
2019-03-12
'
).
getTime
(),
new
Date
(
'
2019-03-17
'
).
getTime
()],
fillColor
:
'
#FF4560
'
,
},
],
},
{
name
:
'
Visits
'
,
data
:
[
90
,
50
,
86
,
40
,
100
,
20
]
},
{
name
:
'
Sales
'
,
data
:
[
70
,
75
,
70
,
76
,
20
,
85
]
},
],
chart
:
{
height
:
350
,
type
:
'
rangeBar
'
,
},
plotOptions
:
{
bar
:
{
horizontal
:
true
,
distributed
:
true
,
dataLabels
:
{
hideOverflowingLabels
:
false
,
},
},
},
dataLabels
:
{
enabled
:
true
,
formatter
:
function
(
val
:
any
,
opts
:
any
)
{
var
label
=
opts
.
w
.
globals
.
labels
[
opts
.
dataPointIndex
];
var
a
=
moment
(
val
[
0
]);
var
b
=
moment
(
val
[
1
]);
var
diff
=
b
.
diff
(
a
,
'
days
'
);
return
label
+
'
:
'
+
diff
+
(
diff
>
1
?
'
days
'
:
'
day
'
);
},
style
:
{
colors
:
[
'
#
f3f4f5
'
,
'
#fff
'
],
colors
:
[
'
#
b9c3cd
'
,
'
#b9c3cd
'
,
'
#b9c3cd
'
,
'
#b9c3cd
'
,
'
#b9c3cd
'
,
'
#b9c3cd
'
],
},
},
xaxis
:
{
type
:
'
datetime
'
,
chart
:
{
height
:
350
,
type
:
'
radar
'
,
dropShadow
:
{
enabled
:
true
,
blur
:
1
,
left
:
1
,
top
:
1
,
},
},
yaxis
:
{
show
:
false
,
},
grid
:
{
row
:
{
colors
:
[
'
#f3f4f5
'
,
'
#fff
'
],
opacity
:
1
,
show
:
false
,
},
legend
:
{
show
:
false
},
title
:
{
show
:
false
,
},
tooltip
:
{
x
:
{
show
:
false
},
},
markers
:
{
size
:
0
,
},
xaxis
:
{
categories
:
[
'
2011
'
,
'
2012
'
,
'
2013
'
,
'
2014
'
,
'
2015
'
,
'
2016
'
],
},
stroke
:
{
width
:
0
,
},
colors
:
[
'
#9f8ed7
'
,
'
#1edec5
'
],
plotOptions
:
{
radar
:
{
polygons
:
{
strokeColors
:
[
'
#e8e8e8
'
,
'
transparent
'
,
'
transparent
'
,
'
transparent
'
,
'
transparent
'
,
'
transparent
'
,
],
connectorColors
:
'
transparent
'
,
},
},
},
fill
:
{
type
:
'
gradient
'
,
gradient
:
{
shade
:
'
dark
'
,
gradientToColors
:
[
'
#8e9ad6
'
,
'
#1fcadb
'
],
shadeIntensity
:
1
,
type
:
'
horizontal
'
,
opacityFrom
:
1
,
opacityTo
:
1
,
stops
:
[
0
,
100
,
100
,
100
],
},
},
});
...
...
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