|
@@ -58,10 +58,12 @@ function getCustomerCountryDistribution($conn, $employee_filter = null) {
|
|
|
* @param string $start_date 开始日期
|
|
|
* @param string $end_date 结束日期
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return mysqli_result 地区订单数据结果集
|
|
|
*/
|
|
|
-function getOrdersByRegion($conn, $start_date, $end_date, $employee_filter = null) {
|
|
|
+function getOrdersByRegion($conn, $start_date, $end_date, $employee_filter = null, $country_filter = 0) {
|
|
|
$employee_condition = "";
|
|
|
+ $country_condition = "";
|
|
|
$params = [$start_date, $end_date];
|
|
|
$types = "ss";
|
|
|
|
|
@@ -86,32 +88,41 @@ function getOrdersByRegion($conn, $start_date, $end_date, $employee_filter = nul
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- $sql = "SELECT
|
|
|
- c.countryName,
|
|
|
- COUNT(DISTINCT o.id) as order_count,
|
|
|
- SUM(o.total_amount) as total_amount,
|
|
|
- (SELECT SUM(oi_sub.quantity)
|
|
|
- FROM order_items oi_sub
|
|
|
- JOIN orders o_sub ON oi_sub.order_id = o_sub.id
|
|
|
- JOIN customer cu_sub ON o_sub.customer_id = cu_sub.id
|
|
|
- WHERE o_sub.order_date BETWEEN ? AND ?
|
|
|
- AND cu_sub.cs_country = cu.cs_country
|
|
|
- $employee_condition) as total_quantity
|
|
|
- FROM orders o
|
|
|
- JOIN customer cu ON o.customer_id = cu.id
|
|
|
- JOIN country c ON cu.cs_country = c.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
- $employee_condition
|
|
|
- GROUP BY cu.cs_country
|
|
|
- ORDER BY total_quantity DESC
|
|
|
- LIMIT 10";
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_filter = intval($country_filter);
|
|
|
+ $country_condition = " AND cu.cs_country = $country_filter";
|
|
|
+ }
|
|
|
|
|
|
- // 添加额外的参数用于子查询
|
|
|
- $params = array_merge([$start_date, $end_date], $params);
|
|
|
- $types = "ss" . $types;
|
|
|
+ // 如果选择了特定国家/地区,则SQL语句不需要分组和排序,直接返回该国家/地区的数据
|
|
|
+ if ($country_filter > 0) {
|
|
|
+ $sql = "SELECT
|
|
|
+ c.countryName,
|
|
|
+ COUNT(DISTINCT o.id) as order_count,
|
|
|
+ SUM(o.total_amount) as total_amount
|
|
|
+ FROM orders o
|
|
|
+ JOIN customer cu ON o.customer_id = cu.id
|
|
|
+ JOIN country c ON cu.cs_country = c.id
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
+ $employee_condition
|
|
|
+ $country_condition
|
|
|
+ GROUP BY cu.cs_country";
|
|
|
+ } else {
|
|
|
+ // 否则返回前10个地区的数据
|
|
|
+ $sql = "SELECT
|
|
|
+ c.countryName,
|
|
|
+ COUNT(DISTINCT o.id) as order_count,
|
|
|
+ SUM(o.total_amount) as total_amount
|
|
|
+ FROM orders o
|
|
|
+ JOIN customer cu ON o.customer_id = cu.id
|
|
|
+ JOIN country c ON cu.cs_country = c.id
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
+ $employee_condition
|
|
|
+ GROUP BY cu.cs_country
|
|
|
+ ORDER BY total_amount DESC
|
|
|
+ LIMIT 10";
|
|
|
+ }
|
|
|
|
|
|
-
|
|
|
-
|
|
|
$stmt = $conn->prepare($sql);
|
|
|
$stmt->bind_param($types, ...$params);
|
|
|
$stmt->execute();
|
|
@@ -125,9 +136,10 @@ function getOrdersByRegion($conn, $start_date, $end_date, $employee_filter = nul
|
|
|
* @param string $current_start 当前周期开始日期
|
|
|
* @param string $current_end 当前周期结束日期
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return array 地区销售同比环比数据
|
|
|
*/
|
|
|
-function getRegionSalesComparison($conn, $current_start, $current_end, $employee_filter = null) {
|
|
|
+function getRegionSalesComparison($conn, $current_start, $current_end, $employee_filter = null, $country_filter = 0) {
|
|
|
// 计算上一个相同时长的周期
|
|
|
$current_start_date = new DateTime($current_start);
|
|
|
$current_end_date = new DateTime($current_end);
|
|
@@ -164,6 +176,16 @@ function getRegionSalesComparison($conn, $current_start, $current_end, $employee
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ $country_condition = "";
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_filter = intval($country_filter);
|
|
|
+ $country_condition = " AND cu.cs_country = $country_filter";
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果选择了特定国家/地区,就不需要LIMIT了
|
|
|
+ $limit_clause = ($country_filter > 0) ? "" : "LIMIT 5";
|
|
|
+
|
|
|
// 获取当前周期数据
|
|
|
$sql = "SELECT
|
|
|
c.countryName,
|
|
@@ -172,11 +194,12 @@ function getRegionSalesComparison($conn, $current_start, $current_end, $employee
|
|
|
FROM orders o
|
|
|
JOIN customer cu ON o.customer_id = cu.id
|
|
|
JOIN country c ON cu.cs_country = c.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
$employee_condition
|
|
|
+ $country_condition
|
|
|
GROUP BY cu.cs_country
|
|
|
ORDER BY total_amount DESC
|
|
|
- LIMIT 5";
|
|
|
+ $limit_clause";
|
|
|
|
|
|
$stmt = $conn->prepare($sql);
|
|
|
$stmt->bind_param("ss", $current_start, $current_end);
|
|
@@ -241,13 +264,27 @@ function getRegionSalesComparison($conn, $current_start, $current_end, $employee
|
|
|
*/
|
|
|
function renderCustomerCountryDistributionChart($country_labels, $country_data) {
|
|
|
?>
|
|
|
- <div class="chart-container">
|
|
|
+ <div class="chart-container country-distribution-chart">
|
|
|
<div class="chart-header">
|
|
|
<h2 class="chart-title">客户国家分布</h2>
|
|
|
</div>
|
|
|
<canvas id="countryDistributionChart"></canvas>
|
|
|
</div>
|
|
|
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:手机端全宽,PC端1/3宽度 */
|
|
|
+ .country-distribution-chart {
|
|
|
+ width: 100%;
|
|
|
+ margin: 0 auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .country-distribution-chart {
|
|
|
+ max-width: 33.333%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+
|
|
|
<script>
|
|
|
// 客户国家分布图
|
|
|
var countryDistributionCtx = document.getElementById('countryDistributionChart').getContext('2d');
|
|
@@ -290,18 +327,31 @@ function renderCustomerCountryDistributionChart($country_labels, $country_data)
|
|
|
*
|
|
|
* @param array $region_labels 地区标签
|
|
|
* @param array $region_orders 地区订单数量
|
|
|
- * @param array $region_quantities 地区产品数量
|
|
|
* @return void
|
|
|
*/
|
|
|
-function renderRegionOrdersChart($region_labels, $region_orders, $region_quantities) {
|
|
|
+function renderRegionOrdersChart($region_labels, $region_orders) {
|
|
|
?>
|
|
|
- <div class="chart-container">
|
|
|
- <div class="chart-header">
|
|
|
- <h2 class="chart-title">地区订单分析</h2>
|
|
|
- </div>
|
|
|
+ <div class="chart-header">
|
|
|
+ <h2 class="chart-title">地区订单分析</h2>
|
|
|
+ </div>
|
|
|
+ <div class="region-orders-chart-container">
|
|
|
<canvas id="regionOrdersChart"></canvas>
|
|
|
</div>
|
|
|
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:调整图表高度 */
|
|
|
+ .region-orders-chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .region-orders-chart-container {
|
|
|
+ height: 33vh; /* 视窗高度的1/3 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
+
|
|
|
<script>
|
|
|
// 地区订单分析图
|
|
|
var regionOrdersCtx = document.getElementById('regionOrdersChart').getContext('2d');
|
|
@@ -315,21 +365,13 @@ function renderRegionOrdersChart($region_labels, $region_orders, $region_quantit
|
|
|
data: <?php echo json_encode($region_orders); ?>,
|
|
|
backgroundColor: 'rgba(54, 162, 235, 0.6)',
|
|
|
borderColor: 'rgba(54, 162, 235, 1)',
|
|
|
- borderWidth: 1,
|
|
|
- yAxisID: 'y-orders'
|
|
|
- },
|
|
|
- {
|
|
|
- label: '产品订购数量',
|
|
|
- data: <?php echo json_encode($region_quantities); ?>,
|
|
|
- backgroundColor: 'rgba(255, 99, 132, 0.6)',
|
|
|
- borderColor: 'rgba(255, 99, 132, 1)',
|
|
|
- borderWidth: 1,
|
|
|
- yAxisID: 'y-quantity'
|
|
|
+ borderWidth: 1
|
|
|
}
|
|
|
]
|
|
|
},
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
+ maintainAspectRatio: false, // 允许图表高度自适应
|
|
|
scales: {
|
|
|
x: {
|
|
|
title: {
|
|
@@ -337,26 +379,12 @@ function renderRegionOrdersChart($region_labels, $region_orders, $region_quantit
|
|
|
text: '地区'
|
|
|
}
|
|
|
},
|
|
|
- 'y-orders': {
|
|
|
- type: 'linear',
|
|
|
- position: 'left',
|
|
|
+ y: {
|
|
|
title: {
|
|
|
display: true,
|
|
|
text: '订单数量'
|
|
|
},
|
|
|
beginAtZero: true
|
|
|
- },
|
|
|
- 'y-quantity': {
|
|
|
- type: 'linear',
|
|
|
- position: 'right',
|
|
|
- title: {
|
|
|
- display: true,
|
|
|
- text: '产品订购数量'
|
|
|
- },
|
|
|
- beginAtZero: true,
|
|
|
- grid: {
|
|
|
- drawOnChartArea: false
|
|
|
- }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -377,50 +405,68 @@ function renderRegionSalesComparisonTable($comparison_data) {
|
|
|
<div class="chart-header">
|
|
|
<h2 class="chart-title">地区销售同比分析</h2>
|
|
|
</div>
|
|
|
- <table class="data-table">
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th>国家/地区</th>
|
|
|
- <th>当前订单数</th>
|
|
|
- <th>上期订单数</th>
|
|
|
- <th>订单增长率</th>
|
|
|
- <th>当前销售额</th>
|
|
|
- <th>上期销售额</th>
|
|
|
- <th>销售额增长率</th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- <?php foreach ($comparison_data as $row): ?>
|
|
|
- <tr>
|
|
|
- <td><?php echo htmlspecialchars($row['countryName']); ?></td>
|
|
|
- <td><?php echo number_format($row['current_orders']); ?></td>
|
|
|
- <td><?php echo number_format($row['prev_orders']); ?></td>
|
|
|
- <td class="<?php echo $row['order_growth'] >= 0 ? 'positive' : 'negative'; ?>">
|
|
|
- <?php echo number_format($row['order_growth'], 2); ?>%
|
|
|
- </td>
|
|
|
- <td>¥<?php echo number_format($row['current_amount'], 2); ?></td>
|
|
|
- <td>¥<?php echo number_format($row['prev_amount'], 2); ?></td>
|
|
|
- <td class="<?php echo $row['amount_growth'] >= 0 ? 'positive' : 'negative'; ?>">
|
|
|
- <?php echo number_format($row['amount_growth'], 2); ?>%
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- <?php endforeach; ?>
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
+ <div class="region-sales-comparison-container">
|
|
|
+ <table class="data-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th>国家/地区</th>
|
|
|
+ <th>当前订单数</th>
|
|
|
+ <th>上期订单数</th>
|
|
|
+ <th>订单增长率</th>
|
|
|
+ <th>当前销售额</th>
|
|
|
+ <th>上期销售额</th>
|
|
|
+ <th>销售额增长率</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <?php foreach ($comparison_data as $row): ?>
|
|
|
+ <tr>
|
|
|
+ <td><?php echo htmlspecialchars($row['countryName']); ?></td>
|
|
|
+ <td><?php echo number_format($row['current_orders']); ?></td>
|
|
|
+ <td><?php echo number_format($row['prev_orders']); ?></td>
|
|
|
+ <td class="<?php echo $row['order_growth'] >= 0 ? 'positive' : 'negative'; ?>">
|
|
|
+ <?php echo number_format($row['order_growth'], 2); ?>%
|
|
|
+ </td>
|
|
|
+ <td>¥<?php echo number_format($row['current_amount'], 2); ?></td>
|
|
|
+ <td>¥<?php echo number_format($row['prev_amount'], 2); ?></td>
|
|
|
+ <td class="<?php echo $row['amount_growth'] >= 0 ? 'positive' : 'negative'; ?>">
|
|
|
+ <?php echo number_format($row['amount_growth'], 2); ?>%
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <?php endforeach; ?>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:调整表格容器 */
|
|
|
+ .region-sales-comparison-container {
|
|
|
+ width: 100%;
|
|
|
+ overflow-x: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .region-sales-comparison-container {
|
|
|
+ max-height: 33vh; /* 视窗高度的1/3 */
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
<?php
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
- * 获取地区总销售额及增长率
|
|
|
+ * 获取地区总销售额
|
|
|
*
|
|
|
* @param mysqli $conn 数据库连接
|
|
|
* @param string $start_date 开始日期
|
|
|
* @param string $end_date 结束日期
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return array 总销售额和增长率
|
|
|
*/
|
|
|
-function getRegionTotalSales($conn, $start_date, $end_date, $employee_filter = null) {
|
|
|
+function getRegionTotalSales($conn, $start_date, $end_date, $employee_filter = null, $country_filter = 0) {
|
|
|
// 如果有业务员过滤条件
|
|
|
$employee_condition = "";
|
|
|
|
|
@@ -444,12 +490,19 @@ function getRegionTotalSales($conn, $start_date, $end_date, $employee_filter = n
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ $country_condition = "";
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_condition = " AND cu.cs_country = " . intval($country_filter);
|
|
|
+ }
|
|
|
+
|
|
|
// 计算当前周期销售额
|
|
|
$sql = "SELECT SUM(o.total_amount) as total_amount
|
|
|
FROM orders o
|
|
|
JOIN customer cu ON o.customer_id = cu.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
- $employee_condition";
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
+ $employee_condition
|
|
|
+ $country_condition";
|
|
|
|
|
|
$stmt = $conn->prepare($sql);
|
|
|
$stmt->bind_param("ss", $start_date, $end_date);
|
|
@@ -498,9 +551,10 @@ function getRegionTotalSales($conn, $start_date, $end_date, $employee_filter = n
|
|
|
* @param string $start_date 开始日期
|
|
|
* @param string $end_date 结束日期
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return array 活跃国家信息
|
|
|
*/
|
|
|
-function getActiveCountries($conn, $start_date, $end_date, $employee_filter = null) {
|
|
|
+function getActiveCountries($conn, $start_date, $end_date, $employee_filter = null, $country_filter = 0) {
|
|
|
// 如果有业务员过滤条件
|
|
|
$employee_condition = "";
|
|
|
|
|
@@ -524,11 +578,26 @@ function getActiveCountries($conn, $start_date, $end_date, $employee_filter = nu
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- $sql = "SELECT COUNT(DISTINCT cu.cs_country) as country_count
|
|
|
- FROM orders o
|
|
|
- JOIN customer cu ON o.customer_id = cu.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
- $employee_condition";
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ $country_condition = "";
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_condition = " AND cu.cs_country = " . intval($country_filter);
|
|
|
+ // 如果指定了国家/地区,则活跃国家数为1或0
|
|
|
+ $sql = "SELECT COUNT(*) as country_count
|
|
|
+ FROM orders o
|
|
|
+ JOIN customer cu ON o.customer_id = cu.id
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
+ $employee_condition
|
|
|
+ $country_condition
|
|
|
+ LIMIT 1";
|
|
|
+ } else {
|
|
|
+ // 否则计算所有活跃国家
|
|
|
+ $sql = "SELECT COUNT(DISTINCT cu.cs_country) as country_count
|
|
|
+ FROM orders o
|
|
|
+ JOIN customer cu ON o.customer_id = cu.id
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
+ $employee_condition";
|
|
|
+ }
|
|
|
|
|
|
$stmt = $conn->prepare($sql);
|
|
|
$stmt->bind_param("ss", $start_date, $end_date);
|
|
@@ -581,7 +650,7 @@ function getAverageOrderByRegion($conn, $start_date, $end_date, $employee_filter
|
|
|
FROM orders o
|
|
|
JOIN customer cu ON o.customer_id = cu.id
|
|
|
JOIN country c ON cu.cs_country = c.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
$employee_condition
|
|
|
GROUP BY cu.cs_country
|
|
|
HAVING order_count >= 5
|
|
@@ -660,7 +729,7 @@ function getRegionCategoryPreferences($conn, $start_date, $end_date, $employee_f
|
|
|
JOIN order_items oi ON o.id = oi.order_id
|
|
|
JOIN products p ON oi.product_id = p.id
|
|
|
JOIN product_categories pc ON p.category_id = pc.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
+ WHERE o.is_deleted = 0 AND oi.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
$employee_condition
|
|
|
GROUP BY cu.cs_country, p.category_id
|
|
|
ORDER BY c.countryName, total_quantity DESC";
|
|
@@ -713,9 +782,10 @@ function getRegionCategoryPreferences($conn, $start_date, $end_date, $employee_f
|
|
|
* @param string $end_date 结束日期
|
|
|
* @param string $period 时间粒度 (day/week/month)
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return array 地区销售增长趋势数据
|
|
|
*/
|
|
|
-function getRegionGrowthTrends($conn, $start_date, $end_date, $period = 'month', $employee_filter = null) {
|
|
|
+function getRegionGrowthTrends($conn, $start_date, $end_date, $period = 'month', $employee_filter = null, $country_filter = 0) {
|
|
|
$period_format = getPeriodFormat($period);
|
|
|
|
|
|
// 如果有业务员过滤条件
|
|
@@ -741,6 +811,13 @@ function getRegionGrowthTrends($conn, $start_date, $end_date, $period = 'month',
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ $country_condition = "";
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_filter = intval($country_filter);
|
|
|
+ $country_condition = " AND cu.cs_country = $country_filter";
|
|
|
+ }
|
|
|
+
|
|
|
$sql = "SELECT
|
|
|
c.countryName,
|
|
|
DATE_FORMAT(o.order_date, ?) as time_period,
|
|
@@ -748,8 +825,9 @@ function getRegionGrowthTrends($conn, $start_date, $end_date, $period = 'month',
|
|
|
FROM orders o
|
|
|
JOIN customer cu ON o.customer_id = cu.id
|
|
|
JOIN country c ON cu.cs_country = c.id
|
|
|
- WHERE o.order_date BETWEEN ? AND ?
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date BETWEEN ? AND ?
|
|
|
$employee_condition
|
|
|
+ $country_condition
|
|
|
GROUP BY cu.cs_country, time_period
|
|
|
ORDER BY c.countryName, time_period";
|
|
|
|
|
@@ -773,17 +851,22 @@ function getRegionGrowthTrends($conn, $start_date, $end_date, $period = 'month',
|
|
|
$trends[$row['countryName']][$row['time_period']] = $row['total_amount'];
|
|
|
}
|
|
|
|
|
|
- // 只保留前5个主要市场
|
|
|
- $top_markets = array_slice(array_keys($trends), 0, 5);
|
|
|
- $filtered_trends = [];
|
|
|
-
|
|
|
- foreach ($top_markets as $market) {
|
|
|
- $filtered_trends[$market] = $trends[$market];
|
|
|
+ // 只有在没有国家/地区筛选条件时才筛选前10个市场
|
|
|
+ if ($country_filter == 0) {
|
|
|
+ // 只保留前10个主要市场
|
|
|
+ $top_markets = array_slice(array_keys($trends), 0, 10);
|
|
|
+ $filtered_trends = [];
|
|
|
+
|
|
|
+ foreach ($top_markets as $market) {
|
|
|
+ $filtered_trends[$market] = $trends[$market];
|
|
|
+ }
|
|
|
+
|
|
|
+ $trends = $filtered_trends;
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
'time_periods' => $time_periods,
|
|
|
- 'trends' => $filtered_trends
|
|
|
+ 'trends' => $trends
|
|
|
];
|
|
|
}
|
|
|
|
|
@@ -792,9 +875,10 @@ function getRegionGrowthTrends($conn, $start_date, $end_date, $period = 'month',
|
|
|
*
|
|
|
* @param mysqli $conn 数据库连接
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return array 地区季节性销售分析数据
|
|
|
*/
|
|
|
-function getRegionSeasonalAnalysis($conn, $employee_filter = null) {
|
|
|
+function getRegionSeasonalAnalysis($conn, $employee_filter = null, $country_filter = 0) {
|
|
|
// 如果有业务员过滤条件
|
|
|
$employee_condition = "";
|
|
|
|
|
@@ -818,6 +902,13 @@ function getRegionSeasonalAnalysis($conn, $employee_filter = null) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ $country_condition = "";
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_filter = intval($country_filter);
|
|
|
+ $country_condition = " AND cu.cs_country = $country_filter";
|
|
|
+ }
|
|
|
+
|
|
|
$sql = "SELECT
|
|
|
c.countryName,
|
|
|
MONTH(o.order_date) as month,
|
|
@@ -825,8 +916,9 @@ function getRegionSeasonalAnalysis($conn, $employee_filter = null) {
|
|
|
FROM orders o
|
|
|
JOIN customer cu ON o.customer_id = cu.id
|
|
|
JOIN country c ON cu.cs_country = c.id
|
|
|
- WHERE o.order_date >= DATE_SUB(CURDATE(), INTERVAL 2 YEAR)
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date >= DATE_SUB(CURDATE(), INTERVAL 2 YEAR)
|
|
|
$employee_condition
|
|
|
+ $country_condition
|
|
|
GROUP BY cu.cs_country, month
|
|
|
ORDER BY c.countryName, month";
|
|
|
|
|
@@ -843,17 +935,29 @@ function getRegionSeasonalAnalysis($conn, $employee_filter = null) {
|
|
|
$seasonal[$row['countryName']][$row['month']] += $row['total_amount'];
|
|
|
}
|
|
|
|
|
|
- // 只保留前5个主要市场
|
|
|
- $top_markets = array_slice(array_keys($seasonal), 0, 5);
|
|
|
- $filtered_seasonal = [];
|
|
|
-
|
|
|
- foreach ($top_markets as $market) {
|
|
|
- $filtered_seasonal[$market] = array_values($seasonal[$market]);
|
|
|
+ // 只有在没有国家/地区筛选条件时才筛选前10个市场
|
|
|
+ if ($country_filter == 0) {
|
|
|
+ // 只保留前10个主要市场
|
|
|
+ $top_markets = array_slice(array_keys($seasonal), 0, 10);
|
|
|
+ $filtered_seasonal = [];
|
|
|
+
|
|
|
+ foreach ($top_markets as $market) {
|
|
|
+ $filtered_seasonal[$market] = array_values($seasonal[$market]);
|
|
|
+ }
|
|
|
+
|
|
|
+ $seasonal = $filtered_seasonal;
|
|
|
+ } else {
|
|
|
+ // 如果有国家筛选,转换为数组值形式
|
|
|
+ $filtered_seasonal = [];
|
|
|
+ foreach ($seasonal as $market => $months_data) {
|
|
|
+ $filtered_seasonal[$market] = array_values($months_data);
|
|
|
+ }
|
|
|
+ $seasonal = $filtered_seasonal;
|
|
|
}
|
|
|
|
|
|
return [
|
|
|
'months' => ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
|
|
|
- 'data' => $filtered_seasonal
|
|
|
+ 'data' => $seasonal
|
|
|
];
|
|
|
}
|
|
|
|
|
@@ -864,9 +968,10 @@ function getRegionSeasonalAnalysis($conn, $employee_filter = null) {
|
|
|
* @param string $start_date 开始日期
|
|
|
* @param string $end_date 结束日期
|
|
|
* @param array|int|null $employee_filter 业务员ID或ID数组,用于过滤数据
|
|
|
+ * @param int $country_filter 国家/地区ID,默认为0表示全部地区
|
|
|
* @return array 地区销售预测数据
|
|
|
*/
|
|
|
-function getRegionSalesForecast($conn, $start_date, $end_date, $employee_filter = null) {
|
|
|
+function getRegionSalesForecast($conn, $start_date, $end_date, $employee_filter = null, $country_filter = 0) {
|
|
|
// 如果有业务员过滤条件
|
|
|
$employee_condition = "";
|
|
|
|
|
@@ -890,6 +995,13 @@ function getRegionSalesForecast($conn, $start_date, $end_date, $employee_filter
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ // 添加国家/地区筛选条件
|
|
|
+ $country_condition = "";
|
|
|
+ if (is_numeric($country_filter) && $country_filter > 0) {
|
|
|
+ $country_filter = intval($country_filter);
|
|
|
+ $country_condition = " AND cu.cs_country = $country_filter";
|
|
|
+ }
|
|
|
+
|
|
|
// 获取过去12个月的销售数据作为基础
|
|
|
$sql = "SELECT
|
|
|
c.countryName,
|
|
@@ -899,8 +1011,9 @@ function getRegionSalesForecast($conn, $start_date, $end_date, $employee_filter
|
|
|
FROM orders o
|
|
|
JOIN customer cu ON o.customer_id = cu.id
|
|
|
JOIN country c ON cu.cs_country = c.id
|
|
|
- WHERE o.order_date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
|
|
+ WHERE o.is_deleted = 0 AND o.order_date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
|
|
|
$employee_condition
|
|
|
+ $country_condition
|
|
|
GROUP BY cu.cs_country, year, month
|
|
|
ORDER BY c.countryName, year, month";
|
|
|
|
|
@@ -947,8 +1060,13 @@ function getRegionSalesForecast($conn, $start_date, $end_date, $employee_filter
|
|
|
// 合并历史和预测时间点
|
|
|
$all_chart_periods = array_merge($all_periods, $future_periods);
|
|
|
|
|
|
- // 只选取主要的5个市场做预测
|
|
|
- $top_markets = array_slice(array_keys($historical), 0, 5);
|
|
|
+ // 只有在没有国家/地区筛选条件时才筛选前10个市场
|
|
|
+ $top_markets = array_keys($historical);
|
|
|
+ if ($country_filter == 0) {
|
|
|
+ // 只选取主要的10个市场做预测
|
|
|
+ $top_markets = array_slice($top_markets, 0, 10);
|
|
|
+ }
|
|
|
+
|
|
|
$forecast_data = [];
|
|
|
|
|
|
foreach ($top_markets as $market) {
|
|
@@ -1194,7 +1312,23 @@ function renderRegionGrowthTrendsChart($growth_data) {
|
|
|
<div class="chart-header">
|
|
|
<h2 class="chart-title">地区销售增长趋势</h2>
|
|
|
</div>
|
|
|
- <canvas id="growthTrendsChart"></canvas>
|
|
|
+ <div class="region-growth-chart-container">
|
|
|
+ <canvas id="growthTrendsChart"></canvas>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:调整图表高度 */
|
|
|
+ .region-growth-chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .region-growth-chart-container {
|
|
|
+ height: 33vh; /* 视窗高度的1/3 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
|
|
|
<script>
|
|
|
// 地区销售增长趋势图
|
|
@@ -1207,6 +1341,7 @@ function renderRegionGrowthTrendsChart($growth_data) {
|
|
|
},
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
+ maintainAspectRatio: false, // 允许图表高度自适应
|
|
|
scales: {
|
|
|
x: {
|
|
|
title: {
|
|
@@ -1263,7 +1398,23 @@ function renderRegionSeasonalAnalysisChart($seasonal_data) {
|
|
|
<div class="chart-header">
|
|
|
<h2 class="chart-title">地区季节性销售分析</h2>
|
|
|
</div>
|
|
|
- <canvas id="seasonalAnalysisChart"></canvas>
|
|
|
+ <div class="region-seasonal-chart-container">
|
|
|
+ <canvas id="seasonalAnalysisChart"></canvas>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:调整图表高度 */
|
|
|
+ .region-seasonal-chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .region-seasonal-chart-container {
|
|
|
+ height: 33vh; /* 视窗高度的1/3 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
|
|
|
<script>
|
|
|
// 地区季节性销售分析图
|
|
@@ -1276,6 +1427,7 @@ function renderRegionSeasonalAnalysisChart($seasonal_data) {
|
|
|
},
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
+ maintainAspectRatio: false, // 允许图表高度自适应
|
|
|
scales: {
|
|
|
x: {
|
|
|
title: {
|
|
@@ -1360,7 +1512,23 @@ function renderRegionSalesForecastChart($forecast_data) {
|
|
|
<div class="chart-header">
|
|
|
<h2 class="chart-title">地区销售预测 (未来6个月)</h2>
|
|
|
</div>
|
|
|
- <canvas id="forecastChart"></canvas>
|
|
|
+ <div class="region-forecast-chart-container">
|
|
|
+ <canvas id="forecastChart"></canvas>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:调整图表高度 */
|
|
|
+ .region-forecast-chart-container {
|
|
|
+ width: 100%;
|
|
|
+ height: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .region-forecast-chart-container {
|
|
|
+ height: 33vh; /* 视窗高度的1/3 */
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
|
|
|
<script>
|
|
|
// 地区销售预测图
|
|
@@ -1373,6 +1541,7 @@ function renderRegionSalesForecastChart($forecast_data) {
|
|
|
},
|
|
|
options: {
|
|
|
responsive: true,
|
|
|
+ maintainAspectRatio: false, // 允许图表高度自适应
|
|
|
scales: {
|
|
|
x: {
|
|
|
title: {
|
|
@@ -1407,35 +1576,49 @@ function renderRegionSalesForecastChart($forecast_data) {
|
|
|
*
|
|
|
* @param array $region_labels 地区标签
|
|
|
* @param array $region_order_counts 地区订单数量
|
|
|
- * @param array $region_quantities 地区产品数量
|
|
|
* @param array $region_amounts 地区销售金额
|
|
|
* @return void
|
|
|
*/
|
|
|
-function renderTopRegionsTable($region_labels, $region_order_counts, $region_quantities, $region_amounts) {
|
|
|
+function renderTopRegionsTable($region_labels, $region_order_counts, $region_amounts) {
|
|
|
?>
|
|
|
- <table class="data-table">
|
|
|
- <thead>
|
|
|
- <tr>
|
|
|
- <th>排名</th>
|
|
|
- <th>国家/地区</th>
|
|
|
- <th>订单数</th>
|
|
|
- <th>产品数量</th>
|
|
|
- <th>销售金额</th>
|
|
|
- <th>平均订单金额</th>
|
|
|
- </tr>
|
|
|
- </thead>
|
|
|
- <tbody>
|
|
|
- <?php for ($i = 0; $i < count($region_labels); $i++): ?>
|
|
|
+ <div class="top-regions-table-container">
|
|
|
+ <table class="data-table">
|
|
|
+ <thead>
|
|
|
<tr>
|
|
|
- <td><?php echo $i + 1; ?></td>
|
|
|
- <td><?php echo htmlspecialchars($region_labels[$i]); ?></td>
|
|
|
- <td><?php echo number_format($region_order_counts[$i]); ?></td>
|
|
|
- <td><?php echo number_format($region_quantities[$i]); ?></td>
|
|
|
- <td>¥<?php echo number_format($region_amounts[$i], 2); ?></td>
|
|
|
- <td>¥<?php echo number_format($region_order_counts[$i] > 0 ? $region_amounts[$i] / $region_order_counts[$i] : 0, 2); ?></td>
|
|
|
+ <th>排名</th>
|
|
|
+ <th>国家/地区</th>
|
|
|
+ <th>订单数</th>
|
|
|
+ <th>销售金额</th>
|
|
|
+ <th>平均订单金额</th>
|
|
|
</tr>
|
|
|
- <?php endfor; ?>
|
|
|
- </tbody>
|
|
|
- </table>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <?php for ($i = 0; $i < count($region_labels); $i++): ?>
|
|
|
+ <tr>
|
|
|
+ <td><?php echo $i + 1; ?></td>
|
|
|
+ <td><?php echo htmlspecialchars($region_labels[$i]); ?></td>
|
|
|
+ <td><?php echo number_format($region_order_counts[$i]); ?></td>
|
|
|
+ <td>¥<?php echo number_format($region_amounts[$i], 2); ?></td>
|
|
|
+ <td>¥<?php echo number_format($region_order_counts[$i] > 0 ? $region_amounts[$i] / $region_order_counts[$i] : 0, 2); ?></td>
|
|
|
+ </tr>
|
|
|
+ <?php endfor; ?>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ /* 响应式布局:调整表格容器 */
|
|
|
+ .top-regions-table-container {
|
|
|
+ width: 100%;
|
|
|
+ overflow-x: auto;
|
|
|
+ }
|
|
|
+
|
|
|
+ @media (min-width: 768px) {
|
|
|
+ .top-regions-table-container {
|
|
|
+ max-height: 33vh; /* 视窗高度的1/3 */
|
|
|
+ overflow-y: auto;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </style>
|
|
|
<?php
|
|
|
}
|