statistics_sales.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. <?php
  2. /**
  3. * 销售统计分析模块
  4. *
  5. * 包含与销售相关的数据分析功能
  6. */
  7. require_once 'statistics_utils.php';
  8. /**
  9. * 获取销售概览数据
  10. *
  11. * @param mysqli $conn 数据库连接
  12. * @param string $start_date 开始日期
  13. * @param string $end_date 结束日期
  14. * @return array 销售概览数据
  15. */
  16. function getSalesOverview($conn, $start_date, $end_date) {
  17. $sql = "SELECT
  18. COUNT(DISTINCT o.id) as total_orders,
  19. SUM(o.total_amount) as total_revenue,
  20. AVG(o.total_amount) as avg_order_value,
  21. COUNT(DISTINCT o.customer_id) as unique_customers,
  22. SUM(oi.quantity) as total_items_sold
  23. FROM orders o
  24. LEFT JOIN order_items oi ON o.id = oi.order_id
  25. WHERE o.order_date BETWEEN ? AND ?
  26. AND o.order_status != 0"; // 排除已取消订单
  27. $stmt = $conn->prepare($sql);
  28. $stmt->bind_param("ss", $start_date, $end_date);
  29. $stmt->execute();
  30. return $stmt->get_result()->fetch_assoc();
  31. }
  32. /**
  33. * 获取订单转化率统计
  34. */
  35. function getOrderConversionStats($conn, $start_date, $end_date) {
  36. $sql = "SELECT
  37. order_status,
  38. COUNT(*) as count,
  39. SUM(total_amount) as amount
  40. FROM orders
  41. WHERE order_date BETWEEN ? AND ?
  42. GROUP BY order_status";
  43. $stmt = $conn->prepare($sql);
  44. $stmt->bind_param("ss", $start_date, $end_date);
  45. $stmt->execute();
  46. return $stmt->get_result();
  47. }
  48. /**
  49. * 获取产品类别销售统计
  50. */
  51. function getProductCategorySales($conn, $start_date, $end_date) {
  52. $sql = "SELECT
  53. pc.name as category_name,
  54. COUNT(DISTINCT o.id) as order_count,
  55. SUM(oi.quantity) as total_quantity,
  56. SUM(oi.total_price) as total_revenue
  57. FROM orders o
  58. JOIN order_items oi ON o.id = oi.order_id
  59. JOIN products p ON oi.product_id = p.id
  60. JOIN product_categories pc ON p.category_id = pc.id
  61. WHERE o.order_date BETWEEN ? AND ?
  62. AND o.order_status != 0
  63. GROUP BY pc.id
  64. ORDER BY total_revenue DESC";
  65. $stmt = $conn->prepare($sql);
  66. $stmt->bind_param("ss", $start_date, $end_date);
  67. $stmt->execute();
  68. return $stmt->get_result();
  69. }
  70. /**
  71. * 获取客户地区分布
  72. */
  73. function getCustomerDistribution($conn, $start_date, $end_date) {
  74. $sql = "SELECT
  75. c.countryName as region,
  76. COUNT(DISTINCT o.customer_id) as customer_count,
  77. COUNT(o.id) as order_count,
  78. SUM(o.total_amount) as total_revenue
  79. FROM orders o
  80. JOIN customer cu ON o.customer_id = cu.id
  81. JOIN country c ON cu.cs_country = c.id
  82. WHERE o.order_date BETWEEN ? AND ?
  83. AND o.order_status != 0
  84. GROUP BY c.id
  85. ORDER BY total_revenue DESC";
  86. $stmt = $conn->prepare($sql);
  87. $stmt->bind_param("ss", $start_date, $end_date);
  88. $stmt->execute();
  89. return $stmt->get_result();
  90. }
  91. /**
  92. * 获取销售员业绩统计
  93. */
  94. function getEmployeePerformance($conn, $start_date, $end_date) {
  95. $sql = "SELECT
  96. e.em_user as employee_name,
  97. COUNT(DISTINCT o.id) as order_count,
  98. COUNT(DISTINCT o.customer_id) as customer_count,
  99. SUM(o.total_amount) as total_revenue,
  100. AVG(o.total_amount) as avg_order_value
  101. FROM orders o
  102. JOIN employee e ON o.employee_id = e.id
  103. WHERE o.order_date BETWEEN ? AND ?
  104. AND o.order_status != 0
  105. GROUP BY e.id
  106. ORDER BY total_revenue DESC";
  107. $stmt = $conn->prepare($sql);
  108. $stmt->bind_param("ss", $start_date, $end_date);
  109. $stmt->execute();
  110. return $stmt->get_result();
  111. }
  112. /**
  113. * 获取支付状态统计
  114. */
  115. function getPaymentStatusStats($conn, $start_date, $end_date) {
  116. $sql = "SELECT
  117. payment_status,
  118. COUNT(*) as count,
  119. SUM(total_amount) as amount
  120. FROM orders
  121. WHERE order_date BETWEEN ? AND ?
  122. AND order_status != 0
  123. GROUP BY payment_status";
  124. $stmt = $conn->prepare($sql);
  125. $stmt->bind_param("ss", $start_date, $end_date);
  126. $stmt->execute();
  127. return $stmt->get_result();
  128. }
  129. /**
  130. * 获取每月销售趋势
  131. *
  132. * @param mysqli $conn 数据库连接
  133. * @param string $start_date 开始日期
  134. * @param string $end_date 结束日期
  135. * @return mysqli_result 月度销售数据结果集
  136. */
  137. function getMonthlySalesTrend($conn, $start_date, $end_date) {
  138. $sql = "SELECT
  139. DATE_FORMAT(order_date, '%Y-%m') as month,
  140. COUNT(DISTINCT id) as orders,
  141. SUM(total_amount) as revenue,
  142. COUNT(DISTINCT customer_id) as unique_customers
  143. FROM orders
  144. WHERE order_date BETWEEN ? AND ?
  145. AND order_status != 0
  146. GROUP BY DATE_FORMAT(order_date, '%Y-%m')
  147. ORDER BY month";
  148. $stmt = $conn->prepare($sql);
  149. $stmt->bind_param("ss", $start_date, $end_date);
  150. $stmt->execute();
  151. return $stmt->get_result();
  152. }
  153. /**
  154. * 获取详细时间段订单趋势
  155. *
  156. * @param mysqli $conn 数据库连接
  157. * @param string $start_date 开始日期
  158. * @param string $end_date 结束日期
  159. * @param string $period 时间粒度 (day/week/month)
  160. * @return mysqli_result 订单趋势数据结果集
  161. */
  162. function getDetailedOrderTrend($conn, $start_date, $end_date, $period = 'day') {
  163. $groupFormat = '%Y-%m-%d';
  164. if ($period == 'week') {
  165. $groupFormat = '%x-W%v';
  166. } else if ($period == 'month') {
  167. $groupFormat = '%Y-%m';
  168. }
  169. $sql = "SELECT
  170. DATE_FORMAT(o.order_date, '$groupFormat') as time_period,
  171. COUNT(DISTINCT o.id) as order_count,
  172. SUM(oi.quantity) as total_quantity,
  173. SUM(o.total_amount) as total_amount,
  174. COUNT(DISTINCT o.customer_id) as unique_customers
  175. FROM orders o
  176. LEFT JOIN order_items oi ON o.id = oi.order_id
  177. WHERE o.order_date BETWEEN ? AND ?
  178. AND o.order_status != 0
  179. GROUP BY time_period
  180. ORDER BY MIN(o.order_date)";
  181. $stmt = $conn->prepare($sql);
  182. $stmt->bind_param("ss", $start_date, $end_date);
  183. $stmt->execute();
  184. return $stmt->get_result();
  185. }
  186. /**
  187. * 渲染销售概览卡片
  188. *
  189. * @param array $sales_overview 销售概览数据
  190. * @return void
  191. */
  192. function renderSalesOverviewCards($sales_overview) {
  193. ?>
  194. <div class="stats-grid">
  195. <div class="stat-card">
  196. <h3>总订单数</h3>
  197. <div class="stat-value"><?php echo number_format($sales_overview['total_orders']); ?></div>
  198. </div>
  199. <div class="stat-card">
  200. <h3>总收入</h3>
  201. <div class="stat-value">¥<?php echo number_format($sales_overview['total_revenue'], 2); ?></div>
  202. </div>
  203. <div class="stat-card">
  204. <h3>平均订单金额</h3>
  205. <div class="stat-value">¥<?php echo number_format($sales_overview['avg_order_value'], 2); ?></div>
  206. </div>
  207. <div class="stat-card">
  208. <h3>独立客户数</h3>
  209. <div class="stat-value"><?php echo number_format($sales_overview['unique_customers']); ?></div>
  210. </div>
  211. <div class="stat-card">
  212. <h3>总销售数量</h3>
  213. <div class="stat-value"><?php echo number_format($sales_overview['total_items_sold']); ?></div>
  214. </div>
  215. </div>
  216. <?php
  217. }
  218. /**
  219. * 渲染订单转化率分析
  220. */
  221. function renderConversionAnalysis($conversion_stats) {
  222. $status_names = [
  223. 1 => '待确认',
  224. 2 => '已确认',
  225. 3 => '生产中',
  226. 4 => '已发货',
  227. 5 => '已完成',
  228. 0 => '已取消'
  229. ];
  230. $total_orders = 0;
  231. $data = [];
  232. while ($row = $conversion_stats->fetch_assoc()) {
  233. $total_orders += $row['count'];
  234. $data[$row['order_status']] = $row;
  235. }
  236. ?>
  237. <div class="analysis-grid">
  238. <div>
  239. <canvas id="orderStatusChart"></canvas>
  240. </div>
  241. <div class="table-responsive">
  242. <table class="data-table">
  243. <thead>
  244. <tr>
  245. <th>订单状态</th>
  246. <th>订单数</th>
  247. <th>转化率</th>
  248. <th>金额</th>
  249. </tr>
  250. </thead>
  251. <tbody>
  252. <?php foreach ($status_names as $status_id => $status_name): ?>
  253. <?php if (isset($data[$status_id])): ?>
  254. <tr>
  255. <td><?php echo $status_name; ?></td>
  256. <td><?php echo number_format($data[$status_id]['count']); ?></td>
  257. <td><?php echo number_format(($data[$status_id]['count'] / $total_orders) * 100, 1); ?>%</td>
  258. <td>¥<?php echo number_format($data[$status_id]['amount'], 2); ?></td>
  259. </tr>
  260. <?php endif; ?>
  261. <?php endforeach; ?>
  262. </tbody>
  263. </table>
  264. </div>
  265. </div>
  266. <script>
  267. var orderStatusCtx = document.getElementById('orderStatusChart').getContext('2d');
  268. new Chart(orderStatusCtx, {
  269. type: 'doughnut',
  270. data: {
  271. labels: <?php
  272. $labels = [];
  273. $values = [];
  274. foreach ($status_names as $status_id => $status_name) {
  275. if (isset($data[$status_id])) {
  276. $labels[] = $status_name;
  277. $values[] = $data[$status_id]['count'];
  278. }
  279. }
  280. echo json_encode($labels);
  281. ?>,
  282. datasets: [{
  283. data: <?php echo json_encode($values); ?>,
  284. backgroundColor: [
  285. '#FF6384',
  286. '#36A2EB',
  287. '#FFCE56',
  288. '#4BC0C0',
  289. '#9966FF',
  290. '#FF9F40'
  291. ]
  292. }]
  293. },
  294. options: {
  295. responsive: true,
  296. plugins: {
  297. legend: {
  298. position: 'right'
  299. },
  300. title: {
  301. display: true,
  302. text: '订单状态分布'
  303. }
  304. }
  305. }
  306. });
  307. </script>
  308. <?php
  309. }
  310. /**
  311. * 渲染产品类别销售分析图表
  312. */
  313. function renderCategorySalesChart($category_sales) {
  314. $labels = [];
  315. $quantities = [];
  316. $revenues = [];
  317. while ($row = $category_sales->fetch_assoc()) {
  318. $labels[] = $row['category_name'];
  319. $quantities[] = $row['total_quantity'];
  320. $revenues[] = $row['total_revenue'];
  321. }
  322. ?>
  323. <div class="analysis-grid">
  324. <div>
  325. <canvas id="categorySalesChart"></canvas>
  326. </div>
  327. <div class="table-responsive">
  328. <table class="data-table">
  329. <thead>
  330. <tr>
  331. <th>产品类别</th>
  332. <th>订单数</th>
  333. <th>销售数量</th>
  334. <th>销售金额</th>
  335. </tr>
  336. </thead>
  337. <tbody>
  338. <?php
  339. $category_sales->data_seek(0);
  340. while ($row = $category_sales->fetch_assoc()):
  341. ?>
  342. <tr>
  343. <td><?php echo $row['category_name']; ?></td>
  344. <td><?php echo number_format($row['order_count']); ?></td>
  345. <td><?php echo number_format($row['total_quantity']); ?></td>
  346. <td>¥<?php echo number_format($row['total_revenue'], 2); ?></td>
  347. </tr>
  348. <?php endwhile; ?>
  349. </tbody>
  350. </table>
  351. </div>
  352. </div>
  353. <script>
  354. var categorySalesCtx = document.getElementById('categorySalesChart').getContext('2d');
  355. new Chart(categorySalesCtx, {
  356. type: 'bar',
  357. data: {
  358. labels: <?php echo json_encode($labels); ?>,
  359. datasets: [
  360. {
  361. label: '销售数量',
  362. data: <?php echo json_encode($quantities); ?>,
  363. backgroundColor: 'rgba(54, 162, 235, 0.5)',
  364. borderColor: 'rgba(54, 162, 235, 1)',
  365. borderWidth: 1,
  366. yAxisID: 'y-quantity'
  367. },
  368. {
  369. label: '销售金额',
  370. data: <?php echo json_encode($revenues); ?>,
  371. backgroundColor: 'rgba(255, 99, 132, 0.5)',
  372. borderColor: 'rgba(255, 99, 132, 1)',
  373. borderWidth: 1,
  374. yAxisID: 'y-revenue'
  375. }
  376. ]
  377. },
  378. options: {
  379. responsive: true,
  380. scales: {
  381. 'y-quantity': {
  382. type: 'linear',
  383. position: 'left',
  384. title: {
  385. display: true,
  386. text: '销售数量'
  387. }
  388. },
  389. 'y-revenue': {
  390. type: 'linear',
  391. position: 'right',
  392. title: {
  393. display: true,
  394. text: '销售金额'
  395. },
  396. grid: {
  397. drawOnChartArea: false
  398. }
  399. }
  400. }
  401. }
  402. });
  403. </script>
  404. <?php
  405. }
  406. /**
  407. * 渲染客户地区分布图表
  408. */
  409. function renderCustomerDistributionChart($customer_distribution) {
  410. $regions = [];
  411. $customers = [];
  412. $revenues = [];
  413. while ($row = $customer_distribution->fetch_assoc()) {
  414. $regions[] = $row['region'];
  415. $customers[] = $row['customer_count'];
  416. $revenues[] = $row['total_revenue'];
  417. }
  418. ?>
  419. <div class="analysis-grid">
  420. <div>
  421. <canvas id="regionDistributionChart"></canvas>
  422. </div>
  423. <div class="table-responsive">
  424. <table class="data-table">
  425. <thead>
  426. <tr>
  427. <th>地区</th>
  428. <th>客户数</th>
  429. <th>订单数</th>
  430. <th>销售金额</th>
  431. </tr>
  432. </thead>
  433. <tbody>
  434. <?php
  435. $customer_distribution->data_seek(0);
  436. while ($row = $customer_distribution->fetch_assoc()):
  437. ?>
  438. <tr>
  439. <td><?php echo $row['region']; ?></td>
  440. <td><?php echo number_format($row['customer_count']); ?></td>
  441. <td><?php echo number_format($row['order_count']); ?></td>
  442. <td>¥<?php echo number_format($row['total_revenue'], 2); ?></td>
  443. </tr>
  444. <?php endwhile; ?>
  445. </tbody>
  446. </table>
  447. </div>
  448. </div>
  449. <script>
  450. var regionDistributionCtx = document.getElementById('regionDistributionChart').getContext('2d');
  451. new Chart(regionDistributionCtx, {
  452. type: 'bar',
  453. data: {
  454. labels: <?php echo json_encode($regions); ?>,
  455. datasets: [
  456. {
  457. label: '客户数',
  458. data: <?php echo json_encode($customers); ?>,
  459. backgroundColor: 'rgba(75, 192, 192, 0.5)',
  460. borderColor: 'rgba(75, 192, 192, 1)',
  461. borderWidth: 1,
  462. yAxisID: 'y-customers'
  463. },
  464. {
  465. label: '销售金额',
  466. data: <?php echo json_encode($revenues); ?>,
  467. backgroundColor: 'rgba(255, 159, 64, 0.5)',
  468. borderColor: 'rgba(255, 159, 64, 1)',
  469. borderWidth: 1,
  470. yAxisID: 'y-revenue'
  471. }
  472. ]
  473. },
  474. options: {
  475. responsive: true,
  476. scales: {
  477. 'y-customers': {
  478. type: 'linear',
  479. position: 'left',
  480. title: {
  481. display: true,
  482. text: '客户数'
  483. }
  484. },
  485. 'y-revenue': {
  486. type: 'linear',
  487. position: 'right',
  488. title: {
  489. display: true,
  490. text: '销售金额'
  491. },
  492. grid: {
  493. drawOnChartArea: false
  494. }
  495. }
  496. }
  497. }
  498. });
  499. </script>
  500. <?php
  501. }
  502. /**
  503. * 渲染销售员业绩表格
  504. */
  505. function renderEmployeePerformanceTable($employee_performance) {
  506. ?>
  507. <div class="table-responsive">
  508. <table class="data-table">
  509. <thead>
  510. <tr>
  511. <th>销售员</th>
  512. <th>订单数</th>
  513. <th>客户数</th>
  514. <th>总销售额</th>
  515. <th>平均订单金额</th>
  516. </tr>
  517. </thead>
  518. <tbody>
  519. <?php while ($row = $employee_performance->fetch_assoc()): ?>
  520. <tr>
  521. <td><?php echo $row['employee_name']; ?></td>
  522. <td><?php echo number_format($row['order_count']); ?></td>
  523. <td><?php echo number_format($row['customer_count']); ?></td>
  524. <td>¥<?php echo number_format($row['total_revenue'], 2); ?></td>
  525. <td>¥<?php echo number_format($row['avg_order_value'], 2); ?></td>
  526. </tr>
  527. <?php endwhile; ?>
  528. </tbody>
  529. </table>
  530. </div>
  531. <?php
  532. }
  533. /**
  534. * 渲染支付状态分析图表
  535. */
  536. function renderPaymentStatusChart($payment_stats) {
  537. $status_names = [
  538. 0 => '未付款',
  539. 1 => '部分付款',
  540. 2 => '已付清'
  541. ];
  542. $data = [];
  543. while ($row = $payment_stats->fetch_assoc()) {
  544. $data[$row['payment_status']] = $row;
  545. }
  546. ?>
  547. <div class="analysis-grid">
  548. <div>
  549. <canvas id="paymentStatusChart"></canvas>
  550. </div>
  551. <div class="table-responsive">
  552. <table class="data-table">
  553. <thead>
  554. <tr>
  555. <th>支付状态</th>
  556. <th>订单数</th>
  557. <th>订单金额</th>
  558. </tr>
  559. </thead>
  560. <tbody>
  561. <?php foreach ($status_names as $status_id => $status_name): ?>
  562. <?php if (isset($data[$status_id])): ?>
  563. <tr>
  564. <td><?php echo $status_name; ?></td>
  565. <td><?php echo number_format($data[$status_id]['count']); ?></td>
  566. <td>¥<?php echo number_format($data[$status_id]['amount'], 2); ?></td>
  567. </tr>
  568. <?php endif; ?>
  569. <?php endforeach; ?>
  570. </tbody>
  571. </table>
  572. </div>
  573. </div>
  574. <script>
  575. var paymentStatusCtx = document.getElementById('paymentStatusChart').getContext('2d');
  576. new Chart(paymentStatusCtx, {
  577. type: 'pie',
  578. data: {
  579. labels: <?php
  580. $labels = [];
  581. $values = [];
  582. foreach ($status_names as $status_id => $status_name) {
  583. if (isset($data[$status_id])) {
  584. $labels[] = $status_name;
  585. $values[] = $data[$status_id]['amount'];
  586. }
  587. }
  588. echo json_encode($labels);
  589. ?>,
  590. datasets: [{
  591. data: <?php echo json_encode($values); ?>,
  592. backgroundColor: [
  593. '#FF6384',
  594. '#36A2EB',
  595. '#4BC0C0'
  596. ]
  597. }]
  598. },
  599. options: {
  600. responsive: true,
  601. plugins: {
  602. legend: {
  603. position: 'right'
  604. },
  605. title: {
  606. display: true,
  607. text: '支付状态分布'
  608. }
  609. }
  610. }
  611. });
  612. </script>
  613. <?php
  614. }
  615. /**
  616. * 渲染月度销售趋势图
  617. *
  618. * @param array $monthly_labels 月份标签
  619. * @param array $monthly_orders 月度订单数量
  620. * @param array $monthly_revenue 月度收入
  621. * @return void
  622. */
  623. function renderMonthlySalesTrendChart($monthly_labels, $monthly_orders, $monthly_revenue) {
  624. ?>
  625. <div class="chart-container">
  626. <div class="chart-header">
  627. <h2 class="chart-title">销售趋势</h2>
  628. </div>
  629. <canvas id="salesTrendChart"></canvas>
  630. </div>
  631. <script>
  632. var salesTrendCtx = document.getElementById('salesTrendChart').getContext('2d');
  633. new Chart(salesTrendCtx, {
  634. type: 'line',
  635. data: {
  636. labels: <?php echo json_encode($monthly_labels); ?>,
  637. datasets: [
  638. {
  639. label: '订单数量',
  640. data: <?php echo json_encode($monthly_orders); ?>,
  641. backgroundColor: 'rgba(54, 162, 235, 0.2)',
  642. borderColor: 'rgba(54, 162, 235, 1)',
  643. borderWidth: 2,
  644. yAxisID: 'y-orders',
  645. tension: 0.1
  646. },
  647. {
  648. label: '销售收入',
  649. data: <?php echo json_encode($monthly_revenue); ?>,
  650. backgroundColor: 'rgba(255, 99, 132, 0.2)',
  651. borderColor: 'rgba(255, 99, 132, 1)',
  652. borderWidth: 2,
  653. yAxisID: 'y-revenue',
  654. tension: 0.1
  655. }
  656. ]
  657. },
  658. options: {
  659. responsive: true,
  660. scales: {
  661. 'y-orders': {
  662. type: 'linear',
  663. position: 'left',
  664. title: {
  665. display: true,
  666. text: '订单数量'
  667. }
  668. },
  669. 'y-revenue': {
  670. type: 'linear',
  671. position: 'right',
  672. title: {
  673. display: true,
  674. text: '销售收入'
  675. },
  676. grid: {
  677. drawOnChartArea: false
  678. }
  679. }
  680. }
  681. }
  682. });
  683. </script>
  684. <?php
  685. }
  686. /**
  687. * 渲染详细订单趋势图
  688. *
  689. * @param array $time_labels 时间标签
  690. * @param array $time_orders 时间段订单数量
  691. * @param array $time_quantities 时间段产品数量
  692. * @param string $period 时间粒度
  693. * @return void
  694. */
  695. function renderDetailedOrderTrendChart($time_labels, $time_orders, $time_quantities, $period = 'day') {
  696. $period_text = $period == 'day' ? '日' : ($period == 'week' ? '周' : '月');
  697. ?>
  698. <div class="chart-container">
  699. <div class="chart-header">
  700. <h2 class="chart-title">详细订单趋势 (<?php echo $period_text; ?>)</h2>
  701. </div>
  702. <canvas id="detailedOrdersChart"></canvas>
  703. </div>
  704. <script>
  705. var detailedOrdersCtx = document.getElementById('detailedOrdersChart').getContext('2d');
  706. new Chart(detailedOrdersCtx, {
  707. type: 'line',
  708. data: {
  709. labels: <?php echo json_encode($time_labels); ?>,
  710. datasets: [
  711. {
  712. label: '订单数量',
  713. data: <?php echo json_encode($time_orders); ?>,
  714. backgroundColor: 'rgba(75, 192, 192, 0.2)',
  715. borderColor: 'rgba(75, 192, 192, 1)',
  716. borderWidth: 2,
  717. yAxisID: 'y-orders',
  718. tension: 0.1
  719. },
  720. {
  721. label: '产品订购数量',
  722. data: <?php echo json_encode($time_quantities); ?>,
  723. backgroundColor: 'rgba(255, 159, 64, 0.2)',
  724. borderColor: 'rgba(255, 159, 64, 1)',
  725. borderWidth: 2,
  726. yAxisID: 'y-quantity',
  727. tension: 0.1
  728. }
  729. ]
  730. },
  731. options: {
  732. responsive: true,
  733. scales: {
  734. x: {
  735. title: {
  736. display: true,
  737. text: '时间'
  738. }
  739. },
  740. 'y-orders': {
  741. type: 'linear',
  742. position: 'left',
  743. title: {
  744. display: true,
  745. text: '订单数量'
  746. },
  747. beginAtZero: true
  748. },
  749. 'y-quantity': {
  750. type: 'linear',
  751. position: 'right',
  752. title: {
  753. display: true,
  754. text: '产品订购数量'
  755. },
  756. beginAtZero: true,
  757. grid: {
  758. drawOnChartArea: false
  759. }
  760. }
  761. }
  762. }
  763. });
  764. </script>
  765. <?php
  766. }