|
@@ -428,4 +428,307 @@ function renderNewCustomersByEmployeeChart($employee_data) {
|
|
|
});
|
|
|
</script>
|
|
|
<?php
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 获取新客户购买产品明细
|
|
|
+ *
|
|
|
+ * @param mysqli $conn 数据库连接
|
|
|
+ * @param string $start_date 开始日期
|
|
|
+ * @param string $end_date 结束日期
|
|
|
+ * @param int $category_id 产品分类ID,0表示所有分类
|
|
|
+ * @return array 新客户购买产品数据
|
|
|
+ */
|
|
|
+function getNewCustomerProductPurchases($conn, $start_date, $end_date, $category_id = 0) {
|
|
|
+ // 获取在指定日期范围内首次购买的客户
|
|
|
+ $new_customer_sql = "
|
|
|
+ SELECT DISTINCT o.customer_id
|
|
|
+ FROM orders o
|
|
|
+ WHERE o.order_date BETWEEN ? AND ?
|
|
|
+ AND o.order_status != 0
|
|
|
+ AND NOT EXISTS (
|
|
|
+ SELECT 1 FROM orders o2
|
|
|
+ WHERE o2.customer_id = o.customer_id
|
|
|
+ AND o2.order_date < ?
|
|
|
+ AND o2.order_status != 0
|
|
|
+ )
|
|
|
+ ";
|
|
|
+
|
|
|
+ $stmt = $conn->prepare($new_customer_sql);
|
|
|
+ $stmt->bind_param("sss", $start_date, $end_date, $start_date);
|
|
|
+ $stmt->execute();
|
|
|
+ $new_customers_result = $stmt->get_result();
|
|
|
+
|
|
|
+ $new_customer_ids = [];
|
|
|
+ while ($row = $new_customers_result->fetch_assoc()) {
|
|
|
+ $new_customer_ids[] = $row['customer_id'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 如果没有新客户,返回空数组
|
|
|
+ if (empty($new_customer_ids)) {
|
|
|
+ return [];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建查询条件中的客户ID列表
|
|
|
+ $customer_ids_str = implode(',', $new_customer_ids);
|
|
|
+
|
|
|
+ // 查询这些新客户购买的产品
|
|
|
+ $category_filter = "";
|
|
|
+ if ($category_id > 0) {
|
|
|
+ $category_filter = "AND p.category_id = " . intval($category_id);
|
|
|
+ }
|
|
|
+
|
|
|
+ $product_sql = "
|
|
|
+ SELECT
|
|
|
+ p.id,
|
|
|
+ p.ProductName as product_name,
|
|
|
+ pc.name as category_name,
|
|
|
+ COUNT(DISTINCT o.id) as order_count,
|
|
|
+ COUNT(DISTINCT o.customer_id) as customer_count,
|
|
|
+ SUM(oi.quantity) as total_quantity,
|
|
|
+ SUM(oi.total_price) as total_revenue,
|
|
|
+ AVG(oi.unit_price) as avg_price
|
|
|
+ FROM orders o
|
|
|
+ JOIN order_items oi ON o.id = oi.order_id
|
|
|
+ JOIN products p ON oi.product_id = p.id
|
|
|
+ LEFT JOIN product_categories pc ON p.category_id = pc.id
|
|
|
+ WHERE o.customer_id IN ({$customer_ids_str})
|
|
|
+ AND o.order_date BETWEEN ? AND ?
|
|
|
+ AND o.order_status != 0
|
|
|
+ {$category_filter}
|
|
|
+ GROUP BY p.id
|
|
|
+ ORDER BY total_revenue DESC
|
|
|
+ ";
|
|
|
+
|
|
|
+ $stmt = $conn->prepare($product_sql);
|
|
|
+ $stmt->bind_param("ss", $start_date, $end_date);
|
|
|
+ $stmt->execute();
|
|
|
+
|
|
|
+ $result = $stmt->get_result();
|
|
|
+ $products = [];
|
|
|
+
|
|
|
+ while ($row = $result->fetch_assoc()) {
|
|
|
+ $products[] = $row;
|
|
|
+ }
|
|
|
+
|
|
|
+ return [
|
|
|
+ 'new_customer_count' => count($new_customer_ids),
|
|
|
+ 'products' => $products
|
|
|
+ ];
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 渲染新客户产品购买明细
|
|
|
+ *
|
|
|
+ * @param array $product_data 产品购买数据
|
|
|
+ * @return void
|
|
|
+ */
|
|
|
+function renderNewCustomerProductPurchases($product_data) {
|
|
|
+ if (empty($product_data) || empty($product_data['products'])) {
|
|
|
+ echo '<div class="alert alert-info">该时间段内没有新客户购买产品数据</div>';
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $products = $product_data['products'];
|
|
|
+ $new_customer_count = $product_data['new_customer_count'];
|
|
|
+ ?>
|
|
|
+
|
|
|
+ <div class="section-intro">
|
|
|
+ <p>本期间共有 <strong><?php echo number_format($new_customer_count); ?></strong> 名新客户进行了购买。以下是他们购买的产品明细:</p>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="table-responsive">
|
|
|
+ <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 ($products as $product): ?>
|
|
|
+ <tr>
|
|
|
+ <td><?php echo htmlspecialchars($product['product_name']); ?></td>
|
|
|
+ <td><?php echo htmlspecialchars($product['category_name'] ?: '未分类'); ?></td>
|
|
|
+ <td><?php echo number_format($product['order_count']); ?></td>
|
|
|
+ <td><?php echo number_format($product['customer_count']); ?></td>
|
|
|
+ <td><?php echo number_format($product['total_quantity']); ?></td>
|
|
|
+ <td><?php echo formatCurrency($product['total_revenue']); ?></td>
|
|
|
+ <td><?php echo formatCurrency($product['avg_price']); ?></td>
|
|
|
+ </tr>
|
|
|
+ <?php endforeach; ?>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 新客户产品购买分布图 -->
|
|
|
+ <div class="chart-container">
|
|
|
+ <div>
|
|
|
+ <canvas id="newCustomerProductChart"></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ <?php
|
|
|
+ // 准备图表数据
|
|
|
+ $product_names = [];
|
|
|
+ $product_quantities = [];
|
|
|
+ $product_revenues = [];
|
|
|
+
|
|
|
+ // 只取前10个产品用于图表显示
|
|
|
+ $top_products = array_slice($products, 0, 10);
|
|
|
+
|
|
|
+ foreach ($top_products as $product) {
|
|
|
+ $product_names[] = $product['product_name'];
|
|
|
+ $product_quantities[] = $product['total_quantity'];
|
|
|
+ $product_revenues[] = $product['total_revenue'];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成图表背景色
|
|
|
+ $colors = generateChartColors(count($top_products));
|
|
|
+ $backgroundColors = [];
|
|
|
+
|
|
|
+ foreach ($colors as $color) {
|
|
|
+ $backgroundColors[] = $color[0];
|
|
|
+ }
|
|
|
+ ?>
|
|
|
+
|
|
|
+ var newCustomerProductCtx = document.getElementById('newCustomerProductChart').getContext('2d');
|
|
|
+ new Chart(newCustomerProductCtx, {
|
|
|
+ type: 'bar',
|
|
|
+ data: {
|
|
|
+ labels: <?php echo json_encode($product_names); ?>,
|
|
|
+ datasets: [
|
|
|
+ {
|
|
|
+ label: '销售数量',
|
|
|
+ data: <?php echo json_encode($product_quantities); ?>,
|
|
|
+ backgroundColor: 'rgba(54, 162, 235, 0.7)',
|
|
|
+ borderColor: 'rgba(54, 162, 235, 1)',
|
|
|
+ borderWidth: 1,
|
|
|
+ yAxisID: 'y-quantity'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '销售金额',
|
|
|
+ data: <?php echo json_encode($product_revenues); ?>,
|
|
|
+ backgroundColor: 'rgba(255, 99, 132, 0.7)',
|
|
|
+ borderColor: 'rgba(255, 99, 132, 1)',
|
|
|
+ borderWidth: 1,
|
|
|
+ yAxisID: 'y-revenue',
|
|
|
+ type: 'line',
|
|
|
+ fill: false
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+ options: {
|
|
|
+ responsive: true,
|
|
|
+ scales: {
|
|
|
+ 'y-quantity': {
|
|
|
+ type: 'linear',
|
|
|
+ position: 'left',
|
|
|
+ title: {
|
|
|
+ display: true,
|
|
|
+ text: '销售数量'
|
|
|
+ },
|
|
|
+ beginAtZero: true
|
|
|
+ },
|
|
|
+ 'y-revenue': {
|
|
|
+ type: 'linear',
|
|
|
+ position: 'right',
|
|
|
+ title: {
|
|
|
+ display: true,
|
|
|
+ text: '销售金额'
|
|
|
+ },
|
|
|
+ beginAtZero: true,
|
|
|
+ grid: {
|
|
|
+ drawOnChartArea: false
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ plugins: {
|
|
|
+ title: {
|
|
|
+ display: true,
|
|
|
+ text: '新客户热门购买产品 (Top 10)'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <!-- 新客户产品类别分布图 -->
|
|
|
+ <div class="chart-container">
|
|
|
+ <div>
|
|
|
+ <canvas id="newCustomerCategoryChart"></canvas>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ <?php
|
|
|
+ // 按产品类别分组
|
|
|
+ $categories = [];
|
|
|
+ $category_data = [];
|
|
|
+
|
|
|
+ foreach ($products as $product) {
|
|
|
+ $category = $product['category_name'] ?: '未分类';
|
|
|
+ if (!isset($category_data[$category])) {
|
|
|
+ $category_data[$category] = [
|
|
|
+ 'quantity' => 0,
|
|
|
+ 'revenue' => 0
|
|
|
+ ];
|
|
|
+ }
|
|
|
+ $category_data[$category]['quantity'] += $product['total_quantity'];
|
|
|
+ $category_data[$category]['revenue'] += $product['total_revenue'];
|
|
|
+ }
|
|
|
+
|
|
|
+ $category_names = array_keys($category_data);
|
|
|
+ $category_quantities = array_column($category_data, 'quantity');
|
|
|
+ $category_revenues = array_column($category_data, 'revenue');
|
|
|
+
|
|
|
+ // 计算占比
|
|
|
+ $total_revenue = array_sum($category_revenues);
|
|
|
+ $revenue_percentages = [];
|
|
|
+
|
|
|
+ foreach ($category_revenues as $revenue) {
|
|
|
+ $revenue_percentages[] = round(($revenue / $total_revenue) * 100, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 生成图表背景色
|
|
|
+ $category_colors = generateChartColors(count($category_data), false);
|
|
|
+ $category_bg_colors = array_column($category_colors, 0);
|
|
|
+ ?>
|
|
|
+
|
|
|
+ var newCustomerCategoryCtx = document.getElementById('newCustomerCategoryChart').getContext('2d');
|
|
|
+ new Chart(newCustomerCategoryCtx, {
|
|
|
+ type: 'pie',
|
|
|
+ data: {
|
|
|
+ labels: <?php echo json_encode($category_names); ?>,
|
|
|
+ datasets: [{
|
|
|
+ data: <?php echo json_encode($revenue_percentages); ?>,
|
|
|
+ backgroundColor: <?php echo json_encode($category_bg_colors); ?>,
|
|
|
+ borderWidth: 1
|
|
|
+ }]
|
|
|
+ },
|
|
|
+ options: {
|
|
|
+ responsive: true,
|
|
|
+ plugins: {
|
|
|
+ title: {
|
|
|
+ display: true,
|
|
|
+ text: '新客户产品类别销售占比'
|
|
|
+ },
|
|
|
+ tooltip: {
|
|
|
+ callbacks: {
|
|
|
+ label: function(context) {
|
|
|
+ return context.label + ': ' + context.raw + '%';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ </script>
|
|
|
+ <?php
|
|
|
}
|