12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472 |
- <?php
- /**
- * 客户统计分析模块
- *
- * 包含与客户相关的数据分析功能
- */
- require_once 'statistics_utils.php';
- /**
- * 获取客户类型分布
- *
- * @param mysqli $conn 数据库连接
- * @return mysqli_result 客户类型分布数据结果集
- */
- function getCustomerTypeDistribution($conn) {
- $sql = "SELECT
- ct.businessType,
- COUNT(c.id) as customer_count
- FROM customer c
- JOIN clienttype ct ON c.cs_type = ct.id
- GROUP BY c.cs_type";
-
- return $conn->query($sql);
- }
- /**
- * 获取成交阶段分布
- *
- * @param mysqli $conn 数据库连接
- * @return mysqli_result 成交阶段分布数据结果集
- */
- function getDealStageDistribution($conn) {
- $sql = "SELECT
- cs_deal,
- CASE
- WHEN cs_deal = 1 THEN '背景调查'
- WHEN cs_deal = 2 THEN '明确需求'
- WHEN cs_deal = 3 THEN '已成交'
- ELSE '其他'
- END as stage_name,
- COUNT(id) as customer_count
- FROM customer
- GROUP BY cs_deal";
-
- return $conn->query($sql);
- }
- /**
- * 获取客户增长趋势
- *
- * @param mysqli $conn 数据库连接
- * @param int $months 获取多少个月的数据,默认12个月
- * @return mysqli_result 客户增长趋势数据结果集
- */
- function getCustomerGrowthTrend($conn, $months = 12) {
- $sql = "SELECT
- DATE_FORMAT(cs_addtime, '%Y-%m') as month,
- COUNT(id) as new_customers
- FROM customer
- WHERE cs_addtime >= DATE_SUB(CURDATE(), INTERVAL ? MONTH)
- GROUP BY DATE_FORMAT(cs_addtime, '%Y-%m')
- ORDER BY month";
-
- $stmt = $conn->prepare($sql);
- $stmt->bind_param("i", $months);
- $stmt->execute();
- return $stmt->get_result();
- }
- /**
- * 获取新老客户订单分析
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @return array 新老客户订单分析数据
- */
- function getNewVsReturningCustomerOrders($conn, $start_date, $end_date) {
- // 获取选定日期范围内的订单
- $sql = "SELECT
- o.customer_id,
- COUNT(o.id) as order_count,
- SUM(o.total_amount) as total_amount,
- MIN(o.order_date) as first_order_date,
- MAX(c.cs_addtime) as customer_addtime
- FROM orders o
- JOIN customer c ON o.customer_id = c.id
- WHERE o.order_date BETWEEN ? AND ?
- GROUP BY o.customer_id";
-
- $stmt = $conn->prepare($sql);
- $stmt->bind_param("ss", $start_date, $end_date);
- $stmt->execute();
- $result = $stmt->get_result();
-
- $new_customers = 0;
- $returning_customers = 0;
- $new_customer_amount = 0;
- $returning_customer_amount = 0;
-
- while ($row = $result->fetch_assoc()) {
- // 查找之前是否有订单
- $prev_sql = "SELECT id FROM orders
- WHERE customer_id = ?
- AND order_date < ?
- LIMIT 1";
-
- $prev_stmt = $conn->prepare($prev_sql);
- $prev_stmt->bind_param("is", $row['customer_id'], $start_date);
- $prev_stmt->execute();
- $prev_result = $prev_stmt->get_result();
-
- if ($prev_result->num_rows > 0) {
- // 老客户
- $returning_customers++;
- $returning_customer_amount += $row['total_amount'];
- } else {
- // 新客户
- $new_customers++;
- $new_customer_amount += $row['total_amount'];
- }
- }
-
- return [
- 'new_customers' => $new_customers,
- 'returning_customers' => $returning_customers,
- 'new_customer_amount' => $new_customer_amount,
- 'returning_customer_amount' => $returning_customer_amount,
- 'total_customers' => $new_customers + $returning_customers,
- 'total_amount' => $new_customer_amount + $returning_customer_amount
- ];
- }
- /**
- * 渲染客户类型分布图
- *
- * @param array $type_labels 类型标签
- * @param array $type_data 类型数据
- * @return void
- */
- function renderCustomerTypeChart($type_labels, $type_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户类型分布</h2>
- </div>
- <canvas id="customerTypeChart"></canvas>
- </div>
-
- <script>
- // 客户类型分布图
- var customerTypeCtx = document.getElementById('customerTypeChart').getContext('2d');
- var customerTypeChart = new Chart(customerTypeCtx, {
- type: 'doughnut',
- data: {
- labels: <?php echo json_encode($type_labels); ?>,
- datasets: [{
- data: <?php echo json_encode($type_data); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(153, 102, 255, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- plugins: {
- legend: {
- position: 'right',
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 渲染成交阶段分布图
- *
- * @param array $stage_labels 阶段标签
- * @param array $stage_data 阶段数据
- * @return void
- */
- function renderDealStageChart($stage_labels, $stage_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">成交阶段分布</h2>
- </div>
- <canvas id="dealStageChart"></canvas>
- </div>
-
- <script>
- // 成交阶段分布图
- var dealStageCtx = document.getElementById('dealStageChart').getContext('2d');
- var dealStageChart = new Chart(dealStageCtx, {
- type: 'bar',
- data: {
- labels: <?php echo json_encode($stage_labels); ?>,
- datasets: [{
- label: '客户数量',
- data: <?php echo json_encode($stage_data); ?>,
- backgroundColor: [
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(255, 99, 132, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- scales: {
- y: {
- beginAtZero: true,
- title: {
- display: true,
- text: '客户数量'
- }
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 渲染客户增长趋势图
- *
- * @param array $growth_labels 增长标签
- * @param array $growth_data 增长数据
- * @return void
- */
- function renderCustomerGrowthChart($growth_labels, $growth_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户增长趋势</h2>
- </div>
- <canvas id="customerGrowthChart"></canvas>
- </div>
-
- <script>
- // 客户增长趋势图
- var customerGrowthCtx = document.getElementById('customerGrowthChart').getContext('2d');
- var customerGrowthChart = new Chart(customerGrowthCtx, {
- type: 'line',
- data: {
- labels: <?php echo json_encode($growth_labels); ?>,
- datasets: [{
- label: '新增客户',
- data: <?php echo json_encode($growth_data); ?>,
- backgroundColor: 'rgba(75, 192, 192, 0.2)',
- borderColor: 'rgba(75, 192, 192, 1)',
- borderWidth: 2,
- tension: 0.1
- }]
- },
- options: {
- responsive: true,
- scales: {
- y: {
- beginAtZero: true,
- title: {
- display: true,
- text: '客户数量'
- }
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 渲染新老客户分析图
- *
- * @param array $new_vs_returning 新老客户数据
- * @return void
- */
- function renderNewVsReturningCustomersChart($new_vs_returning) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">新老客户分析</h2>
- </div>
- <style>
- .pie-charts-container {
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- margin-bottom: 20px;
- }
- .pie-chart-wrapper {
- flex: 0 0 48%;
- max-width: 48%;
- }
- .customer-stats-summary {
- margin-top: 20px;
- padding: 15px;
- background-color: #f9f9f9;
- border-radius: 5px;
- }
- .stats-row {
- display: flex;
- margin-bottom: 15px;
- }
- .stat-item {
- flex: 1;
- padding: 0 10px;
- }
- .stat-label {
- display: block;
- font-weight: bold;
- margin-bottom: 5px;
- color: #555;
- }
- .stat-value {
- font-size: 16px;
- color: #333;
- }
- </style>
- <div class="pie-charts-container">
- <div class="pie-chart-wrapper">
- <h3 style="text-align: center; margin-bottom: 15px;">客户数量分布</h3>
- <canvas id="newVsReturningCustomersChart"></canvas>
- </div>
- <div class="pie-chart-wrapper">
- <h3 style="text-align: center; margin-bottom: 15px;">销售额分布</h3>
- <canvas id="newVsReturningAmountChart"></canvas>
- </div>
- </div>
- <div class="customer-stats-summary">
- <div class="stats-row">
- <div class="stat-item">
- <span class="stat-label">总客户数:</span>
- <span class="stat-value"><?php echo number_format($new_vs_returning['total_customers']); ?></span>
- </div>
- <div class="stat-item">
- <span class="stat-label">新客户:</span>
- <span class="stat-value"><?php echo number_format($new_vs_returning['new_customers']); ?>
- (<?php echo ($new_vs_returning['total_customers'] > 0) ? number_format(($new_vs_returning['new_customers'] / $new_vs_returning['total_customers']) * 100, 1) : '0'; ?>%)</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">老客户:</span>
- <span class="stat-value"><?php echo number_format($new_vs_returning['returning_customers']); ?>
- (<?php echo ($new_vs_returning['total_customers'] > 0) ? number_format(($new_vs_returning['returning_customers'] / $new_vs_returning['total_customers']) * 100, 1) : '0'; ?>%)</span>
- </div>
- </div>
- <div class="stats-row">
- <div class="stat-item">
- <span class="stat-label">总销售额:</span>
- <span class="stat-value">¥<?php echo number_format($new_vs_returning['total_amount'], 2); ?></span>
- </div>
- <div class="stat-item">
- <span class="stat-label">新客户销售额:</span>
- <span class="stat-value">¥<?php echo number_format($new_vs_returning['new_customer_amount'], 2); ?>
- (<?php echo ($new_vs_returning['total_amount'] > 0) ? number_format(($new_vs_returning['new_customer_amount'] / $new_vs_returning['total_amount']) * 100, 1) : '0'; ?>%)</span>
- </div>
- <div class="stat-item">
- <span class="stat-label">老客户销售额:</span>
- <span class="stat-value">¥<?php echo number_format($new_vs_returning['returning_customer_amount'], 2); ?>
- (<?php echo ($new_vs_returning['total_amount'] > 0) ? number_format(($new_vs_returning['returning_customer_amount'] / $new_vs_returning['total_amount']) * 100, 1) : '0'; ?>%)</span>
- </div>
- </div>
- </div>
- </div>
-
- <script>
- // 新老客户数量图
- var newVsReturningCtx = document.getElementById('newVsReturningCustomersChart').getContext('2d');
- var newVsReturningChart = new Chart(newVsReturningCtx, {
- type: 'pie',
- data: {
- labels: ['新客户', '老客户'],
- datasets: [{
- data: [
- <?php echo $new_vs_returning['new_customers']; ?>,
- <?php echo $new_vs_returning['returning_customers']; ?>
- ],
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(255, 99, 132, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: true,
- plugins: {
- legend: {
- position: 'bottom',
- }
- }
- }
- });
-
- // 新老客户销售额图
- var amountCtx = document.getElementById('newVsReturningAmountChart').getContext('2d');
- var amountChart = new Chart(amountCtx, {
- type: 'pie',
- data: {
- labels: ['新客户销售额', '老客户销售额'],
- datasets: [{
- data: [
- <?php echo $new_vs_returning['new_customer_amount']; ?>,
- <?php echo $new_vs_returning['returning_customer_amount']; ?>
- ],
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(255, 99, 132, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- maintainAspectRatio: true,
- plugins: {
- legend: {
- position: 'bottom',
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 获取客户总数
- *
- * @param mysqli $conn 数据库连接
- * @param array|int $employee_filter 业务员ID或ID数组,用于过滤数据 (可选)
- * @return int 客户总数
- */
- function getTotalCustomers($conn, $employee_filter = null) {
- $sql = "SELECT COUNT(id) as total FROM customer";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $sql .= " WHERE cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $sql .= " WHERE cs_belong = $employee_filter";
- }
- }
-
- $result = $conn->query($sql);
- $row = $result->fetch_assoc();
- return $row['total'];
- }
- /**
- * 获取指定时间段内新增客户数
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @param array|int $employee_filter 业务员ID或ID数组,用于过滤数据 (可选)
- * @return int 新增客户数
- */
- function getNewCustomers($conn, $start_date, $end_date, $employee_filter = null) {
- // 使用 mysqli_real_escape_string 防止 SQL 注入
- $start_date = $conn->real_escape_string($start_date);
- $end_date = $conn->real_escape_string($end_date);
-
- $sql = "SELECT COUNT(id) as new_count
- FROM customer
- WHERE cs_addtime BETWEEN '$start_date' AND '$end_date'";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $sql .= " AND cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $sql .= " AND cs_belong = $employee_filter";
- }
- }
-
- $result = $conn->query($sql);
- $row = $result->fetch_assoc();
- return $row['new_count'];
- }
- /**
- * 计算平均客户价值(客户平均订单金额)
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @param array|int $employee_filter 业务员ID或ID数组,用于过滤数据 (可选)
- * @return float 平均客户价值
- */
- function getAverageCustomerValue($conn, $start_date, $end_date, $employee_filter = null) {
- // 使用 mysqli_real_escape_string 防止 SQL 注入
- $start_date = $conn->real_escape_string($start_date);
- $end_date = $conn->real_escape_string($end_date);
-
- $sql = "SELECT AVG(customer_value) as avg_value FROM (
- SELECT
- o.customer_id,
- SUM(o.total_amount) as customer_value
- FROM orders o
- JOIN customer c ON o.customer_id = c.id
- WHERE o.order_date BETWEEN '$start_date' AND '$end_date'";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $sql .= " AND c.cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $sql .= " AND c.cs_belong = $employee_filter";
- }
- }
-
- $sql .= " GROUP BY o.customer_id) as customer_values";
-
- $result = $conn->query($sql);
- $row = $result->fetch_assoc();
- return $row['avg_value'] ? $row['avg_value'] : 0;
- }
- /**
- * 计算客户留存率
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @param array|int $employee_filter 业务员ID或ID数组,用于过滤数据 (可选)
- * @return array 客户留存率数据
- */
- function getCustomerRetentionRate($conn, $start_date, $end_date, $employee_filter = null) {
- // 使用 mysqli_real_escape_string 防止 SQL 注入
- $start_date = $conn->real_escape_string($start_date);
- $end_date = $conn->real_escape_string($end_date);
-
- // 获取之前时间段的客户
- $previous_start = date('Y-m-d', strtotime('-1 year', strtotime($start_date)));
- $previous_end = date('Y-m-d', strtotime('-1 day', strtotime($start_date)));
-
- // 之前时间段的客户ID
- $prev_sql = "SELECT DISTINCT o.customer_id
- FROM orders o
- JOIN customer c ON o.customer_id = c.id
- WHERE o.order_date BETWEEN '$previous_start' AND '$previous_end'";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $prev_sql .= " AND c.cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $prev_sql .= " AND c.cs_belong = $employee_filter";
- }
- }
-
- $prev_result = $conn->query($prev_sql);
-
- $previous_customers = [];
- while ($row = $prev_result->fetch_assoc()) {
- $previous_customers[] = $row['customer_id'];
- }
-
- $previous_count = count($previous_customers);
-
- // 如果没有之前的客户,返回0
- if ($previous_count == 0) {
- return [
- 'retained_count' => 0,
- 'total_previous' => 0,
- 'retention_rate' => 0
- ];
- }
-
- // 查询当前时间段内,之前客户中再次购买的客户数
- $current_sql = "SELECT COUNT(DISTINCT o.customer_id) as retained_count
- FROM orders o
- JOIN customer c ON o.customer_id = c.id
- WHERE o.order_date BETWEEN '$start_date' AND '$end_date'
- AND o.customer_id IN (" . implode(',', $previous_customers) . ")";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $current_sql .= " AND c.cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $current_sql .= " AND c.cs_belong = $employee_filter";
- }
- }
-
- $current_result = $conn->query($current_sql);
- $row = $current_result->fetch_assoc();
-
- $retained_count = $row['retained_count'];
- $retention_rate = ($retained_count / $previous_count) * 100;
-
- return [
- 'retained_count' => $retained_count,
- 'total_previous' => $previous_count,
- 'retention_rate' => $retention_rate
- ];
- }
- /**
- * 计算下单转换率
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @param array|int $employee_filter 业务员ID或ID数组,用于过滤数据 (可选)
- * @return array 下单转换率数据
- */
- function getOrderConversionRate($conn, $start_date, $end_date, $employee_filter = null) {
- // 使用 mysqli_real_escape_string 防止 SQL 注入
- $start_date = $conn->real_escape_string($start_date);
- $end_date = $conn->real_escape_string($end_date);
-
- // 获取指定时间段内总客户数
- $total_sql = "SELECT COUNT(DISTINCT id) as total_count FROM customer WHERE cs_addtime <= '$end_date'";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $total_sql .= " AND cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $total_sql .= " AND cs_belong = $employee_filter";
- }
- }
-
- $total_result = $conn->query($total_sql);
- $total_row = $total_result->fetch_assoc();
- $total_customers = $total_row['total_count'];
-
- // 获取有订单的客户数
- $order_sql = "SELECT COUNT(DISTINCT o.customer_id) as order_count
- FROM orders o
- JOIN customer c ON o.customer_id = c.id
- WHERE o.order_date BETWEEN '$start_date' AND '$end_date'";
-
- // 如果有业务员过滤
- if ($employee_filter !== null) {
- if (is_array($employee_filter) && !empty($employee_filter)) {
- // 处理数组形式的业务员ID列表
- $emp_ids = array();
- foreach ($employee_filter as $emp_id) {
- if (is_numeric($emp_id)) {
- $emp_ids[] = intval($emp_id);
- }
- }
- if (!empty($emp_ids)) {
- $emp_ids_str = implode(',', $emp_ids);
- $order_sql .= " AND c.cs_belong IN ($emp_ids_str)";
- }
- } else if (is_numeric($employee_filter) && $employee_filter > 0) {
- // 处理单个业务员ID
- $employee_filter = intval($employee_filter);
- $order_sql .= " AND c.cs_belong = $employee_filter";
- }
- }
-
- $order_result = $conn->query($order_sql);
- $order_row = $order_result->fetch_assoc();
- $customers_with_orders = $order_row['order_count'];
-
- // 计算转换率
- $conversion_rate = ($total_customers > 0) ? ($customers_with_orders / $total_customers) * 100 : 0;
-
- return [
- 'total_customers' => $total_customers,
- 'customers_with_orders' => $customers_with_orders,
- 'conversion_rate' => $conversion_rate
- ];
- }
- /**
- * 渲染关键指标仪表板
- *
- * @param array $kpi_data 关键指标数据
- * @return void
- */
- function renderKeyMetricsCard($kpi_data) {
- ?>
- <div class="stats-card-container">
- <div class="stats-card">
- <div class="stats-card-header">
- <h3>客户总数</h3>
- </div>
- <div class="stats-card-body">
- <div class="stats-card-value"><?php echo number_format($kpi_data['total_customers']); ?></div>
- </div>
- </div>
-
- <div class="stats-card">
- <div class="stats-card-header">
- <h3>新增客户</h3>
- </div>
- <div class="stats-card-body">
- <div class="stats-card-value"><?php echo number_format($kpi_data['new_customers']); ?></div>
- </div>
- </div>
-
- <div class="stats-card">
- <div class="stats-card-header">
- <h3>平均客户价值</h3>
- </div>
- <div class="stats-card-body">
- <div class="stats-card-value">¥<?php echo number_format($kpi_data['avg_customer_value'], 2); ?></div>
- </div>
- </div>
-
- <div class="stats-card">
- <div class="stats-card-header">
- <h3>客户留存率</h3>
- </div>
- <div class="stats-card-body">
- <div class="stats-card-value"><?php echo number_format($kpi_data['retention_rate'], 1); ?>%</div>
- <div class="stats-card-subtitle"><?php echo number_format($kpi_data['retained_count']); ?> / <?php echo number_format($kpi_data['total_previous']); ?></div>
- </div>
- </div>
-
- <div class="stats-card">
- <div class="stats-card-header">
- <h3>下单转换率</h3>
- </div>
- <div class="stats-card-body">
- <div class="stats-card-value"><?php echo number_format($kpi_data['conversion_rate'], 1); ?>%</div>
- <div class="stats-card-subtitle"><?php echo number_format($kpi_data['customers_with_orders']); ?> / <?php echo number_format($kpi_data['total_customers']); ?></div>
- </div>
- </div>
- </div>
- <?php
- }
- /**
- * 获取客户价值分布数据
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @return array 客户价值分布数据
- */
- function getCustomerValueDistribution($conn, $start_date, $end_date) {
- $sql = "SELECT
- value_segment,
- COUNT(customer_id) as customer_count,
- SUM(total_amount) as total_amount
- FROM (
- SELECT
- o.customer_id,
- SUM(o.total_amount) as total_amount,
- CASE
- WHEN SUM(o.total_amount) > 100000 THEN '高价值客户(>10万)'
- WHEN SUM(o.total_amount) > 50000 THEN '中高价值客户(5-10万)'
- WHEN SUM(o.total_amount) > 10000 THEN '中价值客户(1-5万)'
- WHEN SUM(o.total_amount) > 5000 THEN '低价值客户(5千-1万)'
- ELSE '微价值客户(<5千)'
- END as value_segment
- FROM orders o
- WHERE o.order_date BETWEEN ? AND ?
- GROUP BY o.customer_id
- ) as customer_value
- GROUP BY value_segment
- ORDER BY
- CASE value_segment
- WHEN '高价值客户(>10万)' THEN 1
- WHEN '中高价值客户(5-10万)' THEN 2
- WHEN '中价值客户(1-5万)' THEN 3
- WHEN '低价值客户(5千-1万)' THEN 4
- ELSE 5
- END";
-
- $stmt = $conn->prepare($sql);
- $stmt->bind_param("ss", $start_date, $end_date);
- $stmt->execute();
- $result = $stmt->get_result();
-
- $value_segments = [];
- $customer_counts = [];
- $total_amounts = [];
- $total_customers = 0;
-
- while ($row = $result->fetch_assoc()) {
- $value_segments[] = $row['value_segment'];
- $customer_counts[] = $row['customer_count'];
- $total_amounts[] = $row['total_amount'];
- $total_customers += $row['customer_count'];
- }
-
- return [
- 'segments' => $value_segments,
- 'counts' => $customer_counts,
- 'amounts' => $total_amounts,
- 'total_customers' => $total_customers
- ];
- }
- /**
- * 获取客户活跃度分析数据
- *
- * @param mysqli $conn 数据库连接
- * @param string $end_date 截止日期
- * @return array 客户活跃度分析数据
- */
- function getCustomerActivityAnalysis($conn, $end_date) {
- $sql = "SELECT
- activity_level,
- COUNT(*) as customer_count
- FROM (
- SELECT
- o.customer_id,
- CASE
- WHEN DATEDIFF(?, MAX(o.order_date)) <= 30 THEN '活跃客户(30天内)'
- WHEN DATEDIFF(?, MAX(o.order_date)) <= 90 THEN '一般活跃(90天内)'
- WHEN DATEDIFF(?, MAX(o.order_date)) <= 180 THEN '低活跃(180天内)'
- WHEN DATEDIFF(?, MAX(o.order_date)) <= 365 THEN '沉睡客户(1年内)'
- ELSE '流失客户(超过1年)'
- END as activity_level
- FROM orders o
- GROUP BY o.customer_id
- ) as customer_activity
- GROUP BY activity_level
- ORDER BY
- CASE activity_level
- WHEN '活跃客户(30天内)' THEN 1
- WHEN '一般活跃(90天内)' THEN 2
- WHEN '低活跃(180天内)' THEN 3
- WHEN '沉睡客户(1年内)' THEN 4
- ELSE 5
- END";
-
- $stmt = $conn->prepare($sql);
- $end_date_formatted = date('Y-m-d', strtotime($end_date));
- $stmt->bind_param("ssss", $end_date_formatted, $end_date_formatted, $end_date_formatted, $end_date_formatted);
- $stmt->execute();
- $result = $stmt->get_result();
-
- $activity_levels = [];
- $customer_counts = [];
-
- while ($row = $result->fetch_assoc()) {
- $activity_levels[] = $row['activity_level'];
- $customer_counts[] = $row['customer_count'];
- }
-
- return [
- 'levels' => $activity_levels,
- 'counts' => $customer_counts
- ];
- }
- /**
- * 获取客户流失风险分析数据
- *
- * @param mysqli $conn 数据库连接
- * @param string $end_date 截止日期
- * @return array 客户流失风险分析数据
- */
- function getCustomerChurnRiskAnalysis($conn, $end_date) {
- $sql = "SELECT
- risk_level,
- COUNT(*) as customer_count
- FROM (
- SELECT
- c.id,
- CASE
- WHEN last_order_date IS NULL THEN '从未购买'
- WHEN DATEDIFF(?, last_order_date) <= 90 THEN '低风险(90天内)'
- WHEN DATEDIFF(?, last_order_date) <= 180 THEN '中风险(90-180天)'
- WHEN DATEDIFF(?, last_order_date) <= 365 THEN '高风险(180-365天)'
- ELSE '极高风险(超过1年)'
- END as risk_level
- FROM customer c
- LEFT JOIN (
- SELECT customer_id, MAX(order_date) as last_order_date
- FROM orders
- GROUP BY customer_id
- ) o ON c.id = o.customer_id
- ) as customer_risk
- GROUP BY risk_level
- ORDER BY
- CASE risk_level
- WHEN '低风险(90天内)' THEN 1
- WHEN '中风险(90-180天)' THEN 2
- WHEN '高风险(180-365天)' THEN 3
- WHEN '极高风险(超过1年)' THEN 4
- WHEN '从未购买' THEN 5
- END";
-
- $stmt = $conn->prepare($sql);
- $end_date_formatted = date('Y-m-d', strtotime($end_date));
- $stmt->bind_param("sss", $end_date_formatted, $end_date_formatted, $end_date_formatted);
- $stmt->execute();
- $result = $stmt->get_result();
-
- $risk_levels = [];
- $customer_counts = [];
-
- while ($row = $result->fetch_assoc()) {
- $risk_levels[] = $row['risk_level'];
- $customer_counts[] = $row['customer_count'];
- }
-
- return [
- 'levels' => $risk_levels,
- 'counts' => $customer_counts
- ];
- }
- /**
- * 获取客户来源分析数据
- *
- * @param mysqli $conn 数据库连接
- * @return array 客户来源分析数据
- */
- function getCustomerSourceAnalysis($conn) {
- // 假设cs_from字段代表客户来源,需要根据实际情况调整SQL
- $sql = "SELECT
- source,
- COUNT(*) as customer_count
- FROM (
- SELECT
- id,
- CASE
- WHEN cs_from = 1 THEN '网站注册'
- WHEN cs_from = 2 THEN '销售开发'
- WHEN cs_from = 3 THEN '广告引流'
- WHEN cs_from = 4 THEN '展会获取'
- WHEN cs_from = 5 THEN '客户推荐'
- ELSE '其他来源'
- END as source
- FROM customer
- ) as customer_source
- GROUP BY source
- ORDER BY customer_count DESC";
-
- $result = $conn->query($sql);
-
- $sources = [];
- $counts = [];
-
- while ($row = $result->fetch_assoc()) {
- $sources[] = $row['source'];
- $counts[] = $row['customer_count'];
- }
-
- return [
- 'sources' => $sources,
- 'counts' => $counts
- ];
- }
- /**
- * 渲染客户价值分布图表
- *
- * @param array $value_data 客户价值分布数据
- * @return void
- */
- function renderCustomerValueCharts($value_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户价值分布</h2>
- </div>
-
- <div class="chart-row" style="display: flex; width: 100%; margin: 0 -10px;">
- <div class="chart-column" style="flex: 0 0 50%; max-width: 50%; padding: 0 10px; box-sizing: border-box;">
- <h3 style="text-align: center; margin-bottom: 15px;">客户价值分布(柱状图)</h3>
- <canvas id="customerValueBarChart"></canvas>
- </div>
- <div class="chart-column" style="flex: 0 0 50%; max-width: 50%; padding: 0 10px; box-sizing: border-box;">
- <h3 style="text-align: center; margin-bottom: 15px;">客户价值分布(饼图)</h3>
- <canvas id="customerValuePieChart"></canvas>
- </div>
- </div>
-
- <div class="customer-stats-summary">
- <div class="stats-row">
- <?php foreach ($value_data['segments'] as $index => $segment): ?>
- <div class="stat-item">
- <span class="stat-label"><?php echo $segment; ?>:</span>
- <span class="stat-value"><?php echo number_format($value_data['counts'][$index]); ?>
- (<?php echo ($value_data['total_customers'] > 0) ?
- number_format(($value_data['counts'][$index] / $value_data['total_customers']) * 100, 1) : '0'; ?>%)</span>
- <span class="stat-sub-value">¥<?php echo number_format($value_data['amounts'][$index], 2); ?></span>
- </div>
- <?php endforeach; ?>
- </div>
- </div>
- </div>
-
- <script>
- // 客户价值分布柱状图
- var valueBarCtx = document.getElementById('customerValueBarChart').getContext('2d');
- var valueBarChart = new Chart(valueBarCtx, {
- type: 'bar',
- data: {
- labels: <?php echo json_encode($value_data['segments']); ?>,
- datasets: [{
- label: '客户数量',
- data: <?php echo json_encode($value_data['counts']); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(153, 102, 255, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- scales: {
- y: {
- beginAtZero: true,
- title: {
- display: true,
- text: '客户数量'
- }
- }
- }
- }
- });
-
- // 客户价值分布饼图
- var valuePieCtx = document.getElementById('customerValuePieChart').getContext('2d');
- var valuePieChart = new Chart(valuePieCtx, {
- type: 'pie',
- data: {
- labels: <?php echo json_encode($value_data['segments']); ?>,
- datasets: [{
- data: <?php echo json_encode($value_data['counts']); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(153, 102, 255, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true
- }
- });
- </script>
- <?php
- }
- /**
- * 渲染客户活跃度分析图表
- *
- * @param array $activity_data 客户活跃度数据
- * @return void
- */
- function renderCustomerActivityChart($activity_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户活跃度分析</h2>
- </div>
- <canvas id="customerActivityChart"></canvas>
-
- <div class="customer-stats-summary">
- <div class="stats-row">
- <?php
- $total_customers = array_sum($activity_data['counts']);
- foreach ($activity_data['levels'] as $index => $level):
- ?>
- <div class="stat-item">
- <span class="stat-label"><?php echo $level; ?>:</span>
- <span class="stat-value"><?php echo number_format($activity_data['counts'][$index]); ?>
- (<?php echo ($total_customers > 0) ?
- number_format(($activity_data['counts'][$index] / $total_customers) * 100, 1) : '0'; ?>%)</span>
- </div>
- <?php endforeach; ?>
- </div>
- </div>
- </div>
-
- <script>
- // 客户活跃度分析图
- var activityCtx = document.getElementById('customerActivityChart').getContext('2d');
- var activityChart = new Chart(activityCtx, {
- type: 'bar',
- data: {
- labels: <?php echo json_encode($activity_data['levels']); ?>,
- datasets: [{
- label: '客户数量',
- data: <?php echo json_encode($activity_data['counts']); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(153, 102, 255, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- scales: {
- y: {
- beginAtZero: true,
- title: {
- display: true,
- text: '客户数量'
- }
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 渲染客户流失风险分析图表
- *
- * @param array $risk_data 客户流失风险数据
- * @return void
- */
- function renderCustomerChurnRiskChart($risk_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户流失风险分析</h2>
- </div>
- <canvas id="customerRiskChart"></canvas>
-
- <div class="customer-stats-summary">
- <div class="stats-row">
- <?php
- $total_customers = array_sum($risk_data['counts']);
- foreach ($risk_data['levels'] as $index => $level):
- ?>
- <div class="stat-item">
- <span class="stat-label"><?php echo $level; ?>:</span>
- <span class="stat-value"><?php echo number_format($risk_data['counts'][$index]); ?>
- (<?php echo ($total_customers > 0) ?
- number_format(($risk_data['counts'][$index] / $total_customers) * 100, 1) : '0'; ?>%)</span>
- </div>
- <?php endforeach; ?>
- </div>
- </div>
- </div>
-
- <script>
- // 客户流失风险分析图
- var riskCtx = document.getElementById('customerRiskChart').getContext('2d');
- var riskChart = new Chart(riskCtx, {
- type: 'doughnut',
- data: {
- labels: <?php echo json_encode($risk_data['levels']); ?>,
- datasets: [{
- data: <?php echo json_encode($risk_data['counts']); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(153, 102, 255, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- plugins: {
- legend: {
- position: 'right',
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 渲染客户来源分析图表
- *
- * @param array $source_data 客户来源数据
- * @return void
- */
- function renderCustomerSourceChart($source_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户来源分析</h2>
- </div>
- <canvas id="customerSourceChart"></canvas>
-
- <div class="customer-stats-summary">
- <div class="stats-row">
- <?php
- $total_customers = array_sum($source_data['counts']);
- foreach ($source_data['sources'] as $index => $source):
- ?>
- <div class="stat-item">
- <span class="stat-label"><?php echo $source; ?>:</span>
- <span class="stat-value"><?php echo number_format($source_data['counts'][$index]); ?>
- (<?php echo ($total_customers > 0) ?
- number_format(($source_data['counts'][$index] / $total_customers) * 100, 1) : '0'; ?>%)</span>
- </div>
- <?php endforeach; ?>
- </div>
- </div>
- </div>
-
- <script>
- // 客户来源分析图
- var sourceCtx = document.getElementById('customerSourceChart').getContext('2d');
- var sourceChart = new Chart(sourceCtx, {
- type: 'pie',
- data: {
- labels: <?php echo json_encode($source_data['sources']); ?>,
- datasets: [{
- data: <?php echo json_encode($source_data['counts']); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(153, 102, 255, 0.7)',
- 'rgba(255, 159, 64, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- responsive: true,
- plugins: {
- legend: {
- position: 'right',
- }
- }
- }
- });
- </script>
- <?php
- }
- /**
- * 获取客户转化漏斗数据
- *
- * @param mysqli $conn 数据库连接
- * @param string $start_date 开始日期
- * @param string $end_date 结束日期
- * @return array 客户转化漏斗数据
- */
- function getCustomerConversionFunnel($conn, $start_date, $end_date) {
- // 获取总客户数(潜在客户)
- $total_sql = "SELECT COUNT(id) as total FROM customer";
- $total_result = $conn->query($total_sql);
- $total_row = $total_result->fetch_assoc();
- $total_customers = $total_row['total'];
-
- // 获取明确需求的客户数
- $needs_sql = "SELECT COUNT(id) as needs_count FROM customer WHERE cs_deal = 2";
- $needs_result = $conn->query($needs_sql);
- $needs_row = $needs_result->fetch_assoc();
- $needs_customers = $needs_row['needs_count'];
-
- // 获取已成交客户数
- $deal_sql = "SELECT COUNT(id) as deal_count FROM customer WHERE cs_deal = 3";
- $deal_result = $conn->query($deal_sql);
- $deal_row = $deal_result->fetch_assoc();
- $deal_customers = $deal_row['deal_count'];
-
- // 获取有订单的客户数
- $order_sql = "SELECT COUNT(DISTINCT customer_id) as order_count FROM orders WHERE order_date BETWEEN ? AND ?";
- $order_stmt = $conn->prepare($order_sql);
- $order_stmt->bind_param("ss", $start_date, $end_date);
- $order_stmt->execute();
- $order_result = $order_stmt->get_result();
- $order_row = $order_result->fetch_assoc();
- $order_customers = $order_row['order_count'];
-
- // 获取复购客户数(多次下单)
- $repeat_sql = "SELECT COUNT(customer_id) as repeat_count FROM (
- SELECT customer_id, COUNT(id) as order_count
- FROM orders
- WHERE order_date BETWEEN ? AND ?
- GROUP BY customer_id
- HAVING order_count > 1
- ) as repeat_customers";
- $repeat_stmt = $conn->prepare($repeat_sql);
- $repeat_stmt->bind_param("ss", $start_date, $end_date);
- $repeat_stmt->execute();
- $repeat_result = $repeat_stmt->get_result();
- $repeat_row = $repeat_result->fetch_assoc();
- $repeat_customers = $repeat_row['repeat_count'];
-
- return [
- 'stages' => ['潜在客户', '明确需求', '已成交', '有效订单', '复购客户'],
- 'counts' => [$total_customers, $needs_customers, $deal_customers, $order_customers, $repeat_customers]
- ];
- }
- /**
- * 渲染客户转化漏斗图表
- *
- * @param array $funnel_data 客户转化漏斗数据
- * @return void
- */
- function renderCustomerFunnelChart($funnel_data) {
- ?>
- <div class="chart-container">
- <div class="chart-header">
- <h2 class="chart-title">客户转化漏斗</h2>
- </div>
- <canvas id="customerFunnelChart" style="max-height: 400px;"></canvas>
-
- <div class="customer-stats-summary">
- <div class="stats-row">
- <?php
- foreach ($funnel_data['stages'] as $index => $stage):
- $current_count = $funnel_data['counts'][$index];
- $prev_count = $index > 0 ? $funnel_data['counts'][$index-1] : $current_count;
- $conversion_rate = $prev_count > 0 ? ($current_count / $prev_count) * 100 : 0;
- ?>
- <div class="stat-item">
- <span class="stat-label"><?php echo $stage; ?>:</span>
- <span class="stat-value"><?php echo number_format($current_count); ?></span>
- <?php if ($index > 0): ?>
- <span class="stat-conversion">
- 转化率: <?php echo number_format($conversion_rate, 1); ?>%
- </span>
- <?php endif; ?>
- </div>
- <?php endforeach; ?>
- </div>
- </div>
- </div>
-
- <script>
- // 客户转化漏斗图
- var funnelCtx = document.getElementById('customerFunnelChart').getContext('2d');
- var funnelChart = new Chart(funnelCtx, {
- type: 'bar',
- data: {
- labels: <?php echo json_encode($funnel_data['stages']); ?>,
- datasets: [{
- label: '客户数量',
- data: <?php echo json_encode($funnel_data['counts']); ?>,
- backgroundColor: [
- 'rgba(54, 162, 235, 0.7)',
- 'rgba(75, 192, 192, 0.7)',
- 'rgba(255, 206, 86, 0.7)',
- 'rgba(255, 99, 132, 0.7)',
- 'rgba(153, 102, 255, 0.7)'
- ],
- borderWidth: 1
- }]
- },
- options: {
- indexAxis: 'y',
- responsive: true,
- scales: {
- x: {
- beginAtZero: true,
- title: {
- display: true,
- text: '客户数量'
- }
- }
- },
- plugins: {
- tooltip: {
- callbacks: {
- afterLabel: function(context) {
- var index = context.dataIndex;
- if (index > 0) {
- var currentValue = context.parsed.x;
- var previousValue = context.dataset.data[index-1];
- var conversionRate = previousValue > 0 ? (currentValue / previousValue * 100).toFixed(1) : 0;
- return '转化率: ' + conversionRate + '%';
- }
- return '';
- }
- }
- }
- }
- }
- });
- </script>
- <?php
- }
|