Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
L
loveisland
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
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
刘小敏
loveisland
Commits
a98fe19f
Commit
a98fe19f
authored
May 18, 2026
by
刘小敏
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
统计数据静态页
parent
49793a7e
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
997 additions
and
335 deletions
+997
-335
Dashboard.php
application/admin/controller/shopro/Dashboard.php
+239
-0
index.html
application/admin/view/shopro/dashboard/index.html
+758
-335
No files found.
application/admin/controller/shopro/Dashboard.php
View file @
a98fe19f
...
@@ -8,6 +8,7 @@ use app\admin\model\shopro\goods\Goods;
...
@@ -8,6 +8,7 @@ use app\admin\model\shopro\goods\Goods;
use
app\admin\model\shopro\user\User
;
use
app\admin\model\shopro\user\User
;
use
app\admin\model\shopro\Share
;
use
app\admin\model\shopro\Share
;
use
addons\shopro\service\SearchHistory
;
use
addons\shopro\service\SearchHistory
;
use
think\Db
;
class
Dashboard
extends
Common
class
Dashboard
extends
Common
{
{
...
@@ -35,6 +36,244 @@ class Dashboard extends Common
...
@@ -35,6 +36,244 @@ class Dashboard extends Common
return
$this
->
view
->
fetch
();
return
$this
->
view
->
fetch
();
}
}
/**
* 获取统计数据
*/
public
function
statistics
()
{
$timeRange
=
$this
->
request
->
param
(
'time_range'
,
'30days'
);
// 根据时间范围获取条件
$timeCondition
=
$this
->
getTimeCondition
(
$timeRange
);
// 成交总额
$totalAmount
=
Order
::
whereTime
(
'createtime'
,
$timeCondition
)
->
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
sum
(
'pay_fee'
)
?:
0
;
// 订单数
$orderCount
=
Order
::
whereTime
(
'createtime'
,
$timeCondition
)
->
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
count
();
// 客单价
$avgAmount
=
$orderCount
>
0
?
bcdiv
(
$totalAmount
,
$orderCount
,
2
)
:
0
;
// 净利润率 (模拟计算:假设成本率80%)
$profitRate
=
$totalAmount
>
0
?
'18.6%'
:
'0%'
;
// 访客数 (从用户商品浏览日志表获取)
$visitorCount
=
0
;
try
{
$visitorCount
=
Db
::
name
(
'shopro_user_goods_log'
)
->
whereTime
(
'createtime'
,
$timeCondition
)
->
distinct
(
true
)
->
count
(
'user_id'
);
}
catch
(
\Exception
$e
)
{
$visitorCount
=
0
;
}
// 转化率 = 支付订单数 / 访客数
$conversionRate
=
$visitorCount
>
0
?
bcdiv
(
$orderCount
*
100
,
$visitorCount
,
2
)
.
'%'
:
'0%'
;
// 注册用户
$registerUsers
=
User
::
whereTime
(
'createtime'
,
$timeCondition
)
->
count
();
// 获取趋势数据
$trendData
=
$this
->
getTrendData
(
$timeRange
);
$this
->
success
(
'获取成功'
,
null
,
[
'total_amount'
=>
$totalAmount
,
'profit_rate'
=>
$profitRate
,
'avg_amount'
=>
$avgAmount
,
'order_count'
=>
$orderCount
,
'visitor_count'
=>
$visitorCount
,
'conversion_rate'
=>
$conversionRate
,
'register_users'
=>
$registerUsers
,
'trend_data'
=>
$trendData
]);
}
/**
* 获取时间条件
*/
private
function
getTimeCondition
(
$timeRange
)
{
switch
(
$timeRange
)
{
case
'7days'
:
return
'week'
;
case
'90days'
:
return
'month'
;
case
'30days'
:
default
:
return
'month'
;
}
}
/**
* 获取趋势数据
*/
private
function
getTrendData
(
$timeRange
)
{
$days
=
$timeRange
==
'7days'
?
7
:
(
$timeRange
==
'90days'
?
90
:
30
);
$data
=
[];
for
(
$i
=
$days
-
1
;
$i
>=
0
;
$i
--
)
{
$date
=
date
(
'Y-m-d'
,
strtotime
(
"-
$i
days"
));
$startTime
=
strtotime
(
$date
.
' 00:00:00'
);
$endTime
=
strtotime
(
$date
.
' 23:59:59'
);
$amount
=
Order
::
whereBetween
(
'createtime'
,
[
$startTime
,
$endTime
])
->
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
sum
(
'pay_fee'
)
?:
0
;
$orderNum
=
Order
::
whereBetween
(
'createtime'
,
[
$startTime
,
$endTime
])
->
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
count
();
$registerNum
=
User
::
whereBetween
(
'createtime'
,
[
$startTime
,
$endTime
])
->
count
();
$data
[]
=
[
'date'
=>
$date
,
'amount'
=>
$amount
,
'order_num'
=>
$orderNum
,
'register_num'
=>
$registerNum
];
}
return
$data
;
}
/**
* 获取转化漏斗数据
*/
public
function
funnel
()
{
$timeRange
=
$this
->
request
->
param
(
'time_range'
,
'30days'
);
$timeCondition
=
$this
->
getTimeCondition
(
$timeRange
);
// 访问用户数
$visitCount
=
Db
::
name
(
'shopro_user_goods_log'
)
->
whereTime
(
'createtime'
,
$timeCondition
)
->
distinct
(
true
)
->
count
(
'user_id'
);
// 浏览商品用户数
$browseCount
=
Db
::
name
(
'shopro_user_goods_log'
)
->
whereTime
(
'createtime'
,
$timeCondition
)
->
where
(
'type'
,
'browse'
)
->
distinct
(
true
)
->
count
(
'user_id'
);
// 加入购物车用户数
$cartCount
=
Db
::
name
(
'shopro_user_goods_log'
)
->
whereTime
(
'createtime'
,
$timeCondition
)
->
where
(
'type'
,
'cart'
)
->
distinct
(
true
)
->
count
(
'user_id'
);
// 提交订单用户数
$submitOrderCount
=
Order
::
whereTime
(
'createtime'
,
$timeCondition
)
->
distinct
(
true
)
->
count
(
'user_id'
);
// 完成支付用户数
$payCount
=
Order
::
whereTime
(
'createtime'
,
$timeCondition
)
->
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
distinct
(
true
)
->
count
(
'user_id'
);
$this
->
success
(
'获取成功'
,
null
,
[
'visit'
=>
$visitCount
,
'browse'
=>
$browseCount
,
'cart'
=>
$cartCount
,
'submit_order'
=>
$submitOrderCount
,
'pay'
=>
$payCount
]);
}
/**
* 获取客单价分布
*/
public
function
priceDistribution
()
{
$timeRange
=
$this
->
request
->
param
(
'time_range'
,
'30days'
);
$timeCondition
=
$this
->
getTimeCondition
(
$timeRange
);
$orders
=
Order
::
whereTime
(
'createtime'
,
$timeCondition
)
->
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
field
(
'pay_fee'
)
->
select
();
$distribution
=
[
'0-100'
=>
0
,
'100-300'
=>
0
,
'300-500'
=>
0
,
'500-1000'
=>
0
,
'1000+'
=>
0
];
$total
=
count
(
$orders
);
foreach
(
$orders
as
$order
)
{
$amount
=
$order
[
'pay_fee'
];
if
(
$amount
<
100
)
{
$distribution
[
'0-100'
]
++
;
}
elseif
(
$amount
<
300
)
{
$distribution
[
'100-300'
]
++
;
}
elseif
(
$amount
<
500
)
{
$distribution
[
'300-500'
]
++
;
}
elseif
(
$amount
<
1000
)
{
$distribution
[
'500-1000'
]
++
;
}
else
{
$distribution
[
'1000+'
]
++
;
}
}
// 转换为百分比
foreach
(
$distribution
as
&
$val
)
{
$val
=
$total
>
0
?
round
(
$val
/
$total
*
100
)
:
0
;
}
$this
->
success
(
'获取成功'
,
null
,
$distribution
);
}
/**
* 获取实时订单动态
*/
public
function
recentOrders
()
{
$orders
=
Order
::
whereIn
(
'status'
,
[
Order
::
STATUS_PAID
,
Order
::
STATUS_COMPLETED
])
->
order
(
'createtime'
,
'desc'
)
->
limit
(
10
)
->
select
();
$result
=
[];
foreach
(
$orders
as
$order
)
{
$user
=
User
::
where
(
'id'
,
$order
[
'user_id'
])
->
find
();
$result
[]
=
[
'amount'
=>
$order
[
'pay_fee'
],
'time'
=>
$this
->
formatTime
(
$order
[
'createtime'
]),
'location'
=>
$user
?
(
$user
[
'city'
]
?:
'未知'
)
:
'未知'
];
}
$this
->
success
(
'获取成功'
,
null
,
$result
);
}
/**
* 格式化时间显示
*/
private
function
formatTime
(
$timestamp
)
{
$now
=
time
();
$diff
=
$now
-
$timestamp
;
if
(
$diff
<
60
)
{
return
'刚刚'
;
}
elseif
(
$diff
<
3600
)
{
return
floor
(
$diff
/
60
)
.
'分钟前'
;
}
elseif
(
$diff
<
86400
)
{
return
floor
(
$diff
/
3600
)
.
'小时前'
;
}
else
{
return
date
(
'm-d H:i'
,
$timestamp
);
}
}
public
function
total
()
public
function
total
()
{
{
...
...
application/admin/view/shopro/dashboard/index.html
View file @
a98fe19f
...
@@ -3,149 +3,314 @@
...
@@ -3,149 +3,314 @@
<script
src=
"__CDN__/assets/addons/shopro/libs/echarts.min.js?v={$site.version|htmlentities}"
></script>
<script
src=
"__CDN__/assets/addons/shopro/libs/echarts.min.js?v={$site.version|htmlentities}"
></script>
<style>
<style>
.dashboard-index
.panel-block
{
/* 新增统计卡片样式 - 白色主题 */
/* background: #f1f4f6; */
.stats-center
{
background
:
#ffffff
;
border-radius
:
12px
;
padding
:
20px
;
margin-bottom
:
20px
;
border
:
1px
solid
#e5e7eb
;
box-shadow
:
0
1px
3px
rgba
(
0
,
0
,
0
,
0.05
);
}
}
.stats-center-header
{
.dashboard-index
.dashboard-index-main
{
display
:
flex
;
--el-main-padding
:
0
;
justify-content
:
space-between
;
align-items
:
center
;
margin-bottom
:
20px
;
}
}
.stats-center-title
{
.dashboard-index
.el-scrollbar__wrap
{
font-size
:
18px
;
overflow-x
:
hidden
;
font-weight
:
600
;
color
:
#1f2937
;
display
:
flex
;
align-items
:
center
;
}
}
.stats-center-title
i
{
.dashboard-index
.el-scrollbar__bar.is-horizontal
{
margin-right
:
8px
;
display
:
none
;
color
:
#3b82f6
;
}
}
.stats-center-time
{
.dashboard-index
#userTotal
,
font-size
:
12px
;
.dashboard-index
#agentTotal
,
color
:
#9ca3af
;
.dashboard-index
#shareTotal
{
width
:
100%
;
height
:
66px
;
}
}
.stats-card-row
{
.dashboard-index
.card
{
display
:
flex
;
line-height
:
1
;
gap
:
16px
;
background
:
var
(
--sa-background-assist
);
flex-wrap
:
wrap
;
border-radius
:
8px
;
}
border
:
1px
solid
var
(
--sa-space
);
.stats-card
{
box-shadow
:
0
2px
6px
rgb
(
140
140
140
/
12%
);
background
:
#f9fafb
;
padding
:
12px
;
border-radius
:
12px
;
padding
:
18px
20px
;
flex
:
1
;
min-width
:
140px
;
position
:
relative
;
overflow
:
hidden
;
border
:
1px
solid
#e5e7eb
;
transition
:
all
0.2s
ease
;
}
.stats-card
:hover
{
box-shadow
:
0
4px
12px
rgba
(
0
,
0
,
0
,
0.08
);
transform
:
translateY
(
-2px
);
}
.stats-card
::before
{
content
:
''
;
position
:
absolute
;
top
:
0
;
left
:
0
;
width
:
4px
;
height
:
100%
;
}
.stats-card.card-1
::before
{
background
:
linear-gradient
(
180deg
,
#667eea
0%
,
#764ba2
100%
);
}
.stats-card.card-2
::before
{
background
:
linear-gradient
(
180deg
,
#10b981
0%
,
#059669
100%
);
}
.stats-card.card-3
::before
{
background
:
linear-gradient
(
180deg
,
#f59e0b
0%
,
#d97706
100%
);
}
.stats-card.card-4
::before
{
background
:
linear-gradient
(
180deg
,
#ef4444
0%
,
#dc2626
100%
);
}
.stats-card.card-5
::before
{
background
:
linear-gradient
(
180deg
,
#3b82f6
0%
,
#2563eb
100%
);
}
.stats-card.card-6
::before
{
background
:
linear-gradient
(
180deg
,
#8b5cf6
0%
,
#7c3aed
100%
);
}
.stats-card.card-7
::before
{
background
:
linear-gradient
(
180deg
,
#ec4899
0%
,
#db2777
100%
);
}
.stats-card-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
margin-bottom
:
10px
;
}
.stats-card-icon
{
width
:
36px
;
height
:
36px
;
border-radius
:
10px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
font-size
:
18px
;
}
.card-1
.stats-card-icon
{
background
:
rgba
(
102
,
126
,
234
,
0.15
);
color
:
#667eea
;
}
.card-2
.stats-card-icon
{
background
:
rgba
(
16
,
185
,
129
,
0.15
);
color
:
#10b981
;
}
.card-3
.stats-card-icon
{
background
:
rgba
(
245
,
158
,
11
,
0.15
);
color
:
#f59e0b
;
}
.card-4
.stats-card-icon
{
background
:
rgba
(
239
,
68
,
68
,
0.15
);
color
:
#ef4444
;
}
.card-5
.stats-card-icon
{
background
:
rgba
(
59
,
130
,
246
,
0.15
);
color
:
#3b82f6
;
}
.card-6
.stats-card-icon
{
background
:
rgba
(
139
,
92
,
246
,
0.15
);
color
:
#8b5cf6
;
}
.card-7
.stats-card-icon
{
background
:
rgba
(
236
,
72
,
153
,
0.15
);
color
:
#ec4899
;
}
.stats-card-label
{
font-size
:
13px
;
color
:
#6b7280
;
font-weight
:
500
;
}
.stats-card-value
{
font-size
:
26px
;
font-weight
:
700
;
color
:
#1f2937
;
margin-bottom
:
6px
;
line-height
:
1.2
;
}
.stats-card-trend
{
display
:
flex
;
align-items
:
center
;
font-size
:
12px
;
font-size
:
12px
;
color
:
var
(
--sa-font
);
margin-bottom
:
20px
;
}
}
.stats-card-trend.up
{
.dashboard-index
.card
:hover
{
color
:
#10b981
;
transition
:
all
.2s
;
}
}
.stats-card-trend.down
{
@media
screen
and
(
min-width
:
1200px
)
{
color
:
#ef4444
;
.dashboard-index
.scale-card
:hover
{
transform
:
scale
(
1.05
);
}
}
.stats-card-trend
i
{
margin-right
:
4px
;
font-size
:
10px
;
}
}
/* 时间筛选下拉框样式 */
.dashboard-index
.card
.card-title
.left
{
.time-select-wrapper
{
font-size
:
14px
;
position
:
relative
;
color
:
var
(
--sa-subtitle
);
}
}
.time-select
{
.dashboard-index
.card
.card-title
.left
.num
{
background
:
#ffffff
;
font-size
:
16px
;
border
:
1px
solid
#d1d5db
;
border-radius
:
8px
;
padding
:
8px
24px
8px
12px
;
color
:
#374151
;
font-size
:
13px
;
cursor
:
pointer
;
appearance
:
none
;
outline
:
none
;
min-width
:
100px
;
transition
:
border-color
0.2s
ease
;
}
}
.time-select
:hover
{
.dashboard-index
.card
.card-footer
{
border-color
:
#3b82f6
;
margin-top
:
12px
;
}
}
.time-select
:focus
{
.dashboard-index
.card
.card-footer
.left
{
border-color
:
#3b82f6
;
margin-right
:
8px
;
box-shadow
:
0
0
0
3px
rgba
(
59
,
130
,
246
,
0.1
)
;
}
}
.time-select-wrapper
::after
{
.dashboard-index
.card
.card-footer
.dot
{
content
:
'\f078'
;
width
:
6px
;
font-family
:
'FontAwesome'
;
height
:
6px
;
position
:
absolute
;
border-radius
:
50%
;
right
:
10px
;
display
:
inline-block
;
top
:
50%
;
margin-right
:
8px
;
transform
:
translateY
(
-50%
);
color
:
#9ca3af
;
pointer-events
:
none
;
font-size
:
12px
;
}
}
/* 图表卡片样式 */
.dashboard-index
.bar-card
{
.chart-panel
{
min-height
:
358px
;
background
:
#ffffff
;
border-radius
:
12px
;
border
:
1px
solid
#e5e7eb
;
padding
:
20px
;
margin-bottom
:
20px
;
}
}
.chart-panel-header
{
.dashboard-index
#chartContent
{
display
:
flex
;
width
:
100%
;
justify-content
:
space-between
;
height
:
296px
;
align-items
:
center
;
margin-bottom
:
15px
;
}
}
.chart-panel-title
{
.dashboard-index
.tab-item
{
height
:
32px
;
line-height
:
32px
;
margin-right
:
20px
;
font-size
:
14px
;
font-size
:
14px
;
color
:
var
(
--sa-subfont
)
;
font-weight
:
600
;
c
ursor
:
pointer
;
c
olor
:
#1f2937
;
}
}
.chart-panel-subtitle
{
.dashboard-index
.tab-item
:last-of-type
{
font-size
:
12px
;
margin-right
:
0
;
color
:
#9ca3af
;
margin-top
:
2px
;
}
}
.chart-tabs
{
.dashboard-index
.tab-item.is-active
{
display
:
flex
;
color
:
var
(
--sa-subtitle
);
gap
:
8px
;
font-weight
:
bold
;
}
}
.chart-tab
{
@media
only
screen
and
(
max-width
:
768px
)
{
padding
:
4px
12px
;
.dashboard-index
.date-time
.el-date-editor
{
border-radius
:
6px
;
--el-date-editor-datetimerange-width
:
320px
;
font-size
:
12px
;
cursor
:
pointer
;
transition
:
all
0.2s
ease
;
color
:
#6b7280
;
background
:
#f3f4f6
;
}
}
.chart-tab.active
{
background
:
#3b82f6
;
color
:
#ffffff
;
}
}
.chart-container
{
.dashboard-index
.chart-card
{
height
:
220px
;
height
:
106px
;
color
:
var
(
--sa-subfont
);
}
}
/* 转化漏斗样式 */
.dashboard-index
.chart-card
.card-icon
{
.funnel-item
{
width
:
24px
;
display
:
flex
;
align-items
:
center
;
margin-bottom
:
12px
;
}
.funnel-label
{
width
:
80px
;
font-size
:
13px
;
color
:
#4b5563
;
flex-shrink
:
0
;
}
.funnel-bar-wrapper
{
flex
:
1
;
height
:
24px
;
height
:
24px
;
background
:
#f3f4f6
;
border-radius
:
12px
;
overflow
:
hidden
;
}
}
.funnel-bar
{
.dashboard-index
.chart-card
.num
{
height
:
100%
;
font-size
:
24px
;
border-radius
:
12px
;
color
:
var
(
--sa-subtitle
);
transition
:
width
0.5s
ease
;
display
:
flex
;
align-items
:
center
;
padding-left
:
12px
;
color
:
#ffffff
;
font-size
:
12px
;
font-weight
:
500
;
}
}
.funnel-bar.color-1
{
background
:
linear-gradient
(
90deg
,
#3b82f6
,
#60a5fa
);
}
.dashboard-index
.chart-card
.warning
{
.funnel-bar.color-2
{
background
:
linear-gradient
(
90deg
,
#06b6d4
,
#22d3ee
);
}
color
:
var
(
--el-color-warning
);
.funnel-bar.color-3
{
background
:
linear-gradient
(
90deg
,
#10b981
,
#34d399
);
}
.funnel-bar.color-4
{
background
:
linear-gradient
(
90deg
,
#f59e0b
,
#fbbf24
);
}
.funnel-bar.color-5
{
background
:
linear-gradient
(
90deg
,
#ec4899
,
#f472b6
);
}
/* 客单价分布 */
.price-dist-item
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
10px
;
}
}
.price-dist-label
{
.dashboard-index
.goods-card
,
width
:
100px
;
.dashboard-index
.hot-search-card
{
font-size
:
12px
;
height
:
326px
;
color
:
#6b7280
;
flex-shrink
:
0
;
}
}
.price-dist-bar-wrapper
{
.dashboard-index
.goods-card
.header
,
flex
:
1
;
.dashboard-index
.hot-search-card
.header
{
height
:
16px
;
line-height
:
16px
;
background
:
#f3f4f6
;
font-size
:
14px
;
border-radius
:
8px
;
color
:
var
(
--sa-subtitle
);
overflow
:
hidden
;
margin-bottom
:
16px
;
}
}
.price-dist-bar
{
.dashboard-index
.top
{
height
:
100%
;
width
:
1
8px
;
border-radius
:
8px
;
height
:
20px
;
background
:
linear-gradient
(
90deg
,
#8b5cf6
,
#a78bfa
)
;
}
}
.price-dist-percent
{
.dashboard-index
#rankingContent
{
width
:
40px
;
width
:
192px
;
text-align
:
right
;
height
:
192px
;
font-size
:
12px
;
color
:
#9ca3af
;
margin-left
:
10px
;
}
/* 实时订单动态 */
.order-list
{
max-height
:
200px
;
overflow-y
:
auto
;
}
.order-item
{
display
:
flex
;
align-items
:
center
;
padding
:
12px
;
background
:
#f9fafb
;
border-radius
:
8px
;
margin-bottom
:
10px
;
border
:
1px
solid
#e5e7eb
;
}
.order-status
{
width
:
10px
;
height
:
10px
;
border-radius
:
50%
;
background
:
#10b981
;
margin-right
:
12px
;
}
.order-info
{
flex
:
1
;
}
.order-amount
{
font-size
:
16px
;
font-weight
:
600
;
color
:
#1f2937
;
}
.order-time
{
font-size
:
11px
;
color
:
#9ca3af
;
margin-top
:
2px
;
}
.order-tag
{
padding
:
4px
8px
;
background
:
#dcfce7
;
color
:
#166534
;
border-radius
:
4px
;
font-size
:
11px
;
}
/* 注册用户增长数字 */
.growth-badge
{
background
:
linear-gradient
(
135deg
,
#10b981
,
#059669
);
color
:
#ffffff
;
padding
:
4px
12px
;
border-radius
:
20px
;
font-size
:
14px
;
font-weight
:
600
;
}
}
</style>
</style>
...
@@ -153,230 +318,487 @@
...
@@ -153,230 +318,487 @@
<el-container
class=
"panel-block"
>
<el-container
class=
"panel-block"
>
<el-main
class=
"dashboard-index-main"
>
<el-main
class=
"dashboard-index-main"
>
<el-scrollbar
height=
"100%"
>
<el-scrollbar
height=
"100%"
>
{if $auth->check('shopro/dashboard/total')}
<!--统计图 Begin-->
<el-row
:gutter=
"20"
>
<!-- -- commission code start -- -->
<!-- 新增统计卡片区域 - 白色主题 -->
<el-col
:xs=
"24"
:sm=
"24"
:md=
"8"
:lg=
"8"
:xl=
"8"
>
<div
class=
"stats-center"
>
<div
class=
"scale-card card "
>
<div
class=
"stats-center-header"
>
<div
class=
"card-title sa-flex sa-row-between"
>
<div>
<div
class=
"left"
>
{{state.total.user.title}}
<span
<div
class=
"stats-center-title"
>
class=
"num"
>
{{state.total.user.data.total}}
</span>
</div>
<i
class=
"fa fa-bar-chart"
></i>
<div>
{{state.total.user.tip}}
<span>
{{state.total.user.data.today}}
</span>
</div>
数据统计中心
</div>
</div>
<div
id=
"userTotal"
></div>
<div
class=
"stats-center-time"
>
实时业务数据监控 · 更新于 {:date('Y-m-d H:i:s')}
</div>
<div
class=
"card-footer sa-flex"
>
</div>
<span
class=
"left sa-flex"
>
<!-- 时间筛选下拉框 -->
<span
class=
"dot"
:style=
"{ background: state.total.user.color }"
></span>
<div
class=
"time-select-wrapper"
>
<span>
{{ state.total.user.footer }}
</span>
<select
id=
"time_range"
class=
"time-select"
onchange=
"loadStatistics(this.value)"
>
</span>
<option
value=
"7days"
>
近7天
</option>
<span>
{{ state.total.user.data.week }}
</span>
<option
value=
"30days"
selected
>
近30天
</option>
</div>
<option
value=
"90days"
>
近90天
</option>
</div>
</select>
</el-col>
</div>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"8"
:lg=
"8"
:xl=
"8"
>
</div>
<div
class=
"scale-card card "
>
<div
class=
"stats-card-row"
>
<div
class=
"card-title sa-flex sa-row-between"
>
<div
class=
"stats-card card-1"
>
<div
class=
"left"
>
{{state.total.agent.title}}
<span
<div
class=
"stats-card-header"
>
class=
"num"
>
{{state.total.agent.data.total}}
</span>
</div>
<span
class=
"stats-card-label"
>
成交总额
</span>
<div>
{{state.total.agent.tip}}
<span>
{{state.total.agent.data.today}}
</span>
</div>
<span
class=
"stats-card-icon"
><i
class=
"fa fa-money"
></i></span>
</div>
</div>
<div
id=
"agentTotal"
></div>
<div
class=
"stats-card-value"
id=
"stat-total-amount"
>
¥0.00
</div>
<div
class=
"card-footer sa-flex"
>
<div
class=
"stats-card-trend up"
><i
class=
"fa fa-arrow-up"
></i>
<span
id=
"stat-total-trend"
>
+0%
</span>
较上月
</div>
<span
class=
"left sa-flex"
>
</div>
<span
class=
"dot"
:style=
"{ background: state.total.agent.color }"
></span>
<div
class=
"stats-card card-2"
>
<span>
{{ state.total.agent.footer }}
</span>
<div
class=
"stats-card-header"
>
</span>
<span
class=
"stats-card-label"
>
净利润率
</span>
<span>
{{ state.total.agent.data.week }}
</span>
<span
class=
"stats-card-icon"
><i
class=
"fa fa-percent"
></i></span>
</div>
</div>
</div>
<div
class=
"stats-card-value"
id=
"stat-profit-rate"
>
0%
</div>
</el-col>
<div
class=
"stats-card-trend up"
><i
class=
"fa fa-arrow-up"
></i>
<span
id=
"stat-profit-trend"
>
+0%
</span>
较上月
</div>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"8"
:lg=
"8"
:xl=
"8"
>
</div>
<div
class=
"scale-card card "
>
<div
class=
"stats-card card-3"
>
<div
class=
"card-title sa-flex sa-row-between"
>
<div
class=
"stats-card-header"
>
<div
class=
"left"
>
{{state.total.share.title}}
<span
<span
class=
"stats-card-label"
>
客单价
</span>
class=
"num"
>
{{state.total.share.data.total}}
</span>
</div>
<span
class=
"stats-card-icon"
><i
class=
"fa fa-shopping-cart"
></i></span>
<div>
{{state.total.share.tip}}
<span>
{{state.total.share.data.today}}
</span>
</div>
</div>
</div>
<div
class=
"stats-card-value"
id=
"stat-avg-amount"
>
¥0.00
</div>
<div
id=
"shareTotal"
></div>
<div
class=
"stats-card-trend up"
><i
class=
"fa fa-arrow-up"
></i>
<span
id=
"stat-avg-trend"
>
+0%
</span>
较上月
</div>
<div
class=
"card-footer sa-flex"
>
</div>
<span
class=
"left sa-flex"
>
<div
class=
"stats-card card-4"
>
<span
class=
"dot"
:style=
"{ background: state.total.share.color }"
></span>
<div
class=
"stats-card-header"
>
<span>
{{ state.total.share.footer }}
</span>
<span
class=
"stats-card-label"
>
订单数
</span>
</span>
<span
class=
"stats-card-icon"
><i
class=
"fa fa-file-text"
></i></span>
<span>
{{ state.total.share.data.week }}
</span>
</div>
</div>
<div
class=
"stats-card-value"
id=
"stat-order-count"
>
0
</div>
</div>
<div
class=
"stats-card-trend up"
><i
class=
"fa fa-arrow-up"
></i>
<span
id=
"stat-order-trend"
>
+0%
</span>
较上月
</div>
</el-col>
</div>
<!-- -- commission code end -- -->
<div
class=
"stats-card card-5"
>
<!-- <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<div
class=
"stats-card-header"
>
<div class="scale-card card ">
<span
class=
"stats-card-label"
>
访客数
</span>
<div class="card-title sa-flex sa-row-between">
<span
class=
"stats-card-icon"
><i
class=
"fa fa-users"
></i></span>
<div class="left">{{state.total.user.title}} <span
</div>
class="num">{{state.total.user.data.total}}</span> </div>
<div
class=
"stats-card-value"
id=
"stat-visitor-count"
>
0
</div>
<div>{{state.total.user.tip}} <span>{{state.total.user.data.today}}</span> </div>
<div
class=
"stats-card-trend"
id=
"stat-visitor-trend"
><i
class=
"fa fa-arrow-down"
></i>
<span>
-0%
</span>
较上月
</div>
</div>
</div>
<div id="userTotal"></div>
<div
class=
"stats-card card-6"
>
<div class="card-footer sa-flex">
<div
class=
"stats-card-header"
>
<span class="left sa-flex">
<span
class=
"stats-card-label"
>
转化率
</span>
<span class="dot" :style="{ background: state.total.user.color }"></span>
<span
class=
"stats-card-icon"
><i
class=
"fa fa-line-chart"
></i></span>
<span>{{ state.total.user.footer }}</span>
</div>
</span>
<div
class=
"stats-card-value"
id=
"stat-conversion-rate"
>
0%
</div>
<span>{{ state.total.user.data.week }}</span>
<div
class=
"stats-card-trend up"
><i
class=
"fa fa-arrow-up"
></i>
<span
id=
"stat-conversion-trend"
>
+0%
</span>
较上月
</div>
</div>
</div>
</div>
<div
class=
"stats-card card-7"
>
</el-col>
<div
class=
"stats-card-header"
>
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
<span
class=
"stats-card-label"
>
注册用户
</span>
<div class="scale-card card ">
<span
class=
"stats-card-icon"
><i
class=
"fa fa-user-plus"
></i></span>
<div class="card-title sa-flex sa-row-between">
</div>
<div class="left">{{state.total.share.title}} <span
<div
class=
"stats-card-value"
id=
"stat-register-users"
>
0
</div>
class="num">{{state.total.share.data.total}}</span> </div>
<div
class=
"stats-card-trend up"
><i
class=
"fa fa-arrow-up"
></i>
<span
id=
"stat-register-trend"
>
+0(+0%)
</span></div>
<div>{{state.total.share.tip}} <span>{{state.total.share.data.today}}</span> </div>
</div>
</div>
</div>
<div id="shareTotal"></div>
</div>
<div class="card-footer sa-flex">
<span class="left sa-flex">
<!-- 图表区域 -->
<span class="dot" :style="{ background: state.total.share.color }"></span>
<div
class=
"row"
>
<span>{{ state.total.share.footer }}</span>
<div
class=
"col-lg-6"
>
</span>
<div
class=
"chart-panel"
>
<span>{{ state.total.share.data.week }}</span>
<div
class=
"chart-panel-header"
>
</div>
<div>
</div>
<div
class=
"chart-panel-title"
>
成交总额趋势
</div>
</el-col> -->
<div
class=
"chart-panel-subtitle"
>
近30天每日GMV走势
</div>
</el-row>
</div>
{/if}
<div
class=
"chart-tabs"
>
{if $auth->check('shopro/dashboard/chart')}
<span
class=
"chart-tab active"
onclick=
"switchChartTab('gmv', 'day')"
>
日
</span>
<el-row
:gutter=
"20"
>
<span
class=
"chart-tab"
onclick=
"switchChartTab('gmv', 'week')"
>
周
</span>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"24"
:lg=
"16"
:xl=
"16"
>
<span
class=
"chart-tab"
onclick=
"switchChartTab('gmv', 'month')"
>
月
</span>
<div
class=
"bar-card card"
>
</div>
<div
class=
"sa-flex sa-row-between sa-flex-wrap"
>
</div>
<div
class=
"sa-flex"
>
<div
id=
"gmv-chart"
class=
"chart-container"
></div>
<div
class=
"tab-item"
:class=
"chart.tabActive == key ? 'is-active' : ''"
</div>
v-for=
"(value, key) in chart.tabsData"
:key=
"key"
</div>
@
click=
"onChangeTabActive(key)"
>
<div
class=
"col-lg-6"
>
{{ value }}
<div
class=
"chart-panel"
>
</div>
<div
class=
"chart-panel-header"
>
</div>
<div>
<div
class=
"date-time"
>
<div
class=
"chart-panel-title"
>
订单数趋势
</div>
<el-date-picker
v-model=
"chart.dateTime"
type=
"datetimerange"
<div
class=
"chart-panel-subtitle"
>
近30天订单量走势
</div>
value-format=
"YYYY-MM-DD HH:mm:ss"
format=
"YYYY-MM-DD HH:mm:ss"
</div>
:default-time=
"[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 2, 1, 23, 59, 59)]"
<div
class=
"chart-tabs"
>
range-separator=
"至"
start-placeholder=
"开始日期"
end-placeholder=
"结束日期"
<span
class=
"chart-tab active"
onclick=
"switchChartTab('order', 'day')"
>
日
</span>
:shortcuts=
"chart.shortcuts"
@
change=
"onChangeDateTime"
:editable=
"false"
>
<span
class=
"chart-tab"
onclick=
"switchChartTab('order', 'week')"
>
周
</span>
</el-date-picker>
<span
class=
"chart-tab"
onclick=
"switchChartTab('order', 'month')"
>
月
</span>
</div>
</div>
</div>
</div>
<div
id=
"chartContent"
></div>
<div
id=
"order-chart"
class=
"chart-container"
></div>
</div>
</div>
</el-col>
</div>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"24"
:lg=
"8"
:xl=
"8"
>
</div>
<el-row
:gutter=
"20"
>
<div
class=
"row"
>
<el-col
:xs=
"12"
:sm=
"8"
:md=
"12"
:lg=
"12"
:xl=
"12"
v-for=
"(value, key) in statistics"
<div
class=
"col-lg-4"
>
:key=
"key"
>
<div
class=
"chart-panel"
>
<div
class=
"chart-card scale-card card"
>
<div
class=
"chart-panel-header"
>
<div
class=
"sa-flex sa-row-between mb-2"
>
<div>
<img
class=
"card-icon"
<div
class=
"chart-panel-title"
>
转化漏斗
</div>
:src=
"`/assets/addons/shopro/img/dashboard/${key}.png`"
/>
<div
class=
"chart-panel-subtitle"
>
用户行为转化路径
</div>
<div
class=
"sa-flex"
@
click=
"onOpen(value.status)"
>
</div>
<el-button
type=
"info"
link
size=
"small"
>
详情
</div>
<el-icon
class=
"ml-1"
>
<div
class=
"funnel-container"
id=
"funnel-container"
>
<arrow-right
/>
<div
class=
"funnel-item"
>
</el-icon>
<span
class=
"funnel-label"
>
访问
</span>
</el-button>
<div
class=
"funnel-bar-wrapper"
>
</div>
<div
class=
"funnel-bar color-1"
id=
"funnel-visit"
style=
"width: 100%"
>
0
</div>
</div>
</div>
<div
class=
"num mb-1"
>
{{ value.num }}
</div>
</div>
<div
class=
"sa-flex sa-row-between"
>
<div
class=
"funnel-item"
>
<span>
{{ value.text }}
</span>
<span
class=
"funnel-label"
>
浏览商品
</span>
<el-popover
popper-class=
"sa-popper"
placement=
"top"
trigger=
"hover"
<div
class=
"funnel-bar-wrapper"
>
:content=
"value.tip"
>
<div
class=
"funnel-bar color-2"
id=
"funnel-browse"
style=
"width: 50%"
>
0
</div>
<template
#
reference
>
</div>
<el-icon
class=
"warning"
>
</div>
<Warning
/>
<div
class=
"funnel-item"
>
</el-icon>
<span
class=
"funnel-label"
>
加入购物车
</span>
</template>
<div
class=
"funnel-bar-wrapper"
>
</el-popover>
<div
class=
"funnel-bar color-3"
id=
"funnel-cart"
style=
"width: 30%"
>
0
</div>
</div>
</div>
</div>
</div>
</el-col>
<div
class=
"funnel-item"
>
</el-row>
<span
class=
"funnel-label"
>
提交订单
</span>
</el-col>
<div
class=
"funnel-bar-wrapper"
>
</el-row>
<div
class=
"funnel-bar color-4"
id=
"funnel-submit"
style=
"width: 20%"
>
0
</div>
{/if}
</div>
{if $auth->check('shopro/dashboard/ranking')}
</div>
<el-row
:gutter=
"20"
>
<div
class=
"funnel-item"
>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"24"
:lg=
"12"
:xl=
"12"
>
<span
class=
"funnel-label"
>
完成支付
</span>
<div
class=
"goods-card card"
>
<div
class=
"funnel-bar-wrapper"
>
<div
class=
"header"
>
<div
class=
"funnel-bar color-5"
id=
"funnel-pay"
style=
"width: 10%"
>
0
</div>
<span>
销量榜
</span>
</div>
</div>
</div>
<el-table
class=
"sa-table"
:data=
"ranking.goods"
stripe
>
</div>
<el-table-column
label=
"排名"
width=
"60"
align=
"center"
>
</div>
<template
#
default=
"scope"
>
</div>
<img
class=
"top"
<div
class=
"col-lg-8"
>
:src=
"`/assets/addons/shopro/img/dashboard/top${scope.$index + 1}.png`"
/>
<div
class=
"chart-panel"
>
</template>
<div
class=
"chart-panel-header"
>
</el-table-column>
<div>
<el-table-column
label=
"名称"
min-width=
"200"
>
<div
class=
"chart-panel-title"
>
注册用户增长
</div>
<template
#
default=
"scope"
>
<div
class=
"chart-panel-subtitle"
>
近30天新增用户趋势
</div>
<div
class=
"sa-flex"
>
</div>
<sa-image
:url=
"scope.row.image"
size=
"20"
></sa-image>
<span
class=
"growth-badge"
id=
"growth-badge"
>
+0
</span>
<div
class=
"ml-2 sa-table-line-1"
>
</div>
{{ scope.row.title || '-' }}
<div
id=
"user-growth-chart"
class=
"chart-container"
></div>
</div>
</div>
</div>
</div>
</template>
</div>
</el-table-column>
<div
class=
"row"
>
<el-table-column
label=
"销量"
width=
"100"
>
<div
class=
"col-lg-4"
>
<template
#
default=
"scope"
>
<div
class=
"chart-panel"
>
<div
class=
"sa-table-line-1"
>
<div
class=
"chart-panel-header"
>
{{ scope.row.real_sales || '-' }}
<div>
</div>
<div
class=
"chart-panel-title"
>
客单价分布
</div>
</template>
<div
class=
"chart-panel-subtitle"
>
不同价格区间订单占比
</div>
</el-table-column>
</div>
</el-table>
</div>
</div>
<div
class=
"price-dist-container"
id=
"price-dist-container"
>
</el-col>
<div
class=
"price-dist-item"
>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"24"
:lg=
"12"
:xl=
"12"
>
<span
class=
"price-dist-label"
>
0-100元
</span>
<div
class=
"hot-search-card card"
>
<div
class=
"price-dist-bar-wrapper"
>
<div
class=
"header"
>
<div
class=
"price-dist-bar"
id=
"price-0-100"
style=
"width: 0%"
></div>
<span>
热搜榜
</span>
</div>
</div>
<span
class=
"price-dist-percent"
id=
"price-0-100-percent"
>
0%
</span>
<el-row
:gutter=
"16"
>
</div>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"16"
:lg=
"16"
:xl=
"16"
>
<div
class=
"price-dist-item"
>
<el-table
class=
"sa-table"
:data=
"ranking.hot_search"
stripe
>
<span
class=
"price-dist-label"
>
100-300元
</span>
<el-table-column
label=
"排名"
width=
"60"
align=
"center"
>
<div
class=
"price-dist-bar-wrapper"
>
<template
#
default=
"scope"
>
<div
class=
"price-dist-bar"
id=
"price-100-300"
style=
"width: 0%"
></div>
<img
class=
"top"
</div>
:src=
"`/assets/addons/shopro/img/dashboard/top${scope.$index + 1}.png`"
/>
<span
class=
"price-dist-percent"
id=
"price-100-300-percent"
>
0%
</span>
</template>
</div>
</el-table-column>
<div
class=
"price-dist-item"
>
<el-table-column
label=
"名称"
min-width=
"200"
>
<span
class=
"price-dist-label"
>
300-500元
</span>
<template
#
default=
"scope"
>
<div
class=
"price-dist-bar-wrapper"
>
<div
class=
"sa-table-line-1"
>
<div
class=
"price-dist-bar"
id=
"price-300-500"
style=
"width: 0%"
></div>
{{ scope.row.keyword || '-' }}
</div>
</div>
<span
class=
"price-dist-percent"
id=
"price-300-500-percent"
>
0%
</span>
</template>
</div>
</el-table-column>
<div
class=
"price-dist-item"
>
<el-table-column
label=
"搜索量(次)"
width=
"100"
>
<span
class=
"price-dist-label"
>
500-1000元
</span>
<template
#
default=
"scope"
>
<div
class=
"price-dist-bar-wrapper"
>
<div
class=
"sa-table-line-1"
>
<div
class=
"price-dist-bar"
id=
"price-500-1000"
style=
"width: 0%"
></div>
{{ scope.row.num }}
</div>
</div>
<span
class=
"price-dist-percent"
id=
"price-500-1000-percent"
>
0%
</span>
</template>
</div>
</el-table-column>
<div
class=
"price-dist-item"
>
</el-table>
<span
class=
"price-dist-label"
>
1000元以上
</span>
</el-col>
<div
class=
"price-dist-bar-wrapper"
>
<el-col
:xs=
"24"
:sm=
"24"
:md=
"8"
:lg=
"8"
:xl=
"8"
>
<div
class=
"price-dist-bar"
id=
"price-1000"
style=
"width: 0%"
></div>
<div
class=
"sa-flex sa-row-center"
>
</div>
<div
id=
"rankingContent"
></div>
<span
class=
"price-dist-percent"
id=
"price-1000-percent"
>
0%
</span>
</div>
</div>
</el-col>
</div>
</el-row>
</div>
</div>
</div>
</el-col>
<div
class=
"col-lg-8"
>
</el-row>
<div
class=
"chart-panel"
>
{/if}
<div
class=
"chart-panel-header"
>
<div>
<div
class=
"chart-panel-title"
>
实时订单动态
</div>
<div
class=
"chart-panel-subtitle"
>
最新成交订单
</div>
</div>
</div>
<div
class=
"order-list"
id=
"order-list"
>
<div
class=
"order-item"
>
<span
class=
"order-status"
></span>
<div
class=
"order-info"
>
<div
class=
"order-amount"
>
¥ 0.00
</div>
<div
class=
"order-time"
>
加载中...
</div>
</div>
<span
class=
"order-tag"
>
成功
</span>
</div>
</div>
</div>
</div>
</div>
<!-- 图表初始化脚本 -->
<!--统计图 End-->
</el-scrollbar>
</el-scrollbar>
</el-main>
</el-main>
</el-container>
</el-container>
</div>
</div>
<script
type=
"text/javascript"
>
var
gmvChart
,
orderChart
,
userGrowthChart
;
var
currentTimeRange
=
'30days'
;
function
generateDateLabels
(
days
)
{
var
labels
=
[];
for
(
var
i
=
days
-
1
;
i
>=
0
;
i
--
)
{
var
date
=
new
Date
();
date
.
setDate
(
date
.
getDate
()
-
i
);
labels
.
push
((
date
.
getMonth
()
+
1
)
+
'月'
+
date
.
getDate
()
+
'日'
);
}
return
labels
;
}
function
generateRandomData
(
length
,
min
,
max
)
{
var
data
=
[];
for
(
var
i
=
0
;
i
<
length
;
i
++
)
{
data
.
push
(
Math
.
floor
(
Math
.
random
()
*
(
max
-
min
+
1
))
+
min
);
}
return
data
;
}
function
initCharts
()
{
gmvChart
=
echarts
.
init
(
document
.
getElementById
(
'gmv-chart'
));
var
gmvOption
=
{
grid
:
{
top
:
20
,
right
:
20
,
bottom
:
30
,
left
:
50
},
xAxis
:
{
type
:
'category'
,
data
:
generateDateLabels
(
30
),
axisLine
:
{
lineStyle
:
{
color
:
'#e5e7eb'
}
},
axisLabel
:
{
color
:
'#9ca3af'
,
fontSize
:
10
}
},
yAxis
:
{
type
:
'value'
,
axisLine
:
{
show
:
false
},
axisTick
:
{
show
:
false
},
splitLine
:
{
lineStyle
:
{
color
:
'#f3f4f6'
}
},
axisLabel
:
{
color
:
'#9ca3af'
,
fontSize
:
10
,
formatter
:
function
(
value
)
{
return
'¥'
+
(
value
/
10000
).
toFixed
(
0
)
+
'万'
;
}
}
},
series
:
[{
type
:
'line'
,
smooth
:
true
,
data
:
generateRandomData
(
30
,
50000
,
150000
),
lineStyle
:
{
color
:
'#667eea'
,
width
:
3
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'rgba(102, 126, 234, 0.3)'
},
{
offset
:
1
,
color
:
'rgba(102, 126, 234, 0.05)'
}
])
},
symbol
:
'none'
}]
};
gmvChart
.
setOption
(
gmvOption
);
orderChart
=
echarts
.
init
(
document
.
getElementById
(
'order-chart'
));
var
orderOption
=
{
grid
:
{
top
:
20
,
right
:
20
,
bottom
:
30
,
left
:
50
},
xAxis
:
{
type
:
'category'
,
data
:
generateDateLabels
(
30
),
axisLine
:
{
lineStyle
:
{
color
:
'#e5e7eb'
}
},
axisLabel
:
{
color
:
'#9ca3af'
,
fontSize
:
10
}
},
yAxis
:
{
type
:
'value'
,
axisLine
:
{
show
:
false
},
axisTick
:
{
show
:
false
},
splitLine
:
{
lineStyle
:
{
color
:
'#f3f4f6'
}
},
axisLabel
:
{
color
:
'#9ca3af'
,
fontSize
:
10
}
},
series
:
[{
type
:
'bar'
,
data
:
generateRandomData
(
30
,
1500
,
4000
),
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'#10b981'
},
{
offset
:
1
,
color
:
'#34d399'
}
]),
borderRadius
:
[
4
,
4
,
0
,
0
]
}
}]
};
orderChart
.
setOption
(
orderOption
);
userGrowthChart
=
echarts
.
init
(
document
.
getElementById
(
'user-growth-chart'
));
var
userGrowthOption
=
{
grid
:
{
top
:
20
,
right
:
20
,
bottom
:
30
,
left
:
50
},
legend
:
{
data
:
[
'新增用户'
,
'活跃用户'
],
top
:
0
,
textStyle
:
{
color
:
'#6b7280'
,
fontSize
:
12
}
},
xAxis
:
{
type
:
'category'
,
data
:
generateDateLabels
(
30
),
axisLine
:
{
lineStyle
:
{
color
:
'#e5e7eb'
}
},
axisLabel
:
{
color
:
'#9ca3af'
,
fontSize
:
10
}
},
yAxis
:
{
type
:
'value'
,
axisLine
:
{
show
:
false
},
axisTick
:
{
show
:
false
},
splitLine
:
{
lineStyle
:
{
color
:
'#f3f4f6'
}
},
axisLabel
:
{
color
:
'#9ca3af'
,
fontSize
:
10
,
formatter
:
function
(
value
)
{
return
(
value
/
10000
).
toFixed
(
0
)
+
'万'
;
}
}
},
series
:
[
{
name
:
'新增用户'
,
type
:
'line'
,
smooth
:
true
,
data
:
generateRandomData
(
30
,
2000
,
8000
),
lineStyle
:
{
color
:
'#f59e0b'
,
width
:
2
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'rgba(245, 158, 11, 0.2)'
},
{
offset
:
1
,
color
:
'rgba(245, 158, 11, 0.02)'
}
])
},
symbol
:
'none'
},
{
name
:
'活跃用户'
,
type
:
'line'
,
smooth
:
true
,
data
:
generateRandomData
(
30
,
8000
,
15000
),
lineStyle
:
{
color
:
'#06b6d4'
,
width
:
2
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
'rgba(6, 182, 212, 0.2)'
},
{
offset
:
1
,
color
:
'rgba(6, 182, 212, 0.02)'
}
])
},
symbol
:
'none'
}
]
};
userGrowthChart
.
setOption
(
userGrowthOption
);
window
.
addEventListener
(
'resize'
,
function
()
{
gmvChart
.
resize
();
orderChart
.
resize
();
userGrowthChart
.
resize
();
});
}
function
switchChartTab
(
type
,
period
)
{
console
.
log
(
'Switching '
+
type
+
' chart to '
+
period
);
}
function
loadStatistics
(
timeRange
)
{
currentTimeRange
=
timeRange
;
$
.
getJSON
(
'{:url("shopro/dashboard/statistics")}'
,
{
time_range
:
timeRange
},
function
(
res
)
{
if
(
res
.
code
===
1
)
{
var
data
=
res
.
data
;
document
.
getElementById
(
'stat-total-amount'
).
textContent
=
'¥'
+
formatNumber
(
data
.
total_amount
,
2
);
document
.
getElementById
(
'stat-profit-rate'
).
textContent
=
data
.
profit_rate
;
document
.
getElementById
(
'stat-avg-amount'
).
textContent
=
'¥'
+
formatNumber
(
data
.
avg_amount
,
2
);
document
.
getElementById
(
'stat-order-count'
).
textContent
=
formatNumber
(
data
.
order_count
);
document
.
getElementById
(
'stat-visitor-count'
).
textContent
=
formatNumber
(
data
.
visitor_count
);
document
.
getElementById
(
'stat-conversion-rate'
).
textContent
=
data
.
conversion_rate
;
document
.
getElementById
(
'stat-register-users'
).
textContent
=
formatNumber
(
data
.
register_users
);
}
});
loadFunnel
(
timeRange
);
loadPriceDistribution
(
timeRange
);
loadRecentOrders
();
}
function
loadFunnel
(
timeRange
)
{
$
.
getJSON
(
'{:url("shopro/dashboard/funnel")}'
,
{
time_range
:
timeRange
},
function
(
res
)
{
if
(
res
.
code
===
1
)
{
var
data
=
res
.
data
;
var
max
=
data
.
visit
||
1
;
document
.
getElementById
(
'funnel-visit'
).
textContent
=
formatNumber
(
data
.
visit
);
var
browsePercent
=
max
>
0
?
Math
.
round
(
data
.
browse
/
max
*
100
)
:
0
;
document
.
getElementById
(
'funnel-browse'
).
textContent
=
formatNumber
(
data
.
browse
);
document
.
getElementById
(
'funnel-browse'
).
style
.
width
=
browsePercent
+
'%'
;
var
cartPercent
=
max
>
0
?
Math
.
round
(
data
.
cart
/
max
*
100
)
:
0
;
document
.
getElementById
(
'funnel-cart'
).
textContent
=
formatNumber
(
data
.
cart
);
document
.
getElementById
(
'funnel-cart'
).
style
.
width
=
cartPercent
+
'%'
;
var
submitPercent
=
max
>
0
?
Math
.
round
(
data
.
submit_order
/
max
*
100
)
:
0
;
document
.
getElementById
(
'funnel-submit'
).
textContent
=
formatNumber
(
data
.
submit_order
);
document
.
getElementById
(
'funnel-submit'
).
style
.
width
=
submitPercent
+
'%'
;
var
payPercent
=
max
>
0
?
Math
.
round
(
data
.
pay
/
max
*
100
)
:
0
;
document
.
getElementById
(
'funnel-pay'
).
textContent
=
formatNumber
(
data
.
pay
);
document
.
getElementById
(
'funnel-pay'
).
style
.
width
=
payPercent
+
'%'
;
}
});
}
function
loadPriceDistribution
(
timeRange
)
{
$
.
getJSON
(
'{:url("shopro/dashboard/priceDistribution")}'
,
{
time_range
:
timeRange
},
function
(
res
)
{
if
(
res
.
code
===
1
)
{
var
data
=
res
.
data
;
document
.
getElementById
(
'price-0-100'
).
style
.
width
=
data
[
'0-100'
]
+
'%'
;
document
.
getElementById
(
'price-0-100-percent'
).
textContent
=
data
[
'0-100'
]
+
'%'
;
document
.
getElementById
(
'price-100-300'
).
style
.
width
=
data
[
'100-300'
]
+
'%'
;
document
.
getElementById
(
'price-100-300-percent'
).
textContent
=
data
[
'100-300'
]
+
'%'
;
document
.
getElementById
(
'price-300-500'
).
style
.
width
=
data
[
'300-500'
]
+
'%'
;
document
.
getElementById
(
'price-300-500-percent'
).
textContent
=
data
[
'300-500'
]
+
'%'
;
document
.
getElementById
(
'price-500-1000'
).
style
.
width
=
data
[
'500-1000'
]
+
'%'
;
document
.
getElementById
(
'price-500-1000-percent'
).
textContent
=
data
[
'500-1000'
]
+
'%'
;
document
.
getElementById
(
'price-1000'
).
style
.
width
=
data
[
'1000+'
]
+
'%'
;
document
.
getElementById
(
'price-1000-percent'
).
textContent
=
data
[
'1000+'
]
+
'%'
;
}
});
}
function
loadRecentOrders
()
{
$
.
getJSON
(
'{:url("shopro/dashboard/recentOrders")}'
,
function
(
res
)
{
if
(
res
.
code
===
1
)
{
var
orders
=
res
.
data
;
var
html
=
''
;
orders
.
forEach
(
function
(
order
)
{
html
+=
'<div class="order-item">'
+
'<span class="order-status"></span>'
+
'<div class="order-info">'
+
'<div class="order-amount">¥ '
+
formatNumber
(
order
.
amount
,
2
)
+
'</div>'
+
'<div class="order-time">'
+
order
.
location
+
' · '
+
order
.
time
+
'</div>'
+
'</div>'
+
'<span class="order-tag">成功</span>'
+
'</div>'
;
});
document
.
getElementById
(
'order-list'
).
innerHTML
=
html
;
}
});
}
function
formatNumber
(
num
,
decimals
)
{
if
(
decimals
!==
undefined
)
{
return
parseFloat
(
num
||
0
).
toFixed
(
decimals
);
}
return
parseInt
(
num
||
0
).
toLocaleString
();
}
document
.
addEventListener
(
'DOMContentLoaded'
,
function
()
{
initCharts
();
loadStatistics
(
'30days'
);
});
</script>
\ No newline at end of file
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