FarmMetricsController.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. <?php
  2. namespace App\Module\Farm\AdminControllers;
  3. use App\Module\Farm\Models\FarmDailyStats;
  4. use App\Module\Farm\AdminControllers\Metrics\FarmLandLevelStatsCard;
  5. use UCore\DcatAdmin\AdminController;
  6. use Dcat\Admin\Layout\Content;
  7. use Dcat\Admin\Widgets\Card;
  8. use Dcat\Admin\Admin;
  9. use Carbon\Carbon;
  10. /**
  11. * 农场统计图表控制器
  12. *
  13. * @AdminController(
  14. * title="农场统计图表",
  15. * permission="farm.metrics",
  16. * menu_title="统计图表",
  17. * menu_parent="农场管理",
  18. * menu_order=101
  19. * )
  20. */
  21. class FarmMetricsController extends AdminController
  22. {
  23. /**
  24. * 首页
  25. */
  26. public function index(Content $content)
  27. {
  28. return $content
  29. ->title('农场统计图表')
  30. ->description('农场模块数据统计分析')
  31. ->row(function ($row) {
  32. $row->column(6, new FarmLandLevelStatsCard());
  33. $row->column(6, $this->getLandStatsCards());
  34. })
  35. ->row($this->getLandTrendsChart())
  36. ->row($this->getHouseRankingCard());
  37. }
  38. /**
  39. * 获取土地等级统计卡片
  40. */
  41. protected function getLandStatsCards()
  42. {
  43. $latestStats = FarmDailyStats::orderBy('stats_date', 'desc')->first();
  44. if (!$latestStats) {
  45. return new Card('土地统计', '<div class="alert alert-warning">暂无统计数据</div>');
  46. }
  47. $landTypeNames = [
  48. 1 => '普通土地',
  49. 2 => '红土地',
  50. 3 => '黑土地',
  51. 4 => '金色特殊土地',
  52. 5 => '蓝色特殊土地',
  53. 6 => '紫色特殊土地',
  54. ];
  55. $cards = '<div class="row">';
  56. for ($type = 1; $type <= 6; $type++) {
  57. $field = "land_type_{$type}";
  58. $count = $latestStats->$field ?? 0;
  59. $typeName = $landTypeNames[$type];
  60. // 根据土地类型设置不同的颜色
  61. $colors = [
  62. 1 => 'bg-secondary', // 普通土地 - 灰色
  63. 2 => 'bg-danger', // 红土地 - 红色
  64. 3 => 'bg-dark', // 黑土地 - 黑色
  65. 4 => 'bg-warning', // 金色特殊土地 - 黄色
  66. 5 => 'bg-primary', // 蓝色特殊土地 - 蓝色
  67. 6 => 'bg-info', // 紫色特殊土地 - 紫色
  68. ];
  69. $color = $colors[$type];
  70. $cards .= "
  71. <div class='col-md-2'>
  72. <div class='info-box'>
  73. <span class='info-box-icon {$color}'><i class='fa fa-map'></i></span>
  74. <div class='info-box-content'>
  75. <span class='info-box-text'>{$typeName}</span>
  76. <span class='info-box-number'>{$count}块</span>
  77. </div>
  78. </div>
  79. </div>
  80. ";
  81. }
  82. $cards .= '</div>';
  83. return new Card('土地类型统计', $cards);
  84. }
  85. /**
  86. * 获取土地趋势图表
  87. */
  88. protected function getLandTrendsChart()
  89. {
  90. // 获取最近30天的统计数据
  91. $stats = FarmDailyStats::where('stats_date', '>=', Carbon::now()->subDays(30))
  92. ->orderBy('stats_date', 'asc')
  93. ->get();
  94. if ($stats->isEmpty()) {
  95. return new Card('土地趋势图', '<div class="alert alert-warning">暂无趋势数据</div>');
  96. }
  97. $dates = [];
  98. $landData = [
  99. 1 => [], // 普通土地
  100. 2 => [], // 红土地
  101. 3 => [], // 黑土地
  102. 4 => [], // 金色特殊土地
  103. 5 => [], // 蓝色特殊土地
  104. 6 => [], // 紫色特殊土地
  105. ];
  106. foreach ($stats as $stat) {
  107. $dates[] = $stat->stats_date->format('m-d');
  108. for ($type = 1; $type <= 6; $type++) {
  109. $field = "land_type_{$type}";
  110. $landData[$type][] = $stat->$field ?? 0;
  111. }
  112. }
  113. $landTypeNames = [
  114. 1 => '普通土地',
  115. 2 => '红土地',
  116. 3 => '黑土地',
  117. 4 => '金色特殊土地',
  118. 5 => '蓝色特殊土地',
  119. 6 => '紫色特殊土地',
  120. ];
  121. // 构建图表数据 - 只显示有数据的土地类型
  122. $series = [];
  123. $colors = ['#6c757d', '#dc3545', '#343a40', '#ffc107', '#007bff', '#17a2b8'];
  124. $legendData = [];
  125. for ($type = 1; $type <= 6; $type++) {
  126. // 检查是否有非零数据
  127. $hasData = array_sum($landData[$type]) > 0;
  128. if ($hasData) {
  129. $series[] = [
  130. 'name' => $landTypeNames[$type],
  131. 'type' => 'line',
  132. 'data' => $landData[$type],
  133. 'smooth' => true, // 平滑曲线
  134. 'symbol' => 'circle', // 数据点样式
  135. 'symbolSize' => 6, // 数据点大小
  136. 'lineStyle' => [
  137. 'width' => 3, // 线条宽度
  138. 'color' => $colors[$type - 1]
  139. ],
  140. 'itemStyle' => [
  141. 'color' => $colors[$type - 1]
  142. ],
  143. 'areaStyle' => [
  144. 'opacity' => 0.1, // 添加半透明填充区域
  145. 'color' => $colors[$type - 1]
  146. ]
  147. ];
  148. $legendData[] = $landTypeNames[$type];
  149. }
  150. }
  151. $chartId = 'land-trends-chart';
  152. $chartScript = "
  153. <script>
  154. $(function() {
  155. var chart = echarts.init(document.getElementById('{$chartId}'));
  156. var option = {
  157. title: {
  158. text: '土地类型趋势图',
  159. left: 'center'
  160. },
  161. tooltip: {
  162. trigger: 'axis'
  163. },
  164. legend: {
  165. data: ['" . implode("','", $legendData) . "'],
  166. top: 30,
  167. textStyle: {
  168. fontSize: 12
  169. }
  170. },
  171. grid: {
  172. left: '3%',
  173. right: '4%',
  174. bottom: '3%',
  175. containLabel: true
  176. },
  177. xAxis: {
  178. type: 'category',
  179. boundaryGap: false,
  180. data: ['" . implode("','", $dates) . "']
  181. },
  182. yAxis: {
  183. type: 'value',
  184. name: '数量(块)',
  185. nameLocation: 'middle',
  186. nameGap: 50,
  187. min: 0,
  188. splitLine: {
  189. show: true,
  190. lineStyle: {
  191. type: 'dashed'
  192. }
  193. }
  194. },
  195. series: " . json_encode($series) . "
  196. };
  197. chart.setOption(option);
  198. // 响应式
  199. window.addEventListener('resize', function() {
  200. chart.resize();
  201. });
  202. });
  203. </script>
  204. ";
  205. $chartHtml = "
  206. <div id='{$chartId}' style='height: 400px;'></div>
  207. {$chartScript}
  208. ";
  209. // 添加ECharts库
  210. Admin::script('https://cdn.jsdelivr.net/npm/echarts@5.4.0/dist/echarts.min.js');
  211. return new Card('土地类型趋势图', $chartHtml);
  212. }
  213. /**
  214. * 获取房屋等级排名卡片
  215. */
  216. protected function getHouseRankingCard()
  217. {
  218. $latestStats = FarmDailyStats::orderBy('stats_date', 'desc')->first();
  219. if (!$latestStats) {
  220. return new Card('房屋等级排名', '<div class="alert alert-warning">暂无统计数据</div>');
  221. }
  222. $houseData = [];
  223. for ($level = 1; $level <= 10; $level++) {
  224. $field = "house_level_{$level}";
  225. $count = $latestStats->$field ?? 0;
  226. if ($count > 0) {
  227. $houseData[] = [
  228. 'level' => $level,
  229. 'label' => "{$level}级房屋",
  230. 'value' => $count,
  231. 'ratio' => 0 // 这里可以计算占比
  232. ];
  233. }
  234. }
  235. // 按数量排序
  236. usort($houseData, function($a, $b) {
  237. return $b['value'] - $a['value'];
  238. });
  239. // 计算占比
  240. $total = array_sum(array_column($houseData, 'value'));
  241. foreach ($houseData as &$item) {
  242. $item['ratio'] = $total > 0 ? round($item['value'] / $total * 100, 1) : 0;
  243. }
  244. $rankingHtml = '<div class="table-responsive">';
  245. $rankingHtml .= '<table class="table table-striped">';
  246. $rankingHtml .= '<thead><tr><th>排名</th><th>房屋等级</th><th>数量</th><th>占比</th></tr></thead>';
  247. $rankingHtml .= '<tbody>';
  248. if (empty($houseData)) {
  249. $rankingHtml .= '<tr><td colspan="4" class="text-center text-muted">暂无数据</td></tr>';
  250. } else {
  251. foreach ($houseData as $index => $item) {
  252. $rank = $index + 1;
  253. $rankingHtml .= "<tr>";
  254. $rankingHtml .= "<td><span class='badge badge-primary'>{$rank}</span></td>";
  255. $rankingHtml .= "<td>{$item['label']}</td>";
  256. $rankingHtml .= "<td><strong>{$item['value']}个</strong></td>";
  257. $rankingHtml .= "<td>{$item['ratio']}%</td>";
  258. $rankingHtml .= "</tr>";
  259. }
  260. }
  261. $rankingHtml .= '</tbody></table></div>';
  262. return new Card('房屋等级排名', $rankingHtml);
  263. }
  264. }