Commit c64d3b9c authored by 刘小敏's avatar 刘小敏

数据中心优化

parent 32f0f5e6
...@@ -36,24 +36,6 @@ class Dashboard extends Common ...@@ -36,24 +36,6 @@ class Dashboard extends Common
*/ */
public function index() public function index()
{ {
// $cardList = [
// [
// 'name' => 'total',
// 'card' => 'shopro/dashboard/total',
// 'status' => $this->auth->check('shopro/dashboard/total')
// ],
// [
// 'name' => 'chart',
// 'card' => 'shopro/dashboard/chart',
// 'status' => $this->auth->check('shopro/dashboard/chart')
// ],
// [
// 'name' => 'ranking',
// 'card' => 'shopro/dashboard/ranking',
// 'status' => $this->auth->check('shopro/dashboard/ranking')
// ],
// ];
// $this->assignconfig('cardList', $cardList);
return $this->view->fetch(); return $this->view->fetch();
} }
...@@ -90,10 +72,20 @@ class Dashboard extends Common ...@@ -90,10 +72,20 @@ class Dashboard extends Common
$cacheKey = 'shopro:data_stats_center:' . $timeRange; $cacheKey = 'shopro:data_stats_center:' . $timeRange;
$cachedData = cache($cacheKey); $cachedData = cache($cacheKey);
// 获取最新统计记录的更新时间
$updateTime = Db::name('shopro_data_stats_center')
->order('created_at', 'desc')
->value('created_at');
if ($cachedData !== false && isset($cachedData['summary']) && isset($cachedData['trend'])) { if ($cachedData !== false && isset($cachedData['summary']) && isset($cachedData['trend'])) {
$summary = $cachedData['summary']; $summary = $cachedData['summary'];
$trendData = $this->buildTrendDataForChart($cachedData['trend'], $period); $trendData = $this->buildTrendDataForChart($cachedData['trend'], $period);
// 较上周期 效果计算
// - 上一周期和当前周期数据都是0时 → 显示"持平"
// - 上一周期是0但当前周期有数据 → 显示"+100%"
// - 上一周期有数据但当前周期是0 → 显示"-100%"
// - 两个周期都有数据时 → 显示实际百分比
$result = [ $result = [
'total_amount' => $summary['total_amount']['value'], 'total_amount' => $summary['total_amount']['value'],
'total_amount_compare_rate' => $summary['total_amount']['compare_rate'], 'total_amount_compare_rate' => $summary['total_amount']['compare_rate'],
...@@ -110,7 +102,8 @@ class Dashboard extends Common ...@@ -110,7 +102,8 @@ class Dashboard extends Common
'register_users' => $summary['register_users']['value'], 'register_users' => $summary['register_users']['value'],
'register_users_compare_rate' => $summary['register_users']['compare_rate'], 'register_users_compare_rate' => $summary['register_users']['compare_rate'],
'trend_data' => $trendData, 'trend_data' => $trendData,
'from_cache' => true 'from_cache' => true,
'update_time' => $updateTime ? date('Y-m-d H:i:s', $updateTime) : date('Y-m-d H:i:s')
]; ];
$this->success('获取成功', null, $result); $this->success('获取成功', null, $result);
...@@ -121,14 +114,22 @@ class Dashboard extends Common ...@@ -121,14 +114,22 @@ class Dashboard extends Common
$result = [ $result = [
'total_amount' => $trendData['total_amount'], 'total_amount' => $trendData['total_amount'],
'total_amount_compare_rate' => $trendData['total_amount_compare_rate'] ?? 0,
'profit_rate' => $trendData['profit_rate'], 'profit_rate' => $trendData['profit_rate'],
'profit_rate_compare_rate' => $trendData['profit_rate_compare_rate'] ?? 0,
'avg_amount' => $trendData['avg_amount'], 'avg_amount' => $trendData['avg_amount'],
'avg_amount_compare_rate' => $trendData['avg_amount_compare_rate'] ?? 0,
'order_count' => $trendData['order_count'], 'order_count' => $trendData['order_count'],
'order_count_compare_rate' => $trendData['order_count_compare_rate'] ?? 0,
'visitor_count' => $trendData['visitor_count'], 'visitor_count' => $trendData['visitor_count'],
'visitor_count_compare_rate' => $trendData['visitor_count_compare_rate'] ?? 0,
'conversion_rate' => $trendData['conversion_rate'], 'conversion_rate' => $trendData['conversion_rate'],
'conversion_rate_compare_rate' => $trendData['conversion_rate_compare_rate'] ?? 0,
'register_users' => $trendData['register_users'], 'register_users' => $trendData['register_users'],
'register_users_compare_rate' => $trendData['register_users_compare_rate'] ?? 0,
'trend_data' => $trendData['trend_data'], 'trend_data' => $trendData['trend_data'],
'from_cache' => false 'from_cache' => false,
'update_time' => $updateTime ? date('Y-m-d H:i:s', $updateTime) : date('Y-m-d H:i:s')
]; ];
$this->success('获取成功', null, $result); $this->success('获取成功', null, $result);
...@@ -327,6 +328,7 @@ class Dashboard extends Common ...@@ -327,6 +328,7 @@ class Dashboard extends Common
$orderCount = 0; $orderCount = 0;
$registerUsers = 0; $registerUsers = 0;
$visitorCount = 0; $visitorCount = 0;
$buyerCount = 0;
$format = $this->getDateFormat($period); $format = $this->getDateFormat($period);
...@@ -343,13 +345,16 @@ class Dashboard extends Common ...@@ -343,13 +345,16 @@ class Dashboard extends Common
->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED]) ->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
->count(); ->count();
$buyerNum = Order::whereBetween('createtime', [$startTime, $endTime])
->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
->where('user_id', '>', 0)
->distinct('user_id')
->count('user_id');
$registerNum = User::whereBetween('createtime', [$startTime, $endTime])->count(); $registerNum = User::whereBetween('createtime', [$startTime, $endTime])->count();
try { try {
$visitorNum = Db::name('shopro_user_goods_log') $visitorNum = $this->getVisitorCountFromTrack($startTime, $endTime);
->whereBetween('createtime', [$startTime, $endTime])
->distinct(true)
->count('user_id');
} catch (\Exception $e) { } catch (\Exception $e) {
$visitorNum = 0; $visitorNum = 0;
} }
...@@ -365,20 +370,63 @@ class Dashboard extends Common ...@@ -365,20 +370,63 @@ class Dashboard extends Common
$orderCount += $orderNum; $orderCount += $orderNum;
$registerUsers += $registerNum; $registerUsers += $registerNum;
$visitorCount += $visitorNum; $visitorCount += $visitorNum;
$buyerCount += $buyerNum;
} }
$previousStartTime = $startTime - ($days * 86400);
$previousEndTime = $startTime - 1;
$previousAmount = Order::whereBetween('createtime', [$previousStartTime, $previousEndTime])
->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
->sum('pay_fee') ?: 0;
$previousOrderNum = Order::whereBetween('createtime', [$previousStartTime, $previousEndTime])
->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
->count();
$previousBuyerNum = Order::whereBetween('createtime', [$previousStartTime, $previousEndTime])
->whereIn('status', [Order::STATUS_PAID, Order::STATUS_COMPLETED])
->where('user_id', '>', 0)
->distinct('user_id')
->count('user_id');
$previousRegisterNum = User::whereBetween('createtime', [$previousStartTime, $previousEndTime])->count();
try {
$previousVisitorNum = $this->getVisitorCountFromTrack($previousStartTime, $previousEndTime);
} catch (\Exception $e) {
$previousVisitorNum = 0;
}
$previousAvgAmount = $previousOrderNum > 0 ? bcdiv($previousAmount, $previousOrderNum, 2) : 0;
$previousConversionRate = $previousVisitorNum > 0 ? bcdiv($previousBuyerNum * 100, $previousVisitorNum, 2) : 0;
$avgAmount = $orderCount > 0 ? bcdiv($totalAmount, $orderCount, 2) : 0; $avgAmount = $orderCount > 0 ? bcdiv($totalAmount, $orderCount, 2) : 0;
$profitRate = $totalAmount > 0 ? '18.6%' : '0%'; $profitRate = $totalAmount > 0 ? '18.6%' : '0%';
$conversionRate = $visitorCount > 0 ? bcdiv($orderCount * 100, $visitorCount, 2) . '%' : '0%'; $conversionRate = $visitorCount > 0 ? bcdiv($buyerCount * 100, $visitorCount, 2) . '%' : '0%';
$totalAmountCompareRate = $previousAmount > 0 ? round((($totalAmount - $previousAmount) / $previousAmount) * 100, 2) : ($totalAmount > 0 ? 100 : ($previousAmount == 0 ? null : -100));
$avgAmountCompareRate = $previousAvgAmount > 0 ? round((($avgAmount - $previousAvgAmount) / $previousAvgAmount) * 100, 2) : ($avgAmount > 0 ? 100 : ($previousAvgAmount == 0 ? null : -100));
$orderCountCompareRate = $previousOrderNum > 0 ? round((($orderCount - $previousOrderNum) / $previousOrderNum) * 100, 2) : ($orderCount > 0 ? 100 : ($previousOrderNum == 0 ? null : -100));
$visitorCountCompareRate = $previousVisitorNum > 0 ? round((($visitorCount - $previousVisitorNum) / $previousVisitorNum) * 100, 2) : ($visitorCount > 0 ? 100 : ($previousVisitorNum == 0 ? null : -100));
$conversionRateCompareRate = $previousConversionRate > 0 ? round((($conversionRate - $previousConversionRate) / $previousConversionRate) * 100, 2) : ($conversionRate > 0 ? 100 : ($previousConversionRate == 0 ? null : -100));
$registerUsersCompareRate = $previousRegisterNum > 0 ? round((($registerUsers - $previousRegisterNum) / $previousRegisterNum) * 100, 2) : ($registerUsers > 0 ? 100 : ($previousRegisterNum == 0 ? null : -100));
return [ return [
'total_amount' => $totalAmount, 'total_amount' => $totalAmount,
'total_amount_compare_rate' => $totalAmountCompareRate,
'order_count' => $orderCount, 'order_count' => $orderCount,
'order_count_compare_rate' => $orderCountCompareRate,
'avg_amount' => $avgAmount, 'avg_amount' => $avgAmount,
'avg_amount_compare_rate' => $avgAmountCompareRate,
'profit_rate' => $profitRate, 'profit_rate' => $profitRate,
'profit_rate_compare_rate' => 0,
'visitor_count' => $visitorCount, 'visitor_count' => $visitorCount,
'visitor_count_compare_rate' => $visitorCountCompareRate,
'conversion_rate' => $conversionRate, 'conversion_rate' => $conversionRate,
'conversion_rate_compare_rate' => $conversionRateCompareRate,
'register_users' => $registerUsers, 'register_users' => $registerUsers,
'register_users_compare_rate' => $registerUsersCompareRate,
'trend_data' => $data 'trend_data' => $data
]; ];
} }
...@@ -630,4 +678,29 @@ class Dashboard extends Common ...@@ -630,4 +678,29 @@ class Dashboard extends Common
} }
} }
/**
* 从埋点表获取访客数
*
* 使用与定时任务相同的逻辑统计去重访客数
* 用户识别优先级:user_id > session_id > ip
*
* @param int $startTime 开始时间戳
* @param int $endTime 结束时间戳
* @return int 去重后的访客数量
*/
private function getVisitorCountFromTrack($startTime, $endTime)
{
$table = config('database.prefix') . 'shopro_miniprogram_track';
$sql = "SELECT COUNT(DISTINCT CASE
WHEN user_id > 0 THEN CONCAT('u_', user_id)
WHEN session_id <> '' THEN CONCAT('s_', session_id)
ELSE CONCAT('i_', ip)
END) AS total
FROM `{$table}`
WHERE create_time BETWEEN ? AND ?";
$result = Db::query($sql, [$startTime, $endTime]);
return isset($result[0]['total']) ? (int)$result[0]['total'] : 0;
}
} }
\ No newline at end of file
...@@ -367,6 +367,7 @@ ...@@ -367,6 +367,7 @@
<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%">
<div class="stats-center"> <div class="stats-center">
<div class="stats-center-header"> <div class="stats-center-header">
<div> <div>
...@@ -377,23 +378,25 @@ ...@@ -377,23 +378,25 @@
<div class="stats-center-time">实时业务数据监控 · 更新于 <span id="update-time">{:date('Y-m-d H:i:s')}</span></div> <div class="stats-center-time">实时业务数据监控 · 更新于 <span id="update-time">{:date('Y-m-d H:i:s')}</span></div>
</div> </div>
<div class="header-right"> <div class="header-right">
<div class="data-status"> <!-- <div class="data-status">
<span class="status-dot"></span> <span class="status-dot"></span>
<span>数据同步中</span> <span>数据同步中</span>
</div> </div> -->
<div class="time-select-wrapper"> <div class="time-select-wrapper">
<select id="time_range" class="time-select" onchange="loadStatistics(this.value)"> <select id="time_range" class="time-select">
<option value="7days">近7天</option> <option value="7days">近7天</option>
<option value="30days" selected>近30天</option> <option value="30days" selected>近30天</option>
<option value="90days">近90天</option> <option value="90days">近90天</option>
</select> </select>
</div> </div>
<button class="refresh-btn" id="refreshBtn" onclick="refreshAllData()"> <button class="refresh-btn" id="refreshBtn">
<i class="fa fa-refresh"></i> <i class="fa fa-refresh"></i>
<span>刷新数据</span> <span>刷新数据</span>
</button> </button>
</div> </div>
</div> </div>
</div>
<div class="stats-center">
<div class="stats-card-row"> <div class="stats-card-row">
<div class="stats-card card-1"> <div class="stats-card card-1">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -401,7 +404,7 @@ ...@@ -401,7 +404,7 @@
<span class="stats-card-icon"><i class="fa fa-money"></i></span> <span class="stats-card-icon"><i class="fa fa-money"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-total-amount">¥0.00</div> <div class="stats-card-value" id="stat-total-amount">¥0.00</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-total-trend">+0%</span> 较上</div> <div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-total-trend">+0%</span> 较上周期</div>
</div> </div>
<div class="stats-card card-2"> <div class="stats-card card-2">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -409,7 +412,7 @@ ...@@ -409,7 +412,7 @@
<span class="stats-card-icon"><i class="fa fa-percent"></i></span> <span class="stats-card-icon"><i class="fa fa-percent"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-profit-rate">0%</div> <div class="stats-card-value" id="stat-profit-rate">0%</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-profit-trend">+0%</span> 较上</div> <div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-profit-trend">+0%</span> 较上周期</div>
</div> </div>
<div class="stats-card card-3"> <div class="stats-card card-3">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -417,7 +420,7 @@ ...@@ -417,7 +420,7 @@
<span class="stats-card-icon"><i class="fa fa-shopping-cart"></i></span> <span class="stats-card-icon"><i class="fa fa-shopping-cart"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-avg-amount">¥0.00</div> <div class="stats-card-value" id="stat-avg-amount">¥0.00</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-avg-trend">+0%</span> 较上</div> <div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-avg-trend">+0%</span> 较上周期</div>
</div> </div>
<div class="stats-card card-4"> <div class="stats-card card-4">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -425,7 +428,7 @@ ...@@ -425,7 +428,7 @@
<span class="stats-card-icon"><i class="fa fa-file-text"></i></span> <span class="stats-card-icon"><i class="fa fa-file-text"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-order-count">0</div> <div class="stats-card-value" id="stat-order-count">0</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-order-trend">+0%</span> 较上</div> <div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-order-trend">+0%</span> 较上周期</div>
</div> </div>
<div class="stats-card card-5"> <div class="stats-card card-5">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -433,7 +436,7 @@ ...@@ -433,7 +436,7 @@
<span class="stats-card-icon"><i class="fa fa-users"></i></span> <span class="stats-card-icon"><i class="fa fa-users"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-visitor-count">0</div> <div class="stats-card-value" id="stat-visitor-count">0</div>
<div class="stats-card-trend" id="stat-visitor-trend"><i class="fa fa-arrow-down"></i> <span>-0%</span> 较上月</div> <div class="stats-card-trend up" id="stat-visitor-trend"><i class="fa fa-arrow-up"></i> <span>+0%</span> 较上周期</div>
</div> </div>
<div class="stats-card card-6"> <div class="stats-card card-6">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -441,15 +444,7 @@ ...@@ -441,15 +444,7 @@
<span class="stats-card-icon"><i class="fa fa-line-chart"></i></span> <span class="stats-card-icon"><i class="fa fa-line-chart"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-conversion-rate">0%</div> <div class="stats-card-value" id="stat-conversion-rate">0%</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-conversion-trend">+0%</span> 较上月</div> <div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-conversion-trend">+0%</span> 较上周期</div>
</div>
<div class="stats-card card-7">
<div class="stats-card-header">
<span class="stats-card-label">新增用户</span>
<span class="stats-card-icon"><i class="fa fa-user-plus"></i></span>
</div>
<div class="stats-card-value" id="stat-new-users">0</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-new-trend">+0(+0%)</span></div>
</div> </div>
<div class="stats-card card-8"> <div class="stats-card card-8">
<div class="stats-card-header"> <div class="stats-card-header">
...@@ -457,7 +452,7 @@ ...@@ -457,7 +452,7 @@
<span class="stats-card-icon"><i class="fa fa-users"></i></span> <span class="stats-card-icon"><i class="fa fa-users"></i></span>
</div> </div>
<div class="stats-card-value" id="stat-register-users">0</div> <div class="stats-card-value" id="stat-register-users">0</div>
<div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-register-trend">+0(+0%)</span></div> <div class="stats-card-trend up"><i class="fa fa-arrow-up"></i> <span id="stat-register-trend">+0%</span> 较上周期</div>
</div> </div>
</div> </div>
</div> </div>
...@@ -470,11 +465,11 @@ ...@@ -470,11 +465,11 @@
<div class="chart-panel-title">成交总额趋势</div> <div class="chart-panel-title">成交总额趋势</div>
<div class="chart-panel-subtitle" id="gmv-subtitle">近30天每日GMV走势</div> <div class="chart-panel-subtitle" id="gmv-subtitle">近30天每日GMV走势</div>
</div> </div>
<div class="chart-tabs"> <!-- <div class="chart-tabs">
<button class="chart-tab active" id="gmv-day" onclick="switchPeriod('gmv', 'day')"></button> <button class="chart-tab active" id="gmv-day" data-chart="gmv" data-period="day">日</button>
<button class="chart-tab" id="gmv-week" onclick="switchPeriod('gmv', 'week')"></button> <button class="chart-tab" id="gmv-week" data-chart="gmv" data-period="week">周</button>
<button class="chart-tab" id="gmv-month" onclick="switchPeriod('gmv', 'month')"></button> <button class="chart-tab" id="gmv-month" data-chart="gmv" data-period="month">月</button>
</div> </div> -->
</div> </div>
<div id="gmv-chart" class="chart-container"></div> <div id="gmv-chart" class="chart-container"></div>
</div> </div>
...@@ -486,11 +481,11 @@ ...@@ -486,11 +481,11 @@
<div class="chart-panel-title">订单数趋势</div> <div class="chart-panel-title">订单数趋势</div>
<div class="chart-panel-subtitle" id="order-subtitle">近30天订单量走势</div> <div class="chart-panel-subtitle" id="order-subtitle">近30天订单量走势</div>
</div> </div>
<div class="chart-tabs"> <!-- <div class="chart-tabs">
<button class="chart-tab active" id="order-day" onclick="switchPeriod('order', 'day')"></button> <button class="chart-tab active" id="order-day" data-chart="order" data-period="day">日</button>
<button class="chart-tab" id="order-week" onclick="switchPeriod('order', 'week')"></button> <button class="chart-tab" id="order-week" data-chart="order" data-period="week">周</button>
<button class="chart-tab" id="order-month" onclick="switchPeriod('order', 'month')"></button> <button class="chart-tab" id="order-month" data-chart="order" data-period="month">月</button>
</div> </div> -->
</div> </div>
<div id="order-chart" class="chart-container"></div> <div id="order-chart" class="chart-container"></div>
</div> </div>
...@@ -548,7 +543,7 @@ ...@@ -548,7 +543,7 @@
</div> </div>
<div> <div>
<span class="growth-badge" id="growth-badge">+0</span> <span class="growth-badge" id="growth-badge">+0</span>
<p class="growth-badge-text">累计注册用户</p> <p class="chart-panel-subtitle">累计注册用户</p>
</div> </div>
</div> </div>
<div id="user-growth-chart" class="chart-container"></div> <div id="user-growth-chart" class="chart-container"></div>
......
...@@ -195,10 +195,10 @@ class DataStatsCenter extends Command ...@@ -195,10 +195,10 @@ class DataStatsCenter extends Command
$currentRegisterTotal = $this->getRegisterCount(0, $currentRange['end']); $currentRegisterTotal = $this->getRegisterCount(0, $currentRange['end']);
$previousRegisterTotal = $this->getRegisterCount(0, $previousRange['end']); $previousRegisterTotal = $this->getRegisterCount(0, $previousRange['end']);
// 计算转化率(订单数/访客数) // 计算转化率(购买用户数/访客数)
$this->customLog('info', '开始计算当前&上一期统计周期转化率'); $this->customLog('info', '开始计算当前&上一期统计周期转化率');
$currentConversion = $currentVisitors > 0 ? round($currentRevenue['order_count'] / $currentVisitors * 100, 2) : 0; $currentConversion = $currentVisitors > 0 ? round($currentRevenue['buyer_count'] / $currentVisitors * 100, 2) : 0;
$previousConversion = $previousVisitors > 0 ? round($previousRevenue['order_count'] / $previousVisitors * 100, 2) : 0; $previousConversion = $previousVisitors > 0 ? round($previousRevenue['buyer_count'] / $previousVisitors * 100, 2) : 0;
// 构建趋势数据 // 构建趋势数据
$this->customLog('info', '开始构建当前统计周期趋势数据'); $this->customLog('info', '开始构建当前统计周期趋势数据');
...@@ -332,6 +332,13 @@ class DataStatsCenter extends Command ...@@ -332,6 +332,13 @@ class DataStatsCenter extends Command
->where('paid_time', 'between', [$startTime, $endTime]) ->where('paid_time', 'between', [$startTime, $endTime])
->count(); ->count();
// 统计购买用户数(去重)
$buyerCount = (int)Db::name('shopro_order')
->whereIn('status', ['paid', 'completed'])
->where('paid_time', 'between', [$startTime, $endTime])
->where('user_id', '>', 0)
->count('DISTINCT user_id');
// 计算客单价(成交额/订单数) // 计算客单价(成交额/订单数)
$avgAmount = $orderCount > 0 ? round($totalAmount / $orderCount, 2) : 0; $avgAmount = $orderCount > 0 ? round($totalAmount / $orderCount, 2) : 0;
...@@ -355,6 +362,7 @@ class DataStatsCenter extends Command ...@@ -355,6 +362,7 @@ class DataStatsCenter extends Command
return [ return [
'total_amount' => $totalAmount, 'total_amount' => $totalAmount,
'order_count' => $orderCount, 'order_count' => $orderCount,
'buyer_count' => $buyerCount,
'avg_amount' => $avgAmount, 'avg_amount' => $avgAmount,
'cost_amount' => $costAmount, 'cost_amount' => $costAmount,
'refund_amount' => $refundAmount, 'refund_amount' => $refundAmount,
......
...@@ -50,6 +50,9 @@ if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.a ...@@ -50,6 +50,9 @@ if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.a
'ElementPlus': { 'ElementPlus': {
deps: ['css!../addons/shopro/libs/element-plus/index.css'] deps: ['css!../addons/shopro/libs/element-plus/index.css']
}, },
'ElementPlusIconsVue': {
deps: ['ElementPlusIconsVue3']
}
}, },
}); });
require(['vue3', 'ElementPlusIconsVue3'], function (Vue3, ElementPlusIconsVue3) { require(['vue3', 'ElementPlusIconsVue3'], function (Vue3, ElementPlusIconsVue3) {
...@@ -75,9 +78,12 @@ if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.a ...@@ -75,9 +78,12 @@ if (Config.modulename == 'admin' && Config.controllername == 'index' && Config.a
const app = createApp({}) const app = createApp({})
app.use(ElementPlus) app.use(ElementPlus)
if (ElementPlusIconsVue && typeof ElementPlusIconsVue === 'object') {
for (const [key, component] of Object.entries(ElementPlusIconsVue)) { for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component) app.component(key, component)
} }
}
app.component('sa-chat', SaChat) app.component('sa-chat', SaChat)
app.mount(`#SaChatWrap`) app.mount(`#SaChatWrap`)
......
...@@ -4,54 +4,32 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -4,54 +4,32 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
index: () => { index: () => {
const { reactive, onMounted } = Vue; const { reactive, onMounted } = Vue;
// ECharts图表实例对象
// gmvChart: 成交总额趋势图表
// orderChart: 订单数趋势图表
// userGrowthChart: 用户增长图表(活跃用户/新增用户)
var gmvChart, orderChart, userGrowthChart; var gmvChart, orderChart, userGrowthChart;
// 当前时间范围筛选条件,默认近30天
var currentTimeRange = '30days'; var currentTimeRange = '30days';
// 成交总额图表的周期(day/week/month)
var currentGmvPeriod = 'day'; var currentGmvPeriod = 'day';
// 订单数图表的周期(day/week/month)
var currentOrderPeriod = 'day'; var currentOrderPeriod = 'day';
const index = { const index = {
setup() { setup() {
const state = reactive({ // 图表配置响应式对象
total: { // 用于底部统计图表的Tab切换(订单数/支付订单/支付金额)
user: {
data: {},
color: '#806AF6',
color1: 'rgba(128, 106, 246, 0.4)',
color2: 'rgba(128, 106, 246, 0)',
title: '用户数量',
tip: '今日新增用户',
footer: '本周新增用户人数',
},
agent: {
data: {},
color: '#409EFF',
color1: 'rgba(64, 158, 255, 0.4)',
color2: 'rgba(64, 158, 255, 0)',
title: '分销商数量',
tip: '今日新增人数',
footer: '本周新增分销商人数',
},
share: {
data: {},
color: '#21C732',
color1: 'rgba(33, 199, 50, 0.4)',
color2: 'rgba(33, 199, 50, 0)',
title: '分享次数',
tip: '今日分享次数',
footer: '本周新增分享次数',
},
},
});
const chart = reactive({ const chart = reactive({
tabsData: { tabsData: {
order: '订单数', order: '订单数',
payOrder: '支付订单', payOrder: '支付订单',
payAmount: '支付金额', payAmount: '支付金额',
}, },
tabActive: 'order', tabActive: 'order', // 当前激活的Tab
dateTime: getTimeSlot(), dateTime: getTimeSlot(), // 时间选择器的时间范围
shortcuts: [ shortcuts: [ // 时间快捷选项
{ {
text: '今天', text: '今天',
value: getTimeSlot(), value: getTimeSlot(),
...@@ -70,31 +48,34 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -70,31 +48,34 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}, },
], ],
data: { data: {
payAmountArr: [], payAmountArr: [], // 支付金额数据数组
payOrderArr: [], payOrderArr: [], // 支付订单数据数组
orderArr: [], orderArr: [], // 订单数数据数组
}, },
}); });
// 统计数据响应式对象
// 存储仪表盘顶部统计卡片的数据
const statistics = reactive({ const statistics = reactive({
total_amount: 0, total_amount: 0, // 成交总额
total_amount_compare_rate: 0, total_amount_compare_rate: 0, // 成交总额环比增长率
profit_rate: '0%', profit_rate: '0%', // 净利润率
profit_rate_compare_rate: 0, profit_rate_compare_rate: 0, // 净利润率环比增长率
avg_amount: 0, avg_amount: 0, // 客单价
avg_amount_compare_rate: 0, avg_amount_compare_rate: 0, // 客单价环比增长率
order_count: 0, order_count: 0, // 订单数
order_count_compare_rate: 0, order_count_compare_rate: 0, // 订单数环比增长率
visitor_count: 0, visitor_count: 0, // 访客数
visitor_count_compare_rate: 0, visitor_count_compare_rate: 0, // 访客数环比增长率
conversion_rate: '0%', conversion_rate: '0%', // 转化率
conversion_rate_compare_rate: 0, conversion_rate_compare_rate: 0, // 转化率环比增长率
register_users: 0, register_users: 0, // 注册用户数
register_users_compare_rate: 0, register_users_compare_rate: 0, // 注册用户环比增长率
trend_data: [], trend_data: [], // 趋势数据数组
from_cache: false from_cache: false // 数据是否来自缓存
}); });
// 底部统计图表的配置选项
const chartOption = reactive({ const chartOption = reactive({
grid: { grid: {
left: '10px', left: '10px',
...@@ -161,6 +142,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -161,6 +142,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
], ],
}); });
/**
* 生成日期标签数组
* @param {number} days - 需要生成的天数
* @returns {Array} - 日期标签数组,格式如 ['1月1日', '1月2日', ...]
*/
function generateDateLabels(days) { function generateDateLabels(days) {
var labels = []; var labels = [];
for (var i = days - 1; i >= 0; i--) { for (var i = days - 1; i >= 0; i--) {
...@@ -171,6 +157,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -171,6 +157,12 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
return labels; return labels;
} }
/**
* 格式化数字显示
* @param {number} num - 需要格式化的数字
* @param {number} decimals - 小数位数(可选)
* @returns {string} - 格式化后的数字字符串
*/
function formatNumber(num, decimals) { function formatNumber(num, decimals) {
if (num === null || num === undefined) num = 0; if (num === null || num === undefined) num = 0;
if (decimals !== undefined) { if (decimals !== undefined) {
...@@ -179,39 +171,60 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -179,39 +171,60 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
return parseInt(num).toLocaleString(); return parseInt(num).toLocaleString();
} }
/**
* 更新趋势元素显示
* @param {HTMLElement} element - 趋势元素DOM节点
* @param {number|null} compareRate - 环比增长率,null表示持平
*/
function updateTrendElement(element, compareRate) { function updateTrendElement(element, compareRate) {
if (!element) return; if (!element) return;
var spanElement = element.querySelector('span');
if (!spanElement) return;
if (compareRate === null || compareRate === undefined) {
spanElement.textContent = '持平';
element.classList.remove('up', 'down');
var icon = element.querySelector('i');
if (icon) {
icon.classList.remove('fa-arrow-up', 'fa-arrow-down');
}
return;
}
compareRate = parseFloat(compareRate) || 0; compareRate = parseFloat(compareRate) || 0;
var trendText = ''; var trendText = '';
var trendClass = ''; var trendClass = '';
if (compareRate > 0) { var roundedRate = Math.round(compareRate);
trendText = '+' + compareRate.toFixed(2) + '%';
if (roundedRate > 0) {
trendText = '+' + roundedRate + '%';
trendClass = 'up'; trendClass = 'up';
} else if (compareRate < 0) { } else if (roundedRate < 0) {
trendText = compareRate.toFixed(2) + '%'; trendText = roundedRate + '%';
trendClass = 'down'; trendClass = 'down';
} else { } else {
trendText = '+0%'; trendText = '+0%';
trendClass = 'up'; trendClass = 'up';
} }
element.textContent = trendText; spanElement.textContent = trendText;
var parentDiv = element.parentElement; element.classList.remove('up', 'down');
if (parentDiv) { element.classList.add(trendClass);
parentDiv.classList.remove('up', 'down');
parentDiv.classList.add(trendClass);
var icon = parentDiv.querySelector('i'); var icon = element.querySelector('i');
if (icon) { if (icon) {
icon.classList.remove('fa-arrow-up', 'fa-arrow-down'); icon.classList.remove('fa-arrow-up', 'fa-arrow-down');
icon.classList.add(trendClass === 'up' ? 'fa-arrow-up' : 'fa-arrow-down'); icon.classList.add(trendClass === 'up' ? 'fa-arrow-up' : 'fa-arrow-down');
} }
} }
}
/**
* 初始化ECharts图表实例
* 创建成交总额趋势、订单数趋势和用户增长图表
*/
function initCharts() { function initCharts() {
gmvChart = echarts.init(document.getElementById('gmv-chart')); gmvChart = echarts.init(document.getElementById('gmv-chart'));
var gmvOption = { var gmvOption = {
...@@ -336,6 +349,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -336,6 +349,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}; };
userGrowthChart.setOption(userGrowthOption); userGrowthChart.setOption(userGrowthOption);
// 监听窗口大小变化,自动调整图表尺寸
window.addEventListener('resize', function() { window.addEventListener('resize', function() {
gmvChart.resize(); gmvChart.resize();
orderChart.resize(); orderChart.resize();
...@@ -343,6 +357,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -343,6 +357,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}); });
} }
/**
* 切换图表周期(日/周/月)
* @param {string} chartType - 图表类型:'gmv' 或 'order'
* @param {string} period - 周期类型:'day'、'week' 或 'month'
*/
function switchPeriod(chartType, period) { function switchPeriod(chartType, period) {
if (chartType === 'gmv') { if (chartType === 'gmv') {
currentGmvPeriod = period; currentGmvPeriod = period;
...@@ -381,6 +400,10 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -381,6 +400,10 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
loadStatistics(currentTimeRange); loadStatistics(currentTimeRange);
} }
/**
* 加载统计数据
* @param {string} timeRange - 时间范围,如 '7days'、'30days'、'90days'
*/
function loadStatistics(timeRange) { function loadStatistics(timeRange) {
currentTimeRange = timeRange; currentTimeRange = timeRange;
Fast.api.ajax({ Fast.api.ajax({
...@@ -410,6 +433,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -410,6 +433,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
statistics.trend_data = data.trend_data; statistics.trend_data = data.trend_data;
statistics.from_cache = data.from_cache; statistics.from_cache = data.from_cache;
// 更新页面显示的更新时间
var updateTimeElement = document.getElementById('update-time');
if (updateTimeElement && data.update_time) {
updateTimeElement.textContent = data.update_time;
console.log(data.update_time);
}
// 更新统计卡片显示
var statTotalAmount = document.getElementById('stat-total-amount'); var statTotalAmount = document.getElementById('stat-total-amount');
var statProfitRate = document.getElementById('stat-profit-rate'); var statProfitRate = document.getElementById('stat-profit-rate');
var statAvgAmount = document.getElementById('stat-avg-amount'); var statAvgAmount = document.getElementById('stat-avg-amount');
...@@ -442,9 +473,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -442,9 +473,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
updateGmvChart(data.trend_data); updateGmvChart(data.trend_data);
// 如果GMV和订单图表周期相同,复用同一数据
if (currentGmvPeriod === currentOrderPeriod) { if (currentGmvPeriod === currentOrderPeriod) {
updateOrderChart(data.trend_data); updateOrderChart(data.trend_data);
} else { } else {
// 周期不同时单独请求订单数据
Fast.api.ajax({ Fast.api.ajax({
url: 'shopro/dashboard/statistics', url: 'shopro/dashboard/statistics',
type: 'GET', type: 'GET',
...@@ -463,11 +496,16 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -463,11 +496,16 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
return false; return false;
}); });
// 同时加载其他数据
loadFunnel(timeRange); loadFunnel(timeRange);
loadPriceDistribution(timeRange); loadPriceDistribution(timeRange);
loadRecentOrders(); loadRecentOrders();
} }
/**
* 更新成交总额趋势图表和用户增长图表
* @param {Array} trendData - 趋势数据数组
*/
function updateGmvChart(trendData) { function updateGmvChart(trendData) {
var labels = trendData.map(function(item) { return item.date; }); var labels = trendData.map(function(item) { return item.date; });
var amounts = trendData.map(function(item) { return item.amount; }); var amounts = trendData.map(function(item) { return item.amount; });
...@@ -479,6 +517,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -479,6 +517,7 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
xAxis: { data: labels }, xAxis: { data: labels },
series: [{ data: amounts }] series: [{ data: amounts }]
}); });
gmvChart.resize();
} }
if (userGrowthChart) { if (userGrowthChart) {
...@@ -489,13 +528,19 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -489,13 +528,19 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
{ data: registers } { data: registers }
] ]
}); });
userGrowthChart.resize();
} }
// 更新用户增长徽章
var totalRegister = registers.reduce(function(a, b) { return a + b; }, 0); var totalRegister = registers.reduce(function(a, b) { return a + b; }, 0);
var growthBadge = document.getElementById('growth-badge'); var growthBadge = document.getElementById('growth-badge');
if (growthBadge) growthBadge.textContent = '+' + formatNumber(totalRegister); if (growthBadge) growthBadge.textContent = '+' + formatNumber(totalRegister);
} }
/**
* 更新订单数趋势图表
* @param {Array} trendData - 趋势数据数组
*/
function updateOrderChart(trendData) { function updateOrderChart(trendData) {
var labels = trendData.map(function(item) { return item.date; }); var labels = trendData.map(function(item) { return item.date; });
var orders = trendData.map(function(item) { return item.order_num; }); var orders = trendData.map(function(item) { return item.order_num; });
...@@ -505,9 +550,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -505,9 +550,14 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
xAxis: { data: labels }, xAxis: { data: labels },
series: [{ data: orders }] series: [{ data: orders }]
}); });
orderChart.resize();
} }
} }
/**
* 加载转化漏斗数据
* @param {string} timeRange - 时间范围
*/
function loadFunnel(timeRange) { function loadFunnel(timeRange) {
Fast.api.ajax({ Fast.api.ajax({
url: 'shopro/dashboard/funnel', url: 'shopro/dashboard/funnel',
...@@ -553,6 +603,10 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -553,6 +603,10 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}); });
} }
/**
* 加载价格分布数据
* @param {string} timeRange - 时间范围
*/
function loadPriceDistribution(timeRange) { function loadPriceDistribution(timeRange) {
Fast.api.ajax({ Fast.api.ajax({
url: 'shopro/dashboard/priceDistribution', url: 'shopro/dashboard/priceDistribution',
...@@ -590,6 +644,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -590,6 +644,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}); });
} }
/**
* 加载最近订单列表
*/
function loadRecentOrders() { function loadRecentOrders() {
Fast.api.ajax({ Fast.api.ajax({
url: 'shopro/dashboard/recentOrders', url: 'shopro/dashboard/recentOrders',
...@@ -615,6 +672,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -615,6 +672,9 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
}); });
} }
/**
* 刷新所有数据
*/
function refreshAllData() { function refreshAllData() {
var refreshBtn = document.getElementById('refreshBtn'); var refreshBtn = document.getElementById('refreshBtn');
var updateTime = document.getElementById('update-time'); var updateTime = document.getElementById('update-time');
...@@ -634,134 +694,134 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -634,134 +694,134 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
} }
function getTotal() { function getTotal() {
Fast.api.ajax({ // Fast.api.ajax({
url: 'shopro/dashboard/total', // url: 'shopro/dashboard/total',
type: 'GET', // type: 'GET',
}, function (ret, res) { // }, function (ret, res) {
state.total.agent.data = res.data.agent_data; // state.total.agent.data = res.data.agent_data;
state.total.share.data = res.data.share_data; // state.total.share.data = res.data.share_data;
state.total.user.data = res.data.user_data; // state.total.user.data = res.data.user_data;
for (var key in state.total) { // for (var key in state.total) {
if (state.total[key].data) { // if (state.total[key].data) {
initChartTotal(key); // initChartTotal(key);
} // }
} // }
return false; // return false;
}, function (ret, res) { }); // }, function (ret, res) { });
} }
function initChartTotal(key) { function initChartTotal(key) {
var chartEl = document.getElementById(key + 'Total'); // var chartEl = document.getElementById(key + 'Total');
if (!chartEl) return; // if (!chartEl) return;
var myChart = echarts.init(chartEl); // var myChart = echarts.init(chartEl);
window.onresize = () => { // window.onresize = () => {
myChart.resize(); // myChart.resize();
}; // };
var option = { // var option = {
grid: { // grid: {
left: 6, // left: 6,
top: 6, // top: 6,
right: 6, // right: 6,
bottom: 6, // bottom: 6,
}, // },
tooltip: { // tooltip: {
trigger: 'axis', // trigger: 'axis',
axisPointer: { // axisPointer: {
type: 'none', // type: 'none',
}, // },
}, // },
xAxis: { // xAxis: {
type: 'category', // type: 'category',
data: ['分', 20, 36, 10, 10, 20], // data: ['分', 20, 36, 10, 10, 20],
boundaryGap: false, // boundaryGap: false,
axisTick: { // axisTick: {
show: false, // show: false,
}, // },
axisLabel: { // axisLabel: {
show: false, // show: false,
}, // },
axisLine: { // axisLine: {
show: false, // show: false,
}, // },
}, // },
yAxis: { // yAxis: {
type: 'value', // type: 'value',
splitLine: { // splitLine: {
show: false, // show: false,
}, // },
axisLabel: { // axisLabel: {
show: false, // show: false,
}, // },
}, // },
series: [{ // series: [{
data: [5, 20, 36, 10, 10, 20], // data: [5, 20, 36, 10, 10, 20],
type: 'line', // type: 'line',
smooth: true, // smooth: true,
showSymbol: false, // showSymbol: false,
symbol: 'circle', // symbol: 'circle',
symbolSize: 6, // symbolSize: 6,
itemStyle: { // itemStyle: {
color: state.total[key].color, // color: state.total[key].color,
}, // },
areaStyle: { // areaStyle: {
color: new echarts.graphic.LinearGradient( // color: new echarts.graphic.LinearGradient(
0, // 0,
0, // 0,
0, // 0,
1, // 1,
[ // [
{ // {
offset: 0, // offset: 0,
color: state.total[key].color1, // color: state.total[key].color1,
}, // },
{ // {
offset: 1, // offset: 1,
color: state.total[key].color2, // color: state.total[key].color2,
}, // },
], // ],
false, // false,
), // ),
}, // },
lineStyle: { // lineStyle: {
width: 2, // width: 2,
}, // },
}] // }]
}; // };
initIntervalTotal(24, 'hours', option, key); // initIntervalTotal(24, 'hours', option, key);
myChart.setOption(option); // myChart.setOption(option);
} }
function initIntervalTotal(interval, kld, option, key) { function initIntervalTotal(interval, kld, option, key) {
let dateTime = Moment().format('YYYY-MM-DD') + ' 00:00:00'; // let dateTime = Moment().format('YYYY-MM-DD') + ' 00:00:00';
let x = []; // let x = [];
let y = []; // let y = [];
let timeStamp = []; // let timeStamp = [];
for (let i = 0; i <= interval; i++) { // for (let i = 0; i <= interval; i++) {
x.push(Moment(dateTime).add(i, kld).format('HH:mm')); // x.push(Moment(dateTime).add(i, kld).format('HH:mm'));
y.push(0); // y.push(0);
timeStamp.push(Moment(dateTime).add(i, kld).valueOf()); // timeStamp.push(Moment(dateTime).add(i, kld).valueOf());
} // }
x.forEach((item, index) => { // x.forEach((item, index) => {
state.total[key].data.array.forEach((item) => { // state.total[key].data.array.forEach((item) => {
if ( // if (
timeStamp[index + 1] && // timeStamp[index + 1] &&
item.createtime_unix > timeStamp[index] && // item.createtime_unix > timeStamp[index] &&
item.createtime_unix <= timeStamp[index + 1] // item.createtime_unix <= timeStamp[index + 1]
) { // ) {
y[index]++; // y[index]++;
} // }
}); // });
}); // });
option.xAxis.data = x; // option.xAxis.data = x;
option.series[0].data = y; // option.series[0].data = y;
} }
function onChangeTabActive(type) { // function onChangeTabActive(type) {
chart.tabActive = type; // chart.tabActive = type;
chartOption.series[0].name = chart.tabsData[chart.tabActive]; // chartOption.series[0].name = chart.tabsData[chart.tabActive];
initChart(); // initChart();
} // }
function onChangeDateTime(e) { function onChangeDateTime(e) {
e && getChart(); e && getChart();
...@@ -887,100 +947,94 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function ( ...@@ -887,100 +947,94 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form', 'moment'], function (
return timeSlot; return timeSlot;
} }
const ranking = reactive({ // const ranking = reactive({
goods: [], // goods: [],
hot_search: [] // hot_search: []
});
const pieOption = reactive({
tooltip: {
trigger: 'item',
formatter: '{a} <br/>{b}: {c} ({d}%)',
},
legend: {
show: false,
},
series: [
{
name: '热搜榜',
type: 'pie',
radius: ['52%', '90%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center',
},
zlevel: 1,
z: 1,
emphasis: {
label: {
show: true,
fontSize: '16',
fontWeight: 'normal',
},
},
labelLine: {
show: false,
},
data: [],
},
],
});
// function getRanking() {
// Fast.api.ajax({
// url: 'shopro/dashboard/ranking',
// type: 'GET',
// }, function (ret, res) {
// ranking.goods = res.data.goods;
// ranking.hot_search = res.data.hot_search;
// pieOption.series[0].data = [];
// ranking.hot_search.forEach(item => {
// pieOption.series[0].data.push({
// name: item.keyword,
// value: item.num,
// }); // });
// const pieOption = reactive({
// tooltip: {
// trigger: 'item',
// formatter: '{a} <br/>{b}: {c} ({d}%)',
// },
// legend: {
// show: false,
// },
// series: [
// {
// name: '热搜榜',
// type: 'pie',
// radius: ['52%', '90%'],
// avoidLabelOverlap: false,
// label: {
// show: false,
// position: 'center',
// },
// zlevel: 1,
// z: 1,
// emphasis: {
// label: {
// show: true,
// fontSize: '16',
// fontWeight: 'normal',
// },
// },
// labelLine: {
// show: false,
// },
// data: [],
// },
// ],
// }); // });
// var rankingContentEl = document.getElementById('rankingContent'); // function onOpen(status) {
// if (rankingContentEl) { // Fast.api.open('shopro/order/order/index?status=' + status + '&createtime=' + encodeURI(JSON.stringify(chart.dateTime)), "订单", {
// var myChart3 = echarts.init(rankingContentEl); // callback() {
// window.onresize = () => { // getChart();
// myChart3.resize();
// };
// myChart3.setOption(pieOption);
// } // }
// return false; // });
// }, function (ret, res) { });
// } // }
function onOpen(status) {
Fast.api.open('shopro/order/order/index?status=' + status + '&createtime=' + encodeURI(JSON.stringify(chart.dateTime)), "订单", {
callback() {
getChart();
}
});
}
onMounted(() => { onMounted(() => {
initCharts(); initCharts();
setTimeout(function() {
if (gmvChart) gmvChart.resize();
if (orderChart) orderChart.resize();
if (userGrowthChart) userGrowthChart.resize();
}, 100);
loadStatistics('30days'); loadStatistics('30days');
var timeRangeSelect = document.getElementById('time_range');
if (timeRangeSelect) {
timeRangeSelect.addEventListener('change', function(e) {
loadStatistics(e.target.value);
});
}
document.querySelectorAll('.chart-tab').forEach(btn => {
btn.addEventListener('click', function(e) {
var chartType = this.dataset.chart;
var period = this.dataset.period;
switchPeriod(chartType, period);
});
});
var refreshBtn = document.getElementById('refreshBtn');
if (refreshBtn) {
refreshBtn.addEventListener('click', refreshAllData);
}
}); });
return { return {
state,
getTotal, getTotal,
chart,
onChangeTabActive,
onChangeDateTime, onChangeDateTime,
statistics, statistics,
getChart,
chartOption, chartOption,
initChart, initChart,
drawX, drawX,
getTimeSlot, getTimeSlot,
ranking,
onOpen,
initCharts, initCharts,
loadStatistics, loadStatistics,
updateGmvChart, updateGmvChart,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment