statistics.php 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009
  1. <?php
  2. require_once 'conn.php';
  3. checkLogin();
  4. // 计算日期范围
  5. $current_month_start = date('Y-m-01');
  6. $current_month_end = date('Y-m-t');
  7. $last_month_start = date('Y-m-01', strtotime('-1 month'));
  8. $last_month_end = date('Y-m-t', strtotime('-1 month'));
  9. $current_year_start = date('Y-01-01');
  10. $current_year_end = date('Y-12-31');
  11. // 可选的日期范围筛选
  12. $date_range = isset($_GET['date_range']) ? $_GET['date_range'] : 'current_month';
  13. $custom_start = isset($_GET['start_date']) ? $_GET['start_date'] : '';
  14. $custom_end = isset($_GET['end_date']) ? $_GET['end_date'] : '';
  15. // 设置日期范围
  16. if ($date_range == 'custom' && !empty($custom_start) && !empty($custom_end)) {
  17. $start_date = $custom_start;
  18. $end_date = $custom_end;
  19. } else {
  20. switch ($date_range) {
  21. case 'last_month':
  22. $start_date = $last_month_start;
  23. $end_date = $last_month_end;
  24. break;
  25. case 'current_year':
  26. $start_date = $current_year_start;
  27. $end_date = $current_year_end;
  28. break;
  29. case 'last_30_days':
  30. $start_date = date('Y-m-d', strtotime('-30 days'));
  31. $end_date = date('Y-m-d');
  32. break;
  33. case 'last_90_days':
  34. $start_date = date('Y-m-d', strtotime('-90 days'));
  35. $end_date = date('Y-m-d');
  36. break;
  37. case 'current_month':
  38. default:
  39. $start_date = $current_month_start;
  40. $end_date = $current_month_end;
  41. break;
  42. }
  43. }
  44. // 格式化日期用于SQL查询
  45. $start_date_sql = date('Y-m-d', strtotime($start_date));
  46. $end_date_sql = date('Y-m-d', strtotime($end_date)) . ' 23:59:59';
  47. // 函数:获取销售概览数据
  48. function getSalesOverview($conn, $start_date, $end_date) {
  49. $sql = "SELECT
  50. COUNT(id) as total_orders,
  51. SUM(total_amount) as total_revenue,
  52. AVG(total_amount) as avg_order_value
  53. FROM orders
  54. WHERE order_date BETWEEN ? AND ?";
  55. $stmt = $conn->prepare($sql);
  56. $stmt->bind_param("ss", $start_date, $end_date);
  57. $stmt->execute();
  58. $result = $stmt->get_result();
  59. return $result->fetch_assoc();
  60. }
  61. // 函数:获取每月销售趋势
  62. function getMonthlySalesTrend($conn, $start_date, $end_date) {
  63. $sql = "SELECT
  64. DATE_FORMAT(order_date, '%Y-%m') as month,
  65. COUNT(id) as orders,
  66. SUM(total_amount) as revenue
  67. FROM orders
  68. WHERE order_date BETWEEN ? AND ?
  69. GROUP BY DATE_FORMAT(order_date, '%Y-%m')
  70. ORDER BY month";
  71. $stmt = $conn->prepare($sql);
  72. $stmt->bind_param("ss", $start_date, $end_date);
  73. $stmt->execute();
  74. return $stmt->get_result();
  75. }
  76. // 函数:获取客户国家分布
  77. function getCustomerCountryDistribution($conn) {
  78. $sql = "SELECT
  79. c.countryName,
  80. COUNT(cu.id) as customer_count
  81. FROM customer cu
  82. JOIN country c ON cu.cs_country = c.id
  83. GROUP BY cu.cs_country
  84. ORDER BY customer_count DESC
  85. LIMIT 10";
  86. return $conn->query($sql);
  87. }
  88. // 函数:获取客户类型分布
  89. function getCustomerTypeDistribution($conn) {
  90. $sql = "SELECT
  91. ct.businessType,
  92. COUNT(c.id) as customer_count
  93. FROM customer c
  94. JOIN clienttype ct ON c.cs_type = ct.id
  95. GROUP BY c.cs_type";
  96. return $conn->query($sql);
  97. }
  98. // 函数:获取成交阶段分布
  99. function getDealStageDistribution($conn) {
  100. $sql = "SELECT
  101. cs_deal,
  102. CASE
  103. WHEN cs_deal = 1 THEN '背景调查'
  104. WHEN cs_deal = 2 THEN '明确需求'
  105. WHEN cs_deal = 3 THEN '已成交'
  106. ELSE '其他'
  107. END as stage_name,
  108. COUNT(id) as customer_count
  109. FROM customer
  110. GROUP BY cs_deal";
  111. return $conn->query($sql);
  112. }
  113. // 函数:获取热门产品
  114. function getTopProducts($conn, $start_date, $end_date, $limit = 5) {
  115. $sql = "SELECT
  116. p.ProductName,
  117. SUM(oi.quantity) as total_quantity,
  118. SUM(oi.total_price) as total_revenue
  119. FROM order_items oi
  120. JOIN products p ON oi.product_id = p.id
  121. JOIN orders o ON oi.order_id = o.id
  122. WHERE o.order_date BETWEEN ? AND ?
  123. GROUP BY oi.product_id
  124. ORDER BY total_revenue DESC
  125. LIMIT ?";
  126. $stmt = $conn->prepare($sql);
  127. $stmt->bind_param("ssi", $start_date, $end_date, $limit);
  128. $stmt->execute();
  129. return $stmt->get_result();
  130. }
  131. // 函数:获取业务员销售业绩
  132. function getEmployeeSalesPerformance($conn, $start_date, $end_date) {
  133. $sql = "SELECT
  134. e.em_user as employee_name,
  135. COUNT(o.id) as order_count,
  136. SUM(o.total_amount) as total_sales
  137. FROM orders o
  138. JOIN employee e ON o.employee_id = e.id
  139. WHERE o.order_date BETWEEN ? AND ?
  140. GROUP BY o.employee_id
  141. ORDER BY total_sales DESC";
  142. $stmt = $conn->prepare($sql);
  143. $stmt->bind_param("ss", $start_date, $end_date);
  144. $stmt->execute();
  145. return $stmt->get_result();
  146. }
  147. // 函数:获取客户增长趋势
  148. function getCustomerGrowthTrend($conn) {
  149. $sql = "SELECT
  150. DATE_FORMAT(cs_addtime, '%Y-%m') as month,
  151. COUNT(id) as new_customers
  152. FROM customer
  153. WHERE cs_addtime >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)
  154. GROUP BY DATE_FORMAT(cs_addtime, '%Y-%m')
  155. ORDER BY month";
  156. return $conn->query($sql);
  157. }
  158. // 函数:获取不同地区的订单数量
  159. function getOrdersByRegion($conn, $start_date, $end_date) {
  160. $sql = "SELECT
  161. c.countryName,
  162. COUNT(o.id) as order_count,
  163. SUM(o.total_amount) as total_amount,
  164. SUM(oi.quantity) as total_quantity
  165. FROM orders o
  166. JOIN customer cu ON o.customer_id = cu.id
  167. JOIN country c ON cu.cs_country = c.id
  168. LEFT JOIN order_items oi ON o.id = oi.order_id
  169. WHERE o.order_date BETWEEN ? AND ?
  170. GROUP BY cu.cs_country
  171. ORDER BY total_quantity DESC
  172. LIMIT 10";
  173. $stmt = $conn->prepare($sql);
  174. $stmt->bind_param("ss", $start_date, $end_date);
  175. $stmt->execute();
  176. return $stmt->get_result();
  177. }
  178. // 函数:获取详细时间段订单数量
  179. function getDetailedOrderTrend($conn, $start_date, $end_date, $period = 'day') {
  180. $groupFormat = '%Y-%m-%d';
  181. $intervalUnit = 'DAY';
  182. if ($period == 'week') {
  183. $groupFormat = '%x-W%v'; // ISO year and week number
  184. $intervalUnit = 'WEEK';
  185. } else if ($period == 'month') {
  186. $groupFormat = '%Y-%m';
  187. $intervalUnit = 'MONTH';
  188. }
  189. $sql = "SELECT
  190. DATE_FORMAT(o.order_date, '$groupFormat') as time_period,
  191. COUNT(o.id) as order_count,
  192. SUM(oi.quantity) as total_quantity
  193. FROM orders o
  194. LEFT JOIN order_items oi ON o.id = oi.order_id
  195. WHERE o.order_date BETWEEN ? AND ?
  196. GROUP BY time_period
  197. ORDER BY MIN(o.order_date)";
  198. $stmt = $conn->prepare($sql);
  199. $stmt->bind_param("ss", $start_date, $end_date);
  200. $stmt->execute();
  201. return $stmt->get_result();
  202. }
  203. // 获取统计数据
  204. $sales_overview = getSalesOverview($conn, $start_date_sql, $end_date_sql);
  205. $monthly_sales = getMonthlySalesTrend($conn, $start_date_sql, $end_date_sql);
  206. $country_distribution = getCustomerCountryDistribution($conn);
  207. $customer_types = getCustomerTypeDistribution($conn);
  208. $deal_stages = getDealStageDistribution($conn);
  209. $top_products = getTopProducts($conn, $start_date_sql, $end_date_sql);
  210. $employee_performance = getEmployeeSalesPerformance($conn, $start_date_sql, $end_date_sql);
  211. $customer_growth = getCustomerGrowthTrend($conn);
  212. $orders_by_region = getOrdersByRegion($conn, $start_date_sql, $end_date_sql);
  213. // 获取详细时间段订单趋势 - 默认按日期
  214. $period = isset($_GET['period']) ? $_GET['period'] : 'day';
  215. $detailed_orders = getDetailedOrderTrend($conn, $start_date_sql, $end_date_sql, $period);
  216. // 将月度销售数据转换为图表所需格式
  217. $monthly_labels = [];
  218. $monthly_orders = [];
  219. $monthly_revenue = [];
  220. while ($row = $monthly_sales->fetch_assoc()) {
  221. $monthly_labels[] = $row['month'];
  222. $monthly_orders[] = $row['orders'];
  223. $monthly_revenue[] = $row['revenue'];
  224. }
  225. // 将国家分布数据转换为图表所需格式
  226. $country_labels = [];
  227. $country_data = [];
  228. while ($row = $country_distribution->fetch_assoc()) {
  229. $country_labels[] = $row['countryName'];
  230. $country_data[] = $row['customer_count'];
  231. }
  232. // 将客户类型数据转换为图表所需格式
  233. $type_labels = [];
  234. $type_data = [];
  235. while ($row = $customer_types->fetch_assoc()) {
  236. $type_labels[] = $row['businessType'];
  237. $type_data[] = $row['customer_count'];
  238. }
  239. // 将成交阶段数据转换为图表所需格式
  240. $stage_labels = [];
  241. $stage_data = [];
  242. while ($row = $deal_stages->fetch_assoc()) {
  243. $stage_labels[] = $row['stage_name'];
  244. $stage_data[] = $row['customer_count'];
  245. }
  246. // 将客户增长数据转换为图表所需格式
  247. $growth_labels = [];
  248. $growth_data = [];
  249. while ($row = $customer_growth->fetch_assoc()) {
  250. $growth_labels[] = $row['month'];
  251. $growth_data[] = $row['new_customers'];
  252. }
  253. // 将地区订单数据转换为图表所需格式
  254. $region_labels = [];
  255. $region_orders = [];
  256. $region_quantities = [];
  257. while ($row = $orders_by_region->fetch_assoc()) {
  258. $region_labels[] = $row['countryName'];
  259. $region_orders[] = $row['order_count'];
  260. $region_quantities[] = $row['total_quantity'];
  261. }
  262. // 将详细时间订单数据转换为图表所需格式
  263. $time_labels = [];
  264. $time_orders = [];
  265. $time_quantities = [];
  266. while ($row = $detailed_orders->fetch_assoc()) {
  267. $time_labels[] = $row['time_period'];
  268. $time_orders[] = $row['order_count'];
  269. $time_quantities[] = $row['total_quantity'];
  270. }
  271. ?>
  272. <!DOCTYPE html>
  273. <html xmlns="http://www.w3.org/1999/xhtml">
  274. <head>
  275. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  276. <title>统计分析</title>
  277. <link rel="stylesheet" href="css/common.css" type="text/css" />
  278. <script src="system/js/jquery-1.7.2.min.js"></script>
  279. <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
  280. <style>
  281. body {
  282. margin: 0;
  283. padding: 20px;
  284. background: #fff;
  285. font-family: Arial, sans-serif;
  286. }
  287. .container {
  288. width: 100%;
  289. max-width: 1200px;
  290. margin: 0 auto;
  291. }
  292. .page-header {
  293. margin-bottom: 20px;
  294. border-bottom: 1px solid #eee;
  295. padding-bottom: 10px;
  296. display: flex;
  297. justify-content: space-between;
  298. align-items: center;
  299. }
  300. .page-title {
  301. font-size: 24px;
  302. font-weight: bold;
  303. color: #333;
  304. margin: 0;
  305. }
  306. .export-btn {
  307. padding: 8px 15px;
  308. background: #4CAF50;
  309. color: white;
  310. border: none;
  311. border-radius: 4px;
  312. cursor: pointer;
  313. text-decoration: none;
  314. display: inline-block;
  315. }
  316. .export-btn:hover {
  317. background: #45a049;
  318. }
  319. .filter-form {
  320. background: #f9f9f9;
  321. padding: 15px;
  322. border-radius: 5px;
  323. margin-bottom: 20px;
  324. display: flex;
  325. flex-wrap: wrap;
  326. gap: 15px;
  327. align-items: center;
  328. }
  329. .form-group {
  330. margin-right: 15px;
  331. }
  332. .form-group label {
  333. display: block;
  334. margin-bottom: 5px;
  335. font-weight: bold;
  336. }
  337. .form-control {
  338. padding: 8px;
  339. border: 1px solid #ddd;
  340. border-radius: 4px;
  341. min-width: 150px;
  342. }
  343. .btn {
  344. padding: 8px 15px;
  345. background: #337ab7;
  346. color: white;
  347. border: none;
  348. border-radius: 4px;
  349. cursor: pointer;
  350. }
  351. .btn:hover {
  352. background: #286090;
  353. }
  354. .stats-grid {
  355. display: grid;
  356. grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
  357. gap: 20px;
  358. margin-bottom: 20px;
  359. }
  360. .stat-card {
  361. background: white;
  362. border-radius: 5px;
  363. box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  364. padding: 20px;
  365. }
  366. .stat-card h3 {
  367. margin-top: 0;
  368. color: #555;
  369. font-size: 16px;
  370. border-bottom: 1px solid #eee;
  371. padding-bottom: 10px;
  372. }
  373. .stat-value {
  374. font-size: 24px;
  375. font-weight: bold;
  376. color: #333;
  377. margin: 15px 0;
  378. }
  379. .chart-container {
  380. margin-bottom: 30px;
  381. background: white;
  382. border-radius: 5px;
  383. box-shadow: 0 2px 4px rgba(0,0,0,0.1);
  384. padding: 20px;
  385. }
  386. .chart-header {
  387. display: flex;
  388. justify-content: space-between;
  389. align-items: center;
  390. margin-bottom: 15px;
  391. }
  392. .chart-title {
  393. font-size: 18px;
  394. font-weight: bold;
  395. color: #333;
  396. margin: 0;
  397. }
  398. .data-table {
  399. width: 100%;
  400. border-collapse: collapse;
  401. margin-top: 20px;
  402. }
  403. .data-table th, .data-table td {
  404. padding: 10px;
  405. text-align: left;
  406. border-bottom: 1px solid #eee;
  407. }
  408. .data-table th {
  409. background: #f5f5f5;
  410. font-weight: bold;
  411. }
  412. .data-table tr:hover {
  413. background: #f9f9f9;
  414. }
  415. .custom-date-inputs {
  416. display: none;
  417. }
  418. #date_range[value="custom"]:checked ~ .custom-date-inputs {
  419. display: block;
  420. }
  421. .download-btn {
  422. padding: 5px 10px;
  423. background: #4CAF50;
  424. color: white;
  425. border: none;
  426. border-radius: 3px;
  427. font-size: 14px;
  428. cursor: pointer;
  429. }
  430. .download-btn:hover {
  431. background: #45a049;
  432. }
  433. /* 确保筛选表单元素在同一行显示 */
  434. .filter-form form {
  435. display: flex;
  436. flex-direction: row;
  437. align-items: flex-end;
  438. gap: 15px;
  439. flex-wrap: nowrap;
  440. width: 100%;
  441. }
  442. .filter-form .form-group {
  443. margin-right: 0;
  444. flex: 0 0 auto;
  445. white-space: nowrap;
  446. }
  447. .filter-form label {
  448. display: block;
  449. margin-bottom: 5px;
  450. font-weight: bold;
  451. }
  452. .filter-form #custom_dates {
  453. display: flex;
  454. flex-direction: row;
  455. gap: 10px;
  456. flex: 0 0 auto;
  457. }
  458. </style>
  459. </head>
  460. <body>
  461. <div class="container">
  462. <div class="page-header">
  463. <h1 class="page-title">统计分析</h1>
  464. <a href="export_statistics.php?date_range=<?php echo $date_range; ?>&start_date=<?php echo $custom_start; ?>&end_date=<?php echo $custom_end; ?>&period=<?php echo $period; ?>" class="export-btn">导出数据</a>
  465. </div>
  466. <div class="filter-form">
  467. <form method="get" action="">
  468. <div class="form-group">
  469. <label>日期范围</label>
  470. <select name="date_range" id="date_range" class="form-control" onchange="toggleCustomDates()">
  471. <option value="current_month" <?php echo $date_range == 'current_month' ? 'selected' : ''; ?>>本月</option>
  472. <option value="last_month" <?php echo $date_range == 'last_month' ? 'selected' : ''; ?>>上月</option>
  473. <option value="last_30_days" <?php echo $date_range == 'last_30_days' ? 'selected' : ''; ?>>过去30天</option>
  474. <option value="last_90_days" <?php echo $date_range == 'last_90_days' ? 'selected' : ''; ?>>过去90天</option>
  475. <option value="current_year" <?php echo $date_range == 'current_year' ? 'selected' : ''; ?>>今年</option>
  476. <option value="custom" <?php echo $date_range == 'custom' ? 'selected' : ''; ?>>自定义</option>
  477. </select>
  478. </div>
  479. <div id="custom_dates" style="display: <?php echo $date_range == 'custom' ? 'flex' : 'none'; ?>;">
  480. <div class="form-group">
  481. <label>开始日期</label>
  482. <input type="date" name="start_date" class="form-control" value="<?php echo $custom_start; ?>">
  483. </div>
  484. <div class="form-group">
  485. <label>结束日期</label>
  486. <input type="date" name="end_date" class="form-control" value="<?php echo $custom_end; ?>">
  487. </div>
  488. </div>
  489. <div class="form-group">
  490. <label>时间粒度</label>
  491. <select name="period" class="form-control">
  492. <option value="day" <?php echo $period == 'day' ? 'selected' : ''; ?>>按日</option>
  493. <option value="week" <?php echo $period == 'week' ? 'selected' : ''; ?>>按周</option>
  494. <option value="month" <?php echo $period == 'month' ? 'selected' : ''; ?>>按月</option>
  495. </select>
  496. </div>
  497. <div class="form-group">
  498. <input type="submit" class="btn" value="应用筛选">
  499. </div>
  500. </form>
  501. </div>
  502. <!-- 销售概览卡片 -->
  503. <div class="stats-grid">
  504. <div class="stat-card">
  505. <h3>总订单数</h3>
  506. <div class="stat-value"><?php echo number_format($sales_overview['total_orders']); ?></div>
  507. </div>
  508. <div class="stat-card">
  509. <h3>总收入</h3>
  510. <div class="stat-value">¥<?php echo number_format($sales_overview['total_revenue'], 2); ?></div>
  511. </div>
  512. <div class="stat-card">
  513. <h3>平均订单金额</h3>
  514. <div class="stat-value">¥<?php echo number_format($sales_overview['avg_order_value'], 2); ?></div>
  515. </div>
  516. </div>
  517. <!-- 月度销售趋势图 -->
  518. <div class="chart-container">
  519. <div class="chart-header">
  520. <h2 class="chart-title">销售趋势</h2>
  521. </div>
  522. <canvas id="salesTrendChart"></canvas>
  523. </div>
  524. <!-- 客户分布图 -->
  525. <div class="stats-grid">
  526. <div class="chart-container">
  527. <div class="chart-header">
  528. <h2 class="chart-title">客户国家分布</h2>
  529. </div>
  530. <canvas id="countryDistributionChart"></canvas>
  531. </div>
  532. <div class="chart-container">
  533. <div class="chart-header">
  534. <h2 class="chart-title">客户类型分布</h2>
  535. </div>
  536. <canvas id="customerTypeChart"></canvas>
  537. </div>
  538. </div>
  539. <div class="stats-grid">
  540. <div class="chart-container">
  541. <div class="chart-header">
  542. <h2 class="chart-title">成交阶段分布</h2>
  543. </div>
  544. <canvas id="dealStageChart"></canvas>
  545. </div>
  546. <div class="chart-container">
  547. <div class="chart-header">
  548. <h2 class="chart-title">客户增长趋势</h2>
  549. </div>
  550. <canvas id="customerGrowthChart"></canvas>
  551. </div>
  552. </div>
  553. <!-- 地区订单分析 -->
  554. <div class="chart-container">
  555. <div class="chart-header">
  556. <h2 class="chart-title">地区订单分析</h2>
  557. </div>
  558. <canvas id="regionOrdersChart"></canvas>
  559. </div>
  560. <!-- 详细时间段订单趋势 -->
  561. <div class="chart-container">
  562. <div class="chart-header">
  563. <h2 class="chart-title">详细订单趋势 (<?php echo $period == 'day' ? '日' : ($period == 'week' ? '周' : '月'); ?>)</h2>
  564. </div>
  565. <canvas id="detailedOrdersChart"></canvas>
  566. </div>
  567. <!-- 热门产品表格 -->
  568. <div class="chart-container">
  569. <div class="chart-header">
  570. <h2 class="chart-title">热门产品</h2>
  571. </div>
  572. <table class="data-table">
  573. <thead>
  574. <tr>
  575. <th>产品名称</th>
  576. <th>销售数量</th>
  577. <th>销售收入</th>
  578. </tr>
  579. </thead>
  580. <tbody>
  581. <?php while ($row = $top_products->fetch_assoc()): ?>
  582. <tr>
  583. <td><?php echo htmlspecialchars($row['ProductName']); ?></td>
  584. <td><?php echo number_format($row['total_quantity']); ?></td>
  585. <td>¥<?php echo number_format($row['total_revenue'], 2); ?></td>
  586. </tr>
  587. <?php endwhile; ?>
  588. </tbody>
  589. </table>
  590. </div>
  591. <!-- 业务员销售业绩表格 -->
  592. <div class="chart-container">
  593. <div class="chart-header">
  594. <h2 class="chart-title">业务员销售业绩</h2>
  595. </div>
  596. <table class="data-table">
  597. <thead>
  598. <tr>
  599. <th>业务员姓名</th>
  600. <th>订单数量</th>
  601. <th>销售总额</th>
  602. </tr>
  603. </thead>
  604. <tbody>
  605. <?php while ($row = $employee_performance->fetch_assoc()): ?>
  606. <tr>
  607. <td><?php echo htmlspecialchars($row['employee_name']); ?></td>
  608. <td><?php echo number_format($row['order_count']); ?></td>
  609. <td>¥<?php echo number_format($row['total_sales'], 2); ?></td>
  610. </tr>
  611. <?php endwhile; ?>
  612. </tbody>
  613. </table>
  614. </div>
  615. </div>
  616. <script>
  617. // 切换自定义日期区域显示
  618. function toggleCustomDates() {
  619. var dateRange = document.getElementById('date_range').value;
  620. var customDates = document.getElementById('custom_dates');
  621. if (dateRange === 'custom') {
  622. customDates.style.display = 'flex';
  623. } else {
  624. customDates.style.display = 'none';
  625. }
  626. }
  627. // 销售趋势图
  628. var salesTrendCtx = document.getElementById('salesTrendChart').getContext('2d');
  629. var salesTrendChart = new Chart(salesTrendCtx, {
  630. type: 'line',
  631. data: {
  632. labels: <?php echo json_encode($monthly_labels); ?>,
  633. datasets: [
  634. {
  635. label: '订单数量',
  636. data: <?php echo json_encode($monthly_orders); ?>,
  637. backgroundColor: 'rgba(54, 162, 235, 0.2)',
  638. borderColor: 'rgba(54, 162, 235, 1)',
  639. borderWidth: 2,
  640. yAxisID: 'y-orders'
  641. },
  642. {
  643. label: '销售收入',
  644. data: <?php echo json_encode($monthly_revenue); ?>,
  645. backgroundColor: 'rgba(255, 99, 132, 0.2)',
  646. borderColor: 'rgba(255, 99, 132, 1)',
  647. borderWidth: 2,
  648. yAxisID: 'y-revenue'
  649. }
  650. ]
  651. },
  652. options: {
  653. responsive: true,
  654. scales: {
  655. 'y-orders': {
  656. type: 'linear',
  657. position: 'left',
  658. title: {
  659. display: true,
  660. text: '订单数量'
  661. }
  662. },
  663. 'y-revenue': {
  664. type: 'linear',
  665. position: 'right',
  666. title: {
  667. display: true,
  668. text: '销售收入'
  669. },
  670. grid: {
  671. drawOnChartArea: false
  672. }
  673. }
  674. }
  675. }
  676. });
  677. // 客户国家分布图
  678. var countryDistributionCtx = document.getElementById('countryDistributionChart').getContext('2d');
  679. var countryDistributionChart = new Chart(countryDistributionCtx, {
  680. type: 'pie',
  681. data: {
  682. labels: <?php echo json_encode($country_labels); ?>,
  683. datasets: [{
  684. data: <?php echo json_encode($country_data); ?>,
  685. backgroundColor: [
  686. 'rgba(255, 99, 132, 0.7)',
  687. 'rgba(54, 162, 235, 0.7)',
  688. 'rgba(255, 206, 86, 0.7)',
  689. 'rgba(75, 192, 192, 0.7)',
  690. 'rgba(153, 102, 255, 0.7)',
  691. 'rgba(255, 159, 64, 0.7)',
  692. 'rgba(199, 199, 199, 0.7)',
  693. 'rgba(83, 102, 255, 0.7)',
  694. 'rgba(40, 159, 64, 0.7)',
  695. 'rgba(210, 199, 199, 0.7)'
  696. ],
  697. borderWidth: 1
  698. }]
  699. },
  700. options: {
  701. responsive: true,
  702. plugins: {
  703. legend: {
  704. position: 'right',
  705. }
  706. }
  707. }
  708. });
  709. // 客户类型分布图
  710. var customerTypeCtx = document.getElementById('customerTypeChart').getContext('2d');
  711. var customerTypeChart = new Chart(customerTypeCtx, {
  712. type: 'doughnut',
  713. data: {
  714. labels: <?php echo json_encode($type_labels); ?>,
  715. datasets: [{
  716. data: <?php echo json_encode($type_data); ?>,
  717. backgroundColor: [
  718. 'rgba(54, 162, 235, 0.7)',
  719. 'rgba(255, 99, 132, 0.7)',
  720. 'rgba(255, 206, 86, 0.7)',
  721. 'rgba(75, 192, 192, 0.7)',
  722. 'rgba(153, 102, 255, 0.7)'
  723. ],
  724. borderWidth: 1
  725. }]
  726. },
  727. options: {
  728. responsive: true,
  729. plugins: {
  730. legend: {
  731. position: 'right',
  732. }
  733. }
  734. }
  735. });
  736. // 成交阶段分布图
  737. var dealStageCtx = document.getElementById('dealStageChart').getContext('2d');
  738. var dealStageChart = new Chart(dealStageCtx, {
  739. type: 'bar',
  740. data: {
  741. labels: <?php echo json_encode($stage_labels); ?>,
  742. datasets: [{
  743. label: '客户数量',
  744. data: <?php echo json_encode($stage_data); ?>,
  745. backgroundColor: [
  746. 'rgba(255, 206, 86, 0.7)',
  747. 'rgba(54, 162, 235, 0.7)',
  748. 'rgba(255, 99, 132, 0.7)'
  749. ],
  750. borderWidth: 1
  751. }]
  752. },
  753. options: {
  754. responsive: true,
  755. scales: {
  756. y: {
  757. beginAtZero: true,
  758. title: {
  759. display: true,
  760. text: '客户数量'
  761. }
  762. }
  763. }
  764. }
  765. });
  766. // 客户增长趋势图
  767. var customerGrowthCtx = document.getElementById('customerGrowthChart').getContext('2d');
  768. var customerGrowthChart = new Chart(customerGrowthCtx, {
  769. type: 'line',
  770. data: {
  771. labels: <?php echo json_encode($growth_labels); ?>,
  772. datasets: [{
  773. label: '新增客户',
  774. data: <?php echo json_encode($growth_data); ?>,
  775. backgroundColor: 'rgba(75, 192, 192, 0.2)',
  776. borderColor: 'rgba(75, 192, 192, 1)',
  777. borderWidth: 2,
  778. tension: 0.1
  779. }]
  780. },
  781. options: {
  782. responsive: true,
  783. scales: {
  784. y: {
  785. beginAtZero: true,
  786. title: {
  787. display: true,
  788. text: '客户数量'
  789. }
  790. }
  791. }
  792. }
  793. });
  794. // 地区订单分析图
  795. var regionOrdersCtx = document.getElementById('regionOrdersChart').getContext('2d');
  796. var regionOrdersChart = new Chart(regionOrdersCtx, {
  797. type: 'bar',
  798. data: {
  799. labels: <?php echo json_encode($region_labels); ?>,
  800. datasets: [
  801. {
  802. label: '订单数量',
  803. data: <?php echo json_encode($region_orders); ?>,
  804. backgroundColor: 'rgba(54, 162, 235, 0.6)',
  805. borderColor: 'rgba(54, 162, 235, 1)',
  806. borderWidth: 1,
  807. yAxisID: 'y-orders'
  808. },
  809. {
  810. label: '产品订购数量',
  811. data: <?php echo json_encode($region_quantities); ?>,
  812. backgroundColor: 'rgba(255, 99, 132, 0.6)',
  813. borderColor: 'rgba(255, 99, 132, 1)',
  814. borderWidth: 1,
  815. yAxisID: 'y-quantity'
  816. }
  817. ]
  818. },
  819. options: {
  820. responsive: true,
  821. scales: {
  822. x: {
  823. title: {
  824. display: true,
  825. text: '地区'
  826. }
  827. },
  828. 'y-orders': {
  829. type: 'linear',
  830. position: 'left',
  831. title: {
  832. display: true,
  833. text: '订单数量'
  834. },
  835. beginAtZero: true
  836. },
  837. 'y-quantity': {
  838. type: 'linear',
  839. position: 'right',
  840. title: {
  841. display: true,
  842. text: '产品订购数量'
  843. },
  844. beginAtZero: true,
  845. grid: {
  846. drawOnChartArea: false
  847. }
  848. }
  849. }
  850. }
  851. });
  852. // 详细时间段订单趋势图
  853. var detailedOrdersCtx = document.getElementById('detailedOrdersChart').getContext('2d');
  854. var detailedOrdersChart = new Chart(detailedOrdersCtx, {
  855. type: 'line',
  856. data: {
  857. labels: <?php echo json_encode($time_labels); ?>,
  858. datasets: [
  859. {
  860. label: '订单数量',
  861. data: <?php echo json_encode($time_orders); ?>,
  862. backgroundColor: 'rgba(75, 192, 192, 0.2)',
  863. borderColor: 'rgba(75, 192, 192, 1)',
  864. borderWidth: 2,
  865. yAxisID: 'y-orders',
  866. tension: 0.1
  867. },
  868. {
  869. label: '产品订购数量',
  870. data: <?php echo json_encode($time_quantities); ?>,
  871. backgroundColor: 'rgba(255, 159, 64, 0.2)',
  872. borderColor: 'rgba(255, 159, 64, 1)',
  873. borderWidth: 2,
  874. yAxisID: 'y-quantity',
  875. tension: 0.1
  876. }
  877. ]
  878. },
  879. options: {
  880. responsive: true,
  881. scales: {
  882. x: {
  883. title: {
  884. display: true,
  885. text: '时间'
  886. }
  887. },
  888. 'y-orders': {
  889. type: 'linear',
  890. position: 'left',
  891. title: {
  892. display: true,
  893. text: '订单数量'
  894. },
  895. beginAtZero: true
  896. },
  897. 'y-quantity': {
  898. type: 'linear',
  899. position: 'right',
  900. title: {
  901. display: true,
  902. text: '产品订购数量'
  903. },
  904. beginAtZero: true,
  905. grid: {
  906. drawOnChartArea: false
  907. }
  908. }
  909. }
  910. }
  911. });
  912. </script>
  913. </body>
  914. </html>