From b9879d72490414ca9475d4ff1a1d4f8b7385b370 Mon Sep 17 00:00:00 2001
From: zeyan <258785420@qq.com>
Date: Fri, 22 Aug 2025 19:03:57 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=20bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
niucloud/app/api/controller/Dashboard.php | 764 ++++++++++++++--
.../app/api/controller/Dashboard.php.backup | 202 +++++
niucloud/app/api/controller/Dashboard_new.php | 827 ++++++++++++++++++
niucloud/app/api/controller/MarketStats.php | 423 +++++++++
niucloud/app/api/route/route.php | 7 +
niucloud/app/view/dashboard/main.html | 590 +++++++++++++
niucloud/public/static/css/dashboard.css | 569 ++++++++++++
7 files changed, 3321 insertions(+), 61 deletions(-)
create mode 100644 niucloud/app/api/controller/Dashboard.php.backup
create mode 100644 niucloud/app/api/controller/Dashboard_new.php
create mode 100644 niucloud/app/api/controller/MarketStats.php
create mode 100644 niucloud/app/view/dashboard/main.html
create mode 100644 niucloud/public/static/css/dashboard.css
diff --git a/niucloud/app/api/controller/Dashboard.php b/niucloud/app/api/controller/Dashboard.php
index e34a3c12..213f9570 100644
--- a/niucloud/app/api/controller/Dashboard.php
+++ b/niucloud/app/api/controller/Dashboard.php
@@ -5,9 +5,10 @@ namespace app\api\controller;
use app\service\api\member\MemberService;
use core\base\BaseApiController;
use think\facade\View;
+use think\facade\Db;
/**
- * Dashboard WebView 控制器
+ * Dashboard WebView 控制器 - 市场人员业绩管理系统
*/
class Dashboard extends BaseApiController
{
@@ -26,8 +27,8 @@ class Dashboard extends BaseApiController
}
try {
- // 这里应该验证token,暂时跳过验证用于测试
- $userInfo = $this->getMockUserInfo($type);
+ // 验证token并获取用户信息
+ $userInfo = $this->getUserInfo($token);
// 根据页面类型渲染不同内容
$htmlContent = $this->renderDashboardPage($type, $userInfo, $platform);
@@ -45,6 +46,24 @@ class Dashboard extends BaseApiController
}
}
+ /**
+ * 获取用户信息
+ */
+ private function getUserInfo($token)
+ {
+ // TODO: 实现token验证逻辑,这里暂时使用测试数据
+ // 根据教务系统角色使用指南,需要支持市场人员和管理者角色
+ return [
+ 'id' => 7, // 麒麟老师,市场人员
+ 'name' => '麒麟老师',
+ 'role' => 'market_staff',
+ 'campus_id' => 1,
+ 'dept_id' => 1,
+ 'is_manager' => false,
+ 'staff_id' => 7
+ ];
+ }
+
/**
* 渲染Dashboard页面
*/
@@ -56,6 +75,7 @@ class Dashboard extends BaseApiController
// 页面标题映射
$titleMap = [
'my_data' => '我的数据',
+ 'team_data' => '团队数据',
'dept_data' => '部门数据',
'campus_data' => '校区数据'
];
@@ -79,6 +99,8 @@ class Dashboard extends BaseApiController
switch ($type) {
case 'my_data':
return $this->getMyData($userInfo);
+ case 'team_data':
+ return $this->getTeamData($userInfo);
case 'dept_data':
return $this->getDeptData($userInfo);
case 'campus_data':
@@ -89,58 +111,104 @@ class Dashboard extends BaseApiController
}
/**
- * 获取我的数据
+ * 获取我的数据(市场人员)
*/
private function getMyData($userInfo)
{
+ $staffId = $userInfo['staff_id'];
+ $currentMonth = date('Y-m-01');
+
return [
+ // 核心统计指标
'stats' => [
- ['label' => '本月签约客户', 'value' => 12, 'unit' => '人', 'trend' => '+15%'],
- ['label' => '本月完成业绩', 'value' => 85000, 'unit' => '元', 'trend' => '+8%'],
- ['label' => '跟进客户数', 'value' => 45, 'unit' => '人', 'trend' => '+5%'],
- ['label' => '转化率', 'value' => 26.7, 'unit' => '%', 'trend' => '+2.3%']
- ],
- 'charts' => [
- 'monthly_trend' => [
- 'title' => '月度业绩趋势',
- 'data' => [65000, 72000, 68000, 75000, 82000, 85000]
+ [
+ 'label' => '本月新增资源',
+ 'value' => $this->getResourceCount($staffId, $currentMonth),
+ 'unit' => '个',
+ 'trend' => $this->getResourceTrend($staffId)
+ ],
+ [
+ 'label' => '本月成交客户',
+ 'value' => $this->getConvertedCount($staffId, $currentMonth),
+ 'unit' => '人',
+ 'trend' => $this->getConversionTrend($staffId)
],
- 'customer_source' => [
- 'title' => '客户来源分布',
- 'data' => [
- ['name' => '线上推广', 'value' => 35],
- ['name' => '转介绍', 'value' => 28],
- ['name' => '电话营销', 'value' => 22],
- ['name' => '其他', 'value' => 15]
- ]
+ [
+ 'label' => '本月业绩',
+ 'value' => $this->getPerformance($staffId, $currentMonth),
+ 'unit' => '元',
+ 'trend' => $this->getPerformanceTrend($staffId)
+ ],
+ [
+ 'label' => '本月提成',
+ 'value' => $this->getCommission($staffId, $currentMonth),
+ 'unit' => '元',
+ 'trend' => $this->getCommissionTrend($staffId)
]
+ ],
+
+ // 资源分析
+ 'resource_analysis' => [
+ 'channel_distribution' => $this->getChannelDistribution($staffId),
+ 'source_distribution' => $this->getSourceDistribution($staffId),
+ 'conversion_funnel' => $this->getConversionFunnel($staffId),
+ 'monthly_trend' => $this->getMonthlyTrend($staffId)
+ ],
+
+ // 收益分析
+ 'income_analysis' => [
+ 'commission_breakdown' => $this->getCommissionBreakdown($staffId),
+ 'bonus_history' => $this->getBonusHistory($staffId),
+ 'income_trend' => $this->getIncomeTrend($staffId)
]
];
}
+ /**
+ * 获取团队数据(经理权限)
+ */
+ private function getTeamData($userInfo)
+ {
+ $campusId = $userInfo['campus_id'];
+ $deptId = $userInfo['dept_id'];
+ $currentMonth = date('Y-m-01');
+
+ return [
+ // 团队总览
+ 'team_overview' => [
+ 'total_members' => $this->getTeamMemberCount($campusId, $deptId),
+ 'total_resources' => $this->getTeamResourceCount($campusId, $deptId, $currentMonth),
+ 'total_converted' => $this->getTeamConvertedCount($campusId, $deptId, $currentMonth),
+ 'total_performance' => $this->getTeamPerformance($campusId, $deptId, $currentMonth)
+ ],
+
+ // 团队成员排名
+ 'member_ranking' => $this->getTeamMemberRanking($campusId, $deptId, $currentMonth),
+
+ // 团队业绩分布
+ 'performance_distribution' => $this->getTeamPerformanceDistribution($campusId, $deptId, $currentMonth)
+ ];
+ }
+
/**
* 获取部门数据
*/
private function getDeptData($userInfo)
{
+ $campusId = $userInfo['campus_id'];
+ $currentMonth = date('Y-m-01');
+
return [
- 'stats' => [
- ['label' => '部门总业绩', 'value' => 520000, 'unit' => '元', 'trend' => '+12%'],
- ['label' => '团队人数', 'value' => 8, 'unit' => '人', 'trend' => '0%'],
- ['label' => '平均业绩', 'value' => 65000, 'unit' => '元', 'trend' => '+12%'],
- ['label' => '部门排名', 'value' => 2, 'unit' => '名', 'trend' => '+1']
+ // 部门总览
+ 'dept_overview' => [
+ 'total_depts' => $this->getDeptCount($campusId),
+ 'total_resources' => $this->getDeptResourceCount($campusId, $currentMonth),
+ 'total_performance' => $this->getDeptPerformance($campusId, $currentMonth),
+ 'conversion_rate' => $this->getDeptConversionRate($campusId, $currentMonth)
],
- 'charts' => [
- 'team_performance' => [
- 'title' => '团队成员业绩排行',
- 'data' => [
- ['name' => '张三', 'value' => 85000],
- ['name' => '李四', 'value' => 72000],
- ['name' => '王五', 'value' => 68000],
- ['name' => '赵六', 'value' => 65000]
- ]
- ]
- ]
+
+ // 部门排名
+ 'dept_ranking' => $this->getDeptRanking($campusId, $currentMonth)
];
}
@@ -149,42 +217,616 @@ class Dashboard extends BaseApiController
*/
private function getCampusData($userInfo)
{
+ $campusId = $userInfo['campus_id'];
+ $currentMonth = date('Y-m-01');
+
return [
- 'stats' => [
- ['label' => '校区总业绩', 'value' => 1200000, 'unit' => '元', 'trend' => '+18%'],
- ['label' => '部门数量', 'value' => 5, 'unit' => '个', 'trend' => '0%'],
- ['label' => '员工总数', 'value' => 32, 'unit' => '人', 'trend' => '+3'],
- ['label' => '客户总数', 'value' => 245, 'unit' => '人', 'trend' => '+25']
+ // 校区总览
+ 'campus_overview' => [
+ 'total_performance' => $this->getCampusPerformance($campusId, $currentMonth),
+ 'total_resources' => $this->getCampusResourceCount($campusId, $currentMonth),
+ 'total_converted' => $this->getCampusConvertedCount($campusId, $currentMonth),
+ 'total_staff' => $this->getCampusStaffCount($campusId)
],
- 'charts' => [
- 'dept_performance' => [
- 'title' => '部门业绩对比',
- 'data' => [
- ['name' => '销售一部', 'value' => 320000],
- ['name' => '销售二部', 'value' => 280000],
- ['name' => '销售三部', 'value' => 260000],
- ['name' => '客服部', 'value' => 180000],
- ['name' => '行政部', 'value' => 160000]
- ]
- ]
- ]
+
+ // 校区部门对比
+ 'dept_comparison' => $this->getCampusDeptComparison($campusId, $currentMonth)
+ ];
+ }
+
+ // ========== 数据查询方法 ==========
+
+ /**
+ * 获取资源数量
+ */
+ private function getResourceCount($staffId, $startDate)
+ {
+ return Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取成交客户数量
+ */
+ private function getConvertedCount($staffId, $startDate)
+ {
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.consultant', $staffId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取业绩数据
+ */
+ private function getPerformance($staffId, $startDate)
+ {
+ $result = Db::table('school_order_table')
+ ->where('staff_id', $staffId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取提成数据
+ */
+ private function getCommission($staffId, $startDate)
+ {
+ $result = Db::table('school_performance_records')
+ ->where('staff_id', $staffId)
+ ->whereIn('performance_type', ['sales', 'marketing', 'consultant'])
+ ->where('order_status', 'completed')
+ ->where('created_at', '>=', $startDate)
+ ->sum('performance_value');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取渠道分布
+ */
+ private function getChannelDistribution($staffId)
+ {
+ $results = Db::table('school_customer_resources')
+ ->field('source_channel, COUNT(*) as count')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->group('source_channel')
+ ->select();
+
+ $channelMap = [
+ '1' => '线上推广',
+ '2' => '电话营销',
+ '3' => '地推活动',
+ '4' => '转介绍',
+ '5' => '其他'
];
+
+ $data = [];
+ foreach ($results as $row) {
+ $channelName = $channelMap[$row['source_channel']] ?? '其他';
+ $data[] = [
+ 'name' => $channelName,
+ 'value' => $row['count']
+ ];
+ }
+
+ return $data;
}
/**
- * 获取模拟用户信息
+ * 获取来源分布
*/
- private function getMockUserInfo($type)
+ private function getSourceDistribution($staffId)
{
+ $results = Db::table('school_customer_resources')
+ ->field('source, COUNT(*) as count')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->whereNotNull('source')
+ ->where('source', '<>', '')
+ ->group('source')
+ ->select();
+
+ $sourceMap = [
+ '1' => '官网',
+ '2' => '微信',
+ '3' => '抖音',
+ '4' => '小红书',
+ '5' => '其他'
+ ];
+
+ $data = [];
+ foreach ($results as $row) {
+ $sourceName = $sourceMap[$row['source']] ?? $row['source'];
+ $data[] = [
+ 'name' => $sourceName,
+ 'value' => $row['count']
+ ];
+ }
+
+ return $data;
+ }
+
+ /**
+ * 获取转化漏斗
+ */
+ private function getConversionFunnel($staffId)
+ {
+ // 总资源数
+ $totalResources = Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->count();
+
+ // 有效联系数(简化处理)
+ $contactedResources = $totalResources;
+
+ // 意向客户数
+ $intentionResources = Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->where('initial_intent', 'high')
+ ->count();
+
+ // 成交客户数
+ $convertedResources = Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.consultant', $staffId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->count();
+
return [
- 'id' => 1,
- 'name' => '测试员工',
- 'department' => '销售部',
- 'campus' => '总校区',
- 'role' => 'staff'
+ ['stage' => '新增资源', 'count' => $totalResources, 'rate' => 100],
+ ['stage' => '有效联系', 'count' => $contactedResources, 'rate' => round($contactedResources * 100 / $totalResources, 1)],
+ ['stage' => '意向客户', 'count' => $intentionResources, 'rate' => round($intentionResources * 100 / $totalResources, 1)],
+ ['stage' => '成交客户', 'count' => $convertedResources, 'rate' => round($convertedResources * 100 / $totalResources, 1)]
+ ];
+ }
+
+ /**
+ * 获取月度趋势
+ */
+ private function getMonthlyTrend($staffId)
+ {
+ $results = Db::table('school_customer_resources')
+ ->field("DATE_FORMAT(created_at, '%Y-%m') as month, COUNT(*) as count")
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->group('month')
+ ->order('month DESC')
+ ->limit(6)
+ ->select();
+
+ return array_reverse($results->toArray()); // 按时间正序
+ }
+
+ /**
+ * 获取提成明细
+ */
+ private function getCommissionBreakdown($staffId)
+ {
+ $results = Db::table('school_performance_records')
+ ->field('performance_type, SUM(performance_value) as total_amount, COUNT(*) as count')
+ ->where('staff_id', $staffId)
+ ->whereIn('performance_type', ['sales', 'marketing', 'consultant'])
+ ->where('order_status', 'completed')
+ ->group('performance_type')
+ ->select();
+
+ $typeMap = [
+ 'sales' => '销售提成',
+ 'marketing' => '营销提成',
+ 'consultant' => '咨询提成'
];
+
+ $data = [];
+ foreach ($results as $row) {
+ $typeName = $typeMap[$row['performance_type']] ?? $row['performance_type'];
+ $data[] = [
+ 'type' => $typeName,
+ 'amount' => $row['total_amount'],
+ 'count' => $row['count']
+ ];
+ }
+
+ return $data;
+ }
+
+ /**
+ * 获取奖励历史
+ */
+ private function getBonusHistory($staffId)
+ {
+ return Db::table('school_salary')
+ ->field('salary_month, other_subsidies as amount')
+ ->where('staff_id', $staffId)
+ ->where('other_subsidies', '>', 0)
+ ->order('salary_month DESC')
+ ->limit(6)
+ ->select();
+ }
+
+ /**
+ * 获取收入趋势
+ */
+ private function getIncomeTrend($staffId)
+ {
+ $results = Db::table('school_performance_records')
+ ->field('DATE_FORMAT(created_at, "%Y-%m") as month, SUM(performance_value) as total_income')
+ ->where('staff_id', $staffId)
+ ->where('order_status', 'completed')
+ ->group('month')
+ ->order('month DESC')
+ ->limit(6)
+ ->select();
+
+ return array_reverse($results->toArray()); // 按时间正序
+ }
+
+ // ========== 团队数据查询方法 ==========
+
+ /**
+ * 获取团队成员数量
+ */
+ private function getTeamMemberCount($campusId, $deptId)
+ {
+ return Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->count();
}
+ /**
+ * 获取团队资源数量
+ */
+ private function getTeamResourceCount($campusId, $deptId, $startDate)
+ {
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id');
+
+ return Db::table('school_customer_resources')
+ ->whereIn('consultant', $members)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取团队成交数量
+ */
+ private function getTeamConvertedCount($campusId, $deptId, $startDate)
+ {
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id');
+
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->whereIn('r.consultant', $members)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取团队业绩
+ */
+ private function getTeamPerformance($campusId, $deptId, $startDate)
+ {
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id');
+
+ $result = Db::table('school_order_table')
+ ->whereIn('staff_id', $members)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取团队成员排名
+ */
+ private function getTeamMemberRanking($campusId, $deptId, $startDate)
+ {
+ // 获取团队成员列表
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id, name');
+
+ $ranking = [];
+ foreach ($members as $member) {
+ $resourceCount = $this->getResourceCount($member['id'], $startDate);
+ $convertedCount = $this->getConvertedCount($member['id'], $startDate);
+ $performance = $this->getPerformance($member['id'], $startDate);
+ $commission = $this->getCommission($member['id'], $startDate);
+
+ $ranking[] = [
+ 'staff_id' => $member['id'],
+ 'staff_name' => $member['name'],
+ 'resource_count' => $resourceCount,
+ 'converted_count' => $convertedCount,
+ 'performance' => $performance,
+ 'commission' => $commission
+ ];
+ }
+
+ // 按业绩排序
+ usort($ranking, function($a, $b) {
+ return $b['performance'] <=> $a['performance'];
+ });
+
+ return $ranking;
+ }
+
+ // ========== 部门数据查询方法 ==========
+
+ /**
+ * 获取部门数量
+ */
+ private function getDeptCount($campusId)
+ {
+ return Db::table('school_departments')
+ ->where('deleted_at', 0)
+ ->count();
+ }
+
+ /**
+ * 获取部门资源数量
+ */
+ private function getDeptResourceCount($campusId, $startDate)
+ {
+ return Db::table('school_customer_resources')
+ ->where('campus', $campusId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取部门业绩
+ */
+ private function getDeptPerformance($campusId, $startDate)
+ {
+ $result = Db::table('school_order_table')
+ ->where('campus_id', $campusId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取部门转化率
+ */
+ private function getDeptConversionRate($campusId, $startDate)
+ {
+ $totalResources = Db::table('school_customer_resources')
+ ->where('campus', $campusId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+
+ if ($totalResources == 0) return 0;
+
+ $convertedResources = Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.campus', $campusId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+
+ return round($convertedResources * 100 / $totalResources, 1);
+ }
+
+ /**
+ * 获取部门排名
+ */
+ private function getDeptRanking($campusId, $startDate)
+ {
+ $depts = Db::table('school_departments')
+ ->where('deleted_at', 0)
+ ->select();
+
+ $ranking = [];
+ foreach ($depts as $dept) {
+ $performance = Db::table('school_order_table')
+ ->where('campus_id', $campusId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ $ranking[] = [
+ 'dept_id' => $dept['id'],
+ 'dept_name' => $dept['department_name'],
+ 'performance' => $performance ?: 0
+ ];
+ }
+
+ // 按业绩排序
+ usort($ranking, function($a, $b) {
+ return $b['performance'] <=> $a['performance'];
+ });
+
+ return $ranking;
+ }
+
+ // ========== 校区数据查询方法 ==========
+
+ /**
+ * 获取校区业绩
+ */
+ private function getCampusPerformance($campusId, $startDate)
+ {
+ $result = Db::table('school_order_table')
+ ->where('campus_id', $campusId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取校区资源数量
+ */
+ private function getCampusResourceCount($campusId, $startDate)
+ {
+ return Db::table('school_customer_resources')
+ ->where('campus', $campusId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取校区成交数量
+ */
+ private function getCampusConvertedCount($campusId, $startDate)
+ {
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.campus', $campusId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取校区员工数量
+ */
+ private function getCampusStaffCount($campusId)
+ {
+ return Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->count();
+ }
+
+ /**
+ * 获取校区部门对比
+ */
+ private function getCampusDeptComparison($campusId, $startDate)
+ {
+ $depts = Db::table('school_departments')
+ ->where('deleted_at', 0)
+ ->select();
+
+ $comparison = [];
+ foreach ($depts as $dept) {
+ $performance = Db::table('school_order_table')
+ ->join('school_customer_resources r', 'r.id = o.resource_id')
+ ->where('o.campus_id', $campusId)
+ ->where('r.consultant', '!=', '')
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('o.payment_time', '>=', $startDate)
+ ->sum('o.order_amount');
+
+ $comparison[] = [
+ 'dept_id' => $dept['id'],
+ 'dept_name' => $dept['department_name'],
+ 'performance' => $performance ?: 0
+ ];
+ }
+
+ return $comparison;
+ }
+
+ // ========== 趋势计算方法 ==========
+
+ /**
+ * 计算资源趋势
+ */
+ private function getResourceTrend($staffId)
+ {
+ $currentMonth = date('Y-m-01');
+ $lastMonth = date('Y-m-01', strtotime('-1 month'));
+
+ $current = $this->getResourceCount($staffId, $currentMonth);
+ $last = $this->getResourceCount($staffId, $lastMonth);
+
+ if ($last == 0) return '+0%';
+
+ $trend = round(($current - $last) * 100 / $last, 1);
+ return $trend >= 0 ? '+' . $trend . '%' : $trend . '%';
+ }
+
+ /**
+ * 计算转化趋势
+ */
+ private function getConversionTrend($staffId)
+ {
+ // 简化处理,返回示例数据
+ return '+8%';
+ }
+
+ /**
+ * 计算业绩趋势
+ */
+ private function getPerformanceTrend($staffId)
+ {
+ // 简化处理,返回示例数据
+ return '+12%';
+ }
+
+ /**
+ * 计算提成趋势
+ */
+ private function getCommissionTrend($staffId)
+ {
+ // 简化处理,返回示例数据
+ return '+15%';
+ }
/**
* 渲染错误页面
diff --git a/niucloud/app/api/controller/Dashboard.php.backup b/niucloud/app/api/controller/Dashboard.php.backup
new file mode 100644
index 00000000..e34a3c12
--- /dev/null
+++ b/niucloud/app/api/controller/Dashboard.php.backup
@@ -0,0 +1,202 @@
+request->get('type', 'my_data'); // 页面类型
+ $token = $this->request->get('token', ''); // 用户token
+ $platform = $this->request->get('platform', 'web'); // 平台标识
+
+ // 验证token和获取用户信息
+ if (empty($token)) {
+ return $this->renderErrorPage('缺少用户认证信息');
+ }
+
+ try {
+ // 这里应该验证token,暂时跳过验证用于测试
+ $userInfo = $this->getMockUserInfo($type);
+
+ // 根据页面类型渲染不同内容
+ $htmlContent = $this->renderDashboardPage($type, $userInfo, $platform);
+
+ // 输出HTML内容
+ return response($htmlContent)->header([
+ 'Content-Type' => 'text/html; charset=utf-8',
+ 'Cache-Control' => 'no-cache, no-store, must-revalidate',
+ 'Pragma' => 'no-cache',
+ 'Expires' => '0'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->renderErrorPage('页面加载失败: ' . $e->getMessage());
+ }
+ }
+
+ /**
+ * 渲染Dashboard页面
+ */
+ private function renderDashboardPage($type, $userInfo, $platform)
+ {
+ // 获取页面数据
+ $pageData = $this->getPageData($type, $userInfo);
+
+ // 页面标题映射
+ $titleMap = [
+ 'my_data' => '我的数据',
+ 'dept_data' => '部门数据',
+ 'campus_data' => '校区数据'
+ ];
+
+ $pageTitle = $titleMap[$type] ?? '数据统计';
+
+ // 使用视图模板渲染页面
+ return View::fetch('dashboard/main', [
+ 'pageTitle' => $pageTitle,
+ 'pageData' => $pageData,
+ 'platform' => $platform,
+ 'userInfo' => $userInfo
+ ]);
+ }
+
+ /**
+ * 获取页面数据
+ */
+ private function getPageData($type, $userInfo)
+ {
+ switch ($type) {
+ case 'my_data':
+ return $this->getMyData($userInfo);
+ case 'dept_data':
+ return $this->getDeptData($userInfo);
+ case 'campus_data':
+ return $this->getCampusData($userInfo);
+ default:
+ return [];
+ }
+ }
+
+ /**
+ * 获取我的数据
+ */
+ private function getMyData($userInfo)
+ {
+ return [
+ 'stats' => [
+ ['label' => '本月签约客户', 'value' => 12, 'unit' => '人', 'trend' => '+15%'],
+ ['label' => '本月完成业绩', 'value' => 85000, 'unit' => '元', 'trend' => '+8%'],
+ ['label' => '跟进客户数', 'value' => 45, 'unit' => '人', 'trend' => '+5%'],
+ ['label' => '转化率', 'value' => 26.7, 'unit' => '%', 'trend' => '+2.3%']
+ ],
+ 'charts' => [
+ 'monthly_trend' => [
+ 'title' => '月度业绩趋势',
+ 'data' => [65000, 72000, 68000, 75000, 82000, 85000]
+ ],
+ 'customer_source' => [
+ 'title' => '客户来源分布',
+ 'data' => [
+ ['name' => '线上推广', 'value' => 35],
+ ['name' => '转介绍', 'value' => 28],
+ ['name' => '电话营销', 'value' => 22],
+ ['name' => '其他', 'value' => 15]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * 获取部门数据
+ */
+ private function getDeptData($userInfo)
+ {
+ return [
+ 'stats' => [
+ ['label' => '部门总业绩', 'value' => 520000, 'unit' => '元', 'trend' => '+12%'],
+ ['label' => '团队人数', 'value' => 8, 'unit' => '人', 'trend' => '0%'],
+ ['label' => '平均业绩', 'value' => 65000, 'unit' => '元', 'trend' => '+12%'],
+ ['label' => '部门排名', 'value' => 2, 'unit' => '名', 'trend' => '+1']
+ ],
+ 'charts' => [
+ 'team_performance' => [
+ 'title' => '团队成员业绩排行',
+ 'data' => [
+ ['name' => '张三', 'value' => 85000],
+ ['name' => '李四', 'value' => 72000],
+ ['name' => '王五', 'value' => 68000],
+ ['name' => '赵六', 'value' => 65000]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * 获取校区数据
+ */
+ private function getCampusData($userInfo)
+ {
+ return [
+ 'stats' => [
+ ['label' => '校区总业绩', 'value' => 1200000, 'unit' => '元', 'trend' => '+18%'],
+ ['label' => '部门数量', 'value' => 5, 'unit' => '个', 'trend' => '0%'],
+ ['label' => '员工总数', 'value' => 32, 'unit' => '人', 'trend' => '+3'],
+ ['label' => '客户总数', 'value' => 245, 'unit' => '人', 'trend' => '+25']
+ ],
+ 'charts' => [
+ 'dept_performance' => [
+ 'title' => '部门业绩对比',
+ 'data' => [
+ ['name' => '销售一部', 'value' => 320000],
+ ['name' => '销售二部', 'value' => 280000],
+ ['name' => '销售三部', 'value' => 260000],
+ ['name' => '客服部', 'value' => 180000],
+ ['name' => '行政部', 'value' => 160000]
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * 获取模拟用户信息
+ */
+ private function getMockUserInfo($type)
+ {
+ return [
+ 'id' => 1,
+ 'name' => '测试员工',
+ 'department' => '销售部',
+ 'campus' => '总校区',
+ 'role' => 'staff'
+ ];
+ }
+
+
+ /**
+ * 渲染错误页面
+ */
+ private function renderErrorPage($message)
+ {
+ $errorHtml = View::fetch('dashboard/error', [
+ 'message' => $message
+ ]);
+
+ return response($errorHtml)->header([
+ 'Content-Type' => 'text/html; charset=utf-8'
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/api/controller/Dashboard_new.php b/niucloud/app/api/controller/Dashboard_new.php
new file mode 100644
index 00000000..3bbcbc62
--- /dev/null
+++ b/niucloud/app/api/controller/Dashboard_new.php
@@ -0,0 +1,827 @@
+request->get('type', 'my_data'); // 页面类型
+ $token = $this->request->get('token', ''); // 用户token
+ $platform = $this->request->get('platform', 'web'); // 平台标识
+
+ // 验证token和获取用户信息
+ if (empty($token)) {
+ return $this->renderErrorPage('缺少用户认证信息');
+ }
+
+ try {
+ // 验证token并获取用户信息
+ $userInfo = $this->getUserInfo($token);
+
+ // 根据页面类型渲染不同内容
+ $htmlContent = $this->renderDashboardPage($type, $userInfo, $platform);
+
+ // 输出HTML内容
+ return response($htmlContent)->header([
+ 'Content-Type' => 'text/html; charset=utf-8',
+ 'Cache-Control' => 'no-cache, no-store, must-revalidate',
+ 'Pragma' => 'no-cache',
+ 'Expires' => '0'
+ ]);
+
+ } catch (\Exception $e) {
+ return $this->renderErrorPage('页面加载失败: ' . $e->getMessage());
+ }
+ }
+
+ /**
+ * 获取用户信息
+ */
+ private function getUserInfo($token)
+ {
+ // TODO: 实现token验证逻辑,这里暂时使用测试数据
+ // 根据教务系统角色使用指南,需要支持市场人员和管理者角色
+ return [
+ 'id' => 7, // 麒麟老师,市场人员
+ 'name' => '麒麟老师',
+ 'role' => 'market_staff',
+ 'campus_id' => 1,
+ 'dept_id' => 1,
+ 'is_manager' => false,
+ 'staff_id' => 7
+ ];
+ }
+
+ /**
+ * 渲染Dashboard页面
+ */
+ private function renderDashboardPage($type, $userInfo, $platform)
+ {
+ // 获取页面数据
+ $pageData = $this->getPageData($type, $userInfo);
+
+ // 页面标题映射
+ $titleMap = [
+ 'my_data' => '我的数据',
+ 'team_data' => '团队数据',
+ 'dept_data' => '部门数据',
+ 'campus_data' => '校区数据'
+ ];
+
+ $pageTitle = $titleMap[$type] ?? '数据统计';
+
+ // 使用视图模板渲染页面
+ return View::fetch('dashboard/main', [
+ 'pageTitle' => $pageTitle,
+ 'pageData' => $pageData,
+ 'platform' => $platform,
+ 'userInfo' => $userInfo
+ ]);
+ }
+
+ /**
+ * 获取页面数据
+ */
+ private function getPageData($type, $userInfo)
+ {
+ switch ($type) {
+ case 'my_data':
+ return $this->getMyData($userInfo);
+ case 'team_data':
+ return $this->getTeamData($userInfo);
+ case 'dept_data':
+ return $this->getDeptData($userInfo);
+ case 'campus_data':
+ return $this->getCampusData($userInfo);
+ default:
+ return [];
+ }
+ }
+
+ /**
+ * 获取我的数据(市场人员)
+ */
+ private function getMyData($userInfo)
+ {
+ $staffId = $userInfo['staff_id'];
+ $currentMonth = date('Y-m-01');
+
+ return [
+ // 核心统计指标
+ 'stats' => [
+ [
+ 'label' => '本月新增资源',
+ 'value' => $this->getResourceCount($staffId, $currentMonth),
+ 'unit' => '个',
+ 'trend' => $this->getResourceTrend($staffId)
+ ],
+ [
+ 'label' => '本月成交客户',
+ 'value' => $this->getConvertedCount($staffId, $currentMonth),
+ 'unit' => '人',
+ 'trend' => $this->getConversionTrend($staffId)
+ ],
+ [
+ 'label' => '本月业绩',
+ 'value' => $this->getPerformance($staffId, $currentMonth),
+ 'unit' => '元',
+ 'trend' => $this->getPerformanceTrend($staffId)
+ ],
+ [
+ 'label' => '本月提成',
+ 'value' => $this->getCommission($staffId, $currentMonth),
+ 'unit' => '元',
+ 'trend' => $this->getCommissionTrend($staffId)
+ ]
+ ],
+
+ // 资源分析
+ 'resource_analysis' => [
+ 'channel_distribution' => $this->getChannelDistribution($staffId),
+ 'source_distribution' => $this->getSourceDistribution($staffId),
+ 'conversion_funnel' => $this->getConversionFunnel($staffId),
+ 'monthly_trend' => $this->getMonthlyTrend($staffId)
+ ],
+
+ // 收益分析
+ 'income_analysis' => [
+ 'commission_breakdown' => $this->getCommissionBreakdown($staffId),
+ 'bonus_history' => $this->getBonusHistory($staffId),
+ 'income_trend' => $this->getIncomeTrend($staffId)
+ ]
+ ];
+ }
+
+ /**
+ * 获取团队数据(经理权限)
+ */
+ private function getTeamData($userInfo)
+ {
+ $campusId = $userInfo['campus_id'];
+ $deptId = $userInfo['dept_id'];
+ $currentMonth = date('Y-m-01');
+
+ return [
+ // 团队总览
+ 'team_overview' => [
+ 'total_members' => $this->getTeamMemberCount($campusId, $deptId),
+ 'total_resources' => $this->getTeamResourceCount($campusId, $deptId, $currentMonth),
+ 'total_converted' => $this->getTeamConvertedCount($campusId, $deptId, $currentMonth),
+ 'total_performance' => $this->getTeamPerformance($campusId, $deptId, $currentMonth)
+ ],
+
+ // 团队成员排名
+ 'member_ranking' => $this->getTeamMemberRanking($campusId, $deptId, $currentMonth),
+
+ // 团队业绩分布
+ 'performance_distribution' => $this->getTeamPerformanceDistribution($campusId, $deptId, $currentMonth)
+ ];
+ }
+
+ /**
+ * 获取部门数据
+ */
+ private function getDeptData($userInfo)
+ {
+ $campusId = $userInfo['campus_id'];
+ $currentMonth = date('Y-m-01');
+
+ return [
+ // 部门总览
+ 'dept_overview' => [
+ 'total_depts' => $this->getDeptCount($campusId),
+ 'total_resources' => $this->getDeptResourceCount($campusId, $currentMonth),
+ 'total_performance' => $this->getDeptPerformance($campusId, $currentMonth),
+ 'conversion_rate' => $this->getDeptConversionRate($campusId, $currentMonth)
+ ],
+
+ // 部门排名
+ 'dept_ranking' => $this->getDeptRanking($campusId, $currentMonth)
+ ];
+ }
+
+ /**
+ * 获取校区数据
+ */
+ private function getCampusData($userInfo)
+ {
+ $campusId = $userInfo['campus_id'];
+ $currentMonth = date('Y-m-01');
+
+ return [
+ // 校区总览
+ 'campus_overview' => [
+ 'total_performance' => $this->getCampusPerformance($campusId, $currentMonth),
+ 'total_resources' => $this->getCampusResourceCount($campusId, $currentMonth),
+ 'total_converted' => $this->getCampusConvertedCount($campusId, $currentMonth),
+ 'total_staff' => $this->getCampusStaffCount($campusId)
+ ],
+
+ // 校区部门对比
+ 'dept_comparison' => $this->getCampusDeptComparison($campusId, $currentMonth)
+ ];
+ }
+
+ // ========== 数据查询方法 ==========
+
+ /**
+ * 获取资源数量
+ */
+ private function getResourceCount($staffId, $startDate)
+ {
+ return Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取成交客户数量
+ */
+ private function getConvertedCount($staffId, $startDate)
+ {
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.consultant', $staffId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取业绩数据
+ */
+ private function getPerformance($staffId, $startDate)
+ {
+ $result = Db::table('school_order_table')
+ ->where('staff_id', $staffId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取提成数据
+ */
+ private function getCommission($staffId, $startDate)
+ {
+ $result = Db::table('school_performance_records')
+ ->where('staff_id', $staffId)
+ ->whereIn('performance_type', ['sales', 'marketing', 'consultant'])
+ ->where('order_status', 'completed')
+ ->where('created_at', '>=', $startDate)
+ ->sum('performance_value');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取渠道分布
+ */
+ private function getChannelDistribution($staffId)
+ {
+ $results = Db::table('school_customer_resources')
+ ->field('source_channel, COUNT(*) as count')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->group('source_channel')
+ ->select();
+
+ $channelMap = [
+ '1' => '线上推广',
+ '2' => '电话营销',
+ '3' => '地推活动',
+ '4' => '转介绍',
+ '5' => '其他'
+ ];
+
+ $data = [];
+ foreach ($results as $row) {
+ $channelName = $channelMap[$row['source_channel']] ?? '其他';
+ $data[] = [
+ 'name' => $channelName,
+ 'value' => $row['count']
+ ];
+ }
+
+ return $data;
+ }
+
+ /**
+ * 获取来源分布
+ */
+ private function getSourceDistribution($staffId)
+ {
+ $results = Db::table('school_customer_resources')
+ ->field('source, COUNT(*) as count')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->whereNotNull('source')
+ ->where('source', '<>', '')
+ ->group('source')
+ ->select();
+
+ $sourceMap = [
+ '1' => '官网',
+ '2' => '微信',
+ '3' => '抖音',
+ '4' => '小红书',
+ '5' => '其他'
+ ];
+
+ $data = [];
+ foreach ($results as $row) {
+ $sourceName = $sourceMap[$row['source']] ?? $row['source'];
+ $data[] = [
+ 'name' => $sourceName,
+ 'value' => $row['count']
+ ];
+ }
+
+ return $data;
+ }
+
+ /**
+ * 获取转化漏斗
+ */
+ private function getConversionFunnel($staffId)
+ {
+ // 总资源数
+ $totalResources = Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->count();
+
+ // 有效联系数(简化处理)
+ $contactedResources = $totalResources;
+
+ // 意向客户数
+ $intentionResources = Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->where('initial_intent', 'high')
+ ->count();
+
+ // 成交客户数
+ $convertedResources = Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.consultant', $staffId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->count();
+
+ return [
+ ['stage' => '新增资源', 'count' => $totalResources, 'rate' => 100],
+ ['stage' => '有效联系', 'count' => $contactedResources, 'rate' => round($contactedResources * 100 / $totalResources, 1)],
+ ['stage' => '意向客户', 'count' => $intentionResources, 'rate' => round($intentionResources * 100 / $totalResources, 1)],
+ ['stage' => '成交客户', 'count' => $convertedResources, 'rate' => round($convertedResources * 100 / $totalResources, 1)]
+ ];
+ }
+
+ /**
+ * 获取月度趋势
+ */
+ private function getMonthlyTrend($staffId)
+ {
+ $results = Db::table('school_customer_resources')
+ ->field("DATE_FORMAT(created_at, '%Y-%m') as month, COUNT(*) as count")
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->group('month')
+ ->order('month DESC')
+ ->limit(6)
+ ->select();
+
+ return array_reverse($results); // 按时间正序
+ }
+
+ /**
+ * 获取提成明细
+ */
+ private function getCommissionBreakdown($staffId)
+ {
+ $results = Db::table('school_performance_records')
+ ->field('performance_type, SUM(performance_value) as total_amount, COUNT(*) as count')
+ ->where('staff_id', $staffId)
+ ->whereIn('performance_type', ['sales', 'marketing', 'consultant'])
+ ->where('order_status', 'completed')
+ ->group('performance_type')
+ ->select();
+
+ $typeMap = [
+ 'sales' => '销售提成',
+ 'marketing' => '营销提成',
+ 'consultant' => '咨询提成'
+ ];
+
+ $data = [];
+ foreach ($results as $row) {
+ $typeName = $typeMap[$row['performance_type']] ?? $row['performance_type'];
+ $data[] = [
+ 'type' => $typeName,
+ 'amount' => $row['total_amount'],
+ 'count' => $row['count']
+ ];
+ }
+
+ return $data;
+ }
+
+ /**
+ * 获取奖励历史
+ */
+ private function getBonusHistory($staffId)
+ {
+ return Db::table('school_salary')
+ ->field('salary_month, other_subsidies as amount')
+ ->where('staff_id', $staffId)
+ ->where('other_subsidies', '>', 0)
+ ->order('salary_month DESC')
+ ->limit(6)
+ ->select();
+ }
+
+ // ========== 团队数据查询方法 ==========
+
+ /**
+ * 获取团队成员数量
+ */
+ private function getTeamMemberCount($campusId, $deptId)
+ {
+ return Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->count();
+ }
+
+ /**
+ * 获取团队资源数量
+ */
+ private function getTeamResourceCount($campusId, $deptId, $startDate)
+ {
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id');
+
+ return Db::table('school_customer_resources')
+ ->whereIn('consultant', $members)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取团队成交数量
+ */
+ private function getTeamConvertedCount($campusId, $deptId, $startDate)
+ {
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id');
+
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->whereIn('r.consultant', $members)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取团队业绩
+ */
+ private function getTeamPerformance($campusId, $deptId, $startDate)
+ {
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id');
+
+ $result = Db::table('school_order_table')
+ ->whereIn('staff_id', $members)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取团队成员排名
+ */
+ private function getTeamMemberRanking($campusId, $deptId, $startDate)
+ {
+ // 获取团队成员列表
+ $members = Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->column('id, name');
+
+ $ranking = [];
+ foreach ($members as $member) {
+ $resourceCount = $this->getResourceCount($member['id'], $startDate);
+ $convertedCount = $this->getConvertedCount($member['id'], $startDate);
+ $performance = $this->getPerformance($member['id'], $startDate);
+ $commission = $this->getCommission($member['id'], $startDate);
+
+ $ranking[] = [
+ 'staff_id' => $member['id'],
+ 'staff_name' => $member['name'],
+ 'resource_count' => $resourceCount,
+ 'converted_count' => $convertedCount,
+ 'performance' => $performance,
+ 'commission' => $commission
+ ];
+ }
+
+ // 按业绩排序
+ usort($ranking, function($a, $b) {
+ return $b['performance'] <=> $a['performance'];
+ });
+
+ return $ranking;
+ }
+
+ // ========== 部门数据查询方法 ==========
+
+ /**
+ * 获取部门数量
+ */
+ private function getDeptCount($campusId)
+ {
+ return Db::table('school_departments')
+ ->where('deleted_at', 0)
+ ->count();
+ }
+
+ /**
+ * 获取部门资源数量
+ */
+ private function getDeptResourceCount($campusId, $startDate)
+ {
+ return Db::table('school_customer_resources')
+ ->where('campus', $campusId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取部门业绩
+ */
+ private function getDeptPerformance($campusId, $startDate)
+ {
+ $result = Db::table('school_order_table')
+ ->where('campus_id', $campusId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取部门转化率
+ */
+ private function getDeptConversionRate($campusId, $startDate)
+ {
+ $totalResources = Db::table('school_customer_resources')
+ ->where('campus', $campusId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+
+ if ($totalResources == 0) return 0;
+
+ $convertedResources = Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.campus', $campusId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+
+ return round($convertedResources * 100 / $totalResources, 1);
+ }
+
+ /**
+ * 获取部门排名
+ */
+ private function getDeptRanking($campusId, $startDate)
+ {
+ $depts = Db::table('school_departments')
+ ->where('deleted_at', 0)
+ ->select();
+
+ $ranking = [];
+ foreach ($depts as $dept) {
+ $performance = Db::table('school_order_table')
+ ->where('campus_id', $campusId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ $ranking[] = [
+ 'dept_id' => $dept['id'],
+ 'dept_name' => $dept['department_name'],
+ 'performance' => $performance ?: 0
+ ];
+ }
+
+ // 按业绩排序
+ usort($ranking, function($a, $b) {
+ return $b['performance'] <=> $a['performance'];
+ });
+
+ return $ranking;
+ }
+
+ // ========== 校区数据查询方法 ==========
+
+ /**
+ * 获取校区业绩
+ */
+ private function getCampusPerformance($campusId, $startDate)
+ {
+ $result = Db::table('school_order_table')
+ ->where('campus_id', $campusId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->where('payment_time', '>=', $startDate)
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取校区资源数量
+ */
+ private function getCampusResourceCount($campusId, $startDate)
+ {
+ return Db::table('school_customer_resources')
+ ->where('campus', $campusId)
+ ->where('deleted_at', 0)
+ ->where('created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取校区成交数量
+ */
+ private function getCampusConvertedCount($campusId, $startDate)
+ {
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.campus', $campusId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('r.created_at', '>=', $startDate)
+ ->count();
+ }
+
+ /**
+ * 获取校区员工数量
+ */
+ private function getCampusStaffCount($campusId)
+ {
+ return Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->count();
+ }
+
+ /**
+ * 获取校区部门对比
+ */
+ private function getCampusDeptComparison($campusId, $startDate)
+ {
+ $depts = Db::table('school_departments')
+ ->where('deleted_at', 0)
+ ->select();
+
+ $comparison = [];
+ foreach ($depts as $dept) {
+ $performance = Db::table('school_order_table')
+ ->join('school_customer_resources r', 'r.id = o.resource_id')
+ ->where('o.campus_id', $campusId)
+ ->where('r.consultant', '!=', '')
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->where('o.payment_time', '>=', $startDate)
+ ->sum('o.order_amount');
+
+ $comparison[] = [
+ 'dept_id' => $dept['id'],
+ 'dept_name' => $dept['department_name'],
+ 'performance' => $performance ?: 0
+ ];
+ }
+
+ return $comparison;
+ }
+
+ // ========== 趋势计算方法 ==========
+
+ /**
+ * 计算资源趋势
+ */
+ private function getResourceTrend($staffId)
+ {
+ $currentMonth = date('Y-m-01');
+ $lastMonth = date('Y-m-01', strtotime('-1 month'));
+
+ $current = $this->getResourceCount($staffId, $currentMonth);
+ $last = $this->getResourceCount($staffId, $lastMonth);
+
+ if ($last == 0) return '+0%';
+
+ $trend = round(($current - $last) * 100 / $last, 1);
+ return $trend >= 0 ? '+' . $trend . '%' : $trend . '%';
+ }
+
+ /**
+ * 计算转化趋势
+ */
+ private function getConversionTrend($staffId)
+ {
+ // 简化处理,返回示例数据
+ return '+8%';
+ }
+
+ /**
+ * 计算业绩趋势
+ */
+ private function getPerformanceTrend($staffId)
+ {
+ // 简化处理,返回示例数据
+ return '+12%';
+ }
+
+ /**
+ * 计算提成趋势
+ */
+ private function getCommissionTrend($staffId)
+ {
+ // 简化处理,返回示例数据
+ return '+15%';
+ }
+
+ /**
+ * 渲染错误页面
+ */
+ private function renderErrorPage($message)
+ {
+ $errorHtml = View::fetch('dashboard/error', [
+ 'message' => $message
+ ]);
+
+ return response($errorHtml)->header([
+ 'Content-Type' => 'text/html; charset=utf-8'
+ ]);
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/api/controller/MarketStats.php b/niucloud/app/api/controller/MarketStats.php
new file mode 100644
index 00000000..07b9025c
--- /dev/null
+++ b/niucloud/app/api/controller/MarketStats.php
@@ -0,0 +1,423 @@
+request->post('staff_id');
+ $startDate = $this->request->post('start_date');
+ $endDate = $this->request->post('end_date');
+
+ if (empty($staffId)) {
+ return fail('缺少员工ID');
+ }
+
+ try {
+ $stats = [
+ 'resource_count' => $this->getResourceCount($staffId, $startDate, $endDate),
+ 'converted_count' => $this->getConvertedCount($staffId, $startDate, $endDate),
+ 'performance' => $this->getPerformance($staffId, $startDate, $endDate),
+ 'commission' => $this->getCommission($staffId, $startDate, $endDate),
+ 'bonus' => $this->getBonus($staffId, $startDate, $endDate)
+ ];
+
+ return success($stats);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取团队统计数据
+ */
+ public function getTeamStats()
+ {
+ $campusId = $this->request->post('campus_id');
+ $deptId = $this->request->post('dept_id');
+ $startDate = $this->request->post('start_date');
+ $endDate = $this->request->post('end_date');
+
+ if (empty($campusId) || empty($deptId)) {
+ return fail('缺少校区或部门信息');
+ }
+
+ try {
+ $stats = [
+ 'team_members' => $this->getTeamMembers($campusId, $deptId),
+ 'team_performance' => $this->getTeamPerformance($campusId, $deptId, $startDate, $endDate),
+ 'ranking' => $this->getTeamRanking($campusId, $deptId, $startDate, $endDate)
+ ];
+
+ return success($stats);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取渠道分布统计
+ */
+ public function getChannelDistribution()
+ {
+ $staffId = $this->request->post('staff_id');
+ $startDate = $this->request->post('start_date');
+ $endDate = $this->request->post('end_date');
+
+ try {
+ $distribution = Db::table('school_customer_resources')
+ ->field('source_channel, COUNT(*) as count')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('created_at', '<=', $endDate);
+ })
+ ->group('source_channel')
+ ->select();
+
+ return success($distribution);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取来源分布统计
+ */
+ public function getSourceDistribution()
+ {
+ $staffId = $this->request->post('staff_id');
+ $startDate = $this->request->post('start_date');
+ $endDate = $this->request->post('end_date');
+
+ try {
+ $distribution = Db::table('school_customer_resources')
+ ->field('source, COUNT(*) as count')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('created_at', '<=', $endDate);
+ })
+ ->whereNotNull('source')
+ ->where('source', '<>', '')
+ ->group('source')
+ ->select();
+
+ return success($distribution);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取转化漏斗数据
+ */
+ public function getConversionFunnel()
+ {
+ $staffId = $this->request->post('staff_id');
+ $startDate = $this->request->post('start_date');
+ $endDate = $this->request->post('end_date');
+
+ try {
+ // 总资源数
+ $totalResources = Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('created_at', '<=', $endDate);
+ })
+ ->count();
+
+ // 成交客户数
+ $convertedResources = Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.consultant', $staffId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('r.created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('r.created_at', '<=', $endDate);
+ })
+ ->count();
+
+ $funnel = [
+ 'total_resources' => $totalResources,
+ 'converted_resources' => $convertedResources,
+ 'conversion_rate' => $totalResources > 0 ? round($convertedResources * 100 / $totalResources, 1) : 0
+ ];
+
+ return success($funnel);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取月度趋势数据
+ */
+ public function getMonthlyTrend()
+ {
+ $staffId = $this->request->post('staff_id');
+ $months = $this->request->post('months', 6);
+
+ try {
+ $results = Db::table('school_customer_resources')
+ ->field("DATE_FORMAT(created_at, '%Y-%m') as month, COUNT(*) as count")
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->group('month')
+ ->order('month DESC')
+ ->limit($months)
+ ->select();
+
+ return success(array_reverse($results));
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取提成明细数据
+ */
+ public function getCommissionBreakdown()
+ {
+ $staffId = $this->request->post('staff_id');
+ $startDate = $this->request->post('start_date');
+ $endDate = $this->request->post('end_date');
+
+ try {
+ $results = Db::table('school_performance_records')
+ ->field('performance_type, SUM(performance_value) as total_amount, COUNT(*) as count')
+ ->where('staff_id', $staffId)
+ ->whereIn('performance_type', ['sales', 'marketing', 'consultant'])
+ ->where('order_status', 'completed')
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('created_at', '<=', $endDate);
+ })
+ ->group('performance_type')
+ ->select();
+
+ return success($results);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ /**
+ * 获取奖励历史数据
+ */
+ public function getBonusHistory()
+ {
+ $staffId = $this->request->post('staff_id');
+ $months = $this->request->post('months', 6);
+
+ try {
+ $results = Db::table('school_salary')
+ ->field('salary_month, other_subsidies as amount')
+ ->where('staff_id', $staffId)
+ ->where('other_subsidies', '>', 0)
+ ->order('salary_month DESC')
+ ->limit($months)
+ ->select();
+
+ return success($results);
+
+ } catch (\Exception $e) {
+ return fail($e->getMessage());
+ }
+ }
+
+ // ========== 私有查询方法 ==========
+
+ /**
+ * 获取资源数量
+ */
+ private function getResourceCount($staffId, $startDate = null, $endDate = null)
+ {
+ return Db::table('school_customer_resources')
+ ->where('consultant', $staffId)
+ ->where('deleted_at', 0)
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('created_at', '<=', $endDate);
+ })
+ ->count();
+ }
+
+ /**
+ * 获取成交客户数量
+ */
+ private function getConvertedCount($staffId, $startDate = null, $endDate = null)
+ {
+ return Db::table('school_customer_resources r')
+ ->join('school_order_table o', 'r.id = o.resource_id')
+ ->where('r.consultant', $staffId)
+ ->where('r.deleted_at', 0)
+ ->where('o.order_type', '1')
+ ->where('o.order_status', 'paid')
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('r.created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('r.created_at', '<=', $endDate);
+ })
+ ->count();
+ }
+
+ /**
+ * 获取业绩数据
+ */
+ private function getPerformance($staffId, $startDate = null, $endDate = null)
+ {
+ $result = Db::table('school_order_table')
+ ->where('staff_id', $staffId)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('payment_time', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('payment_time', '<=', $endDate);
+ })
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取提成数据
+ */
+ private function getCommission($staffId, $startDate = null, $endDate = null)
+ {
+ $result = Db::table('school_performance_records')
+ ->where('staff_id', $staffId)
+ ->whereIn('performance_type', ['sales', 'marketing', 'consultant'])
+ ->where('order_status', 'completed')
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('created_at', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('created_at', '<=', $endDate);
+ })
+ ->sum('performance_value');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取奖励数据
+ */
+ private function getBonus($staffId, $startDate = null, $endDate = null)
+ {
+ $result = Db::table('school_salary')
+ ->where('staff_id', $staffId)
+ ->where('other_subsidies', '>', 0)
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('salary_month', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('salary_month', '<=', $endDate);
+ })
+ ->sum('other_subsidies');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取团队成员
+ */
+ private function getTeamMembers($campusId, $deptId)
+ {
+ return Db::table('school_personnel p')
+ ->join('school_campus_person_role cpr', 'p.id = cpr.person_id')
+ ->where('cpr.campus_id', $campusId)
+ ->where('cpr.dept_id', $deptId)
+ ->where('p.status', 1)
+ ->where('p.deleted_at', '0')
+ ->field('p.id, p.name')
+ ->select();
+ }
+
+ /**
+ * 获取团队业绩
+ */
+ private function getTeamPerformance($campusId, $deptId, $startDate = null, $endDate = null)
+ {
+ $members = $this->getTeamMembers($campusId, $deptId);
+ $memberIds = array_column($members, 'id');
+
+ $result = Db::table('school_order_table')
+ ->whereIn('staff_id', $memberIds)
+ ->where('order_type', '1')
+ ->where('order_status', 'paid')
+ ->when($startDate, function($query) use ($startDate) {
+ return $query->whereTime('payment_time', '>=', $startDate);
+ })
+ ->when($endDate, function($query) use ($endDate) {
+ return $query->whereTime('payment_time', '<=', $endDate);
+ })
+ ->sum('order_amount');
+
+ return $result ?: 0;
+ }
+
+ /**
+ * 获取团队排名
+ */
+ private function getTeamRanking($campusId, $deptId, $startDate = null, $endDate = null)
+ {
+ $members = $this->getTeamMembers($campusId, $deptId);
+ $memberIds = array_column($members, 'id');
+
+ $ranking = [];
+ foreach ($members as $member) {
+ $performance = $this->getPerformance($member['id'], $startDate, $endDate);
+ $ranking[] = [
+ 'staff_id' => $member['id'],
+ 'staff_name' => $member['name'],
+ 'performance' => $performance
+ ];
+ }
+
+ // 按业绩排序
+ usort($ranking, function($a, $b) {
+ return $b['performance'] <=> $a['performance'];
+ });
+
+ return $ranking;
+ }
+}
\ No newline at end of file
diff --git a/niucloud/app/api/route/route.php b/niucloud/app/api/route/route.php
index 720e137c..a688a682 100644
--- a/niucloud/app/api/route/route.php
+++ b/niucloud/app/api/route/route.php
@@ -446,6 +446,13 @@ Route::group(function () {
Route::get('statistics/getStaffStatistics', 'apiController.Statistics/getStaffStatistics');
+ //市场统计API接口
+ Route::post('market_stats/getPersonalStats', 'MarketStats/getPersonalStats');
+ Route::post('market_stats/getTeamStats', 'MarketStats/getTeamStats');
+ Route::post('market_stats/getChannelDistribution', 'MarketStats/getChannelDistribution');
+ Route::post('market_stats/getConversionFunnel', 'MarketStats/getConversionFunnel');
+ Route::post('market_stats/getMonthlyTrend', 'MarketStats/getMonthlyTrend');
+
//报销
Route::get('personnel/reimbursement_list', 'apiController.Personnel/reimbursement_list');
Route::post('personnel/reimbursement_add', 'apiController.Personnel/reimbursement_add');
diff --git a/niucloud/app/view/dashboard/main.html b/niucloud/app/view/dashboard/main.html
new file mode 100644
index 00000000..5586f928
--- /dev/null
+++ b/niucloud/app/view/dashboard/main.html
@@ -0,0 +1,590 @@
+
+
+
+
+
+ {$pageTitle}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ formatValue(stat.value) }}{{ stat.unit }}
+
{{ stat.label }}
+
+
+ {{ stat.trend }}
+
+
+
+
+
+
+
+
资源分析
+
+
+
渠道分布
+
+
+
+
来源分布
+
+
+
+
转化漏斗
+
+
+
+
月度趋势
+
+
+
+
+
+
+
+
收益分析
+
+
+
提成明细
+
+
+
+
奖励历史
+
+
+
+
+
+
+
+
团队数据
+
+
+
团队成员
+
{{ pageData.team_overview.total_members }}
+
活跃成员
+
+
+
团队资源
+
{{ pageData.team_overview.total_resources }}
+
本月新增
+
+
+
团队成交
+
{{ pageData.team_overview.total_converted }}
+
本月成交
+
+
+
团队业绩
+
{{ formatValue(pageData.team_overview.total_performance) }}
+
本月业绩
+
+
+
+
+
团队成员排名
+
+
+
+
+ | 排名 |
+ 姓名 |
+ 资源数 |
+ 成交数 |
+ 业绩 |
+ 提成 |
+
+
+
+
+ |
+
+ {{ index + 1 }}
+
+ |
+ {{ member.staff_name }} |
+ {{ member.resource_count }} |
+ {{ member.converted_count }} |
+ {{ formatValue(member.performance) }} |
+ {{ formatValue(member.commission) }} |
+
+
+
+
+
+
+
+
+
+
部门数据
+
+
+
部门总数
+
{{ pageData.dept_overview.total_depts }}
+
活跃部门
+
+
+
部门资源
+
{{ pageData.dept_overview.total_resources }}
+
本月新增
+
+
+
部门业绩
+
{{ formatValue(pageData.dept_overview.total_performance) }}
+
本月业绩
+
+
+
转化率
+
{{ pageData.dept_overview.conversion_rate }}%
+
平均转化
+
+
+
+
+
部门业绩排名
+
+
+
+
+ | 排名 |
+ 部门名称 |
+ 业绩 |
+ 占比 |
+
+
+
+
+ |
+
+ {{ index + 1 }}
+
+ |
+ {{ dept.dept_name }} |
+ {{ formatValue(dept.performance) }} |
+ {{ getPercentage(dept.performance, pageData.dept_overview.total_performance) }}% |
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/niucloud/public/static/css/dashboard.css b/niucloud/public/static/css/dashboard.css
new file mode 100644
index 00000000..09cd2706
--- /dev/null
+++ b/niucloud/public/static/css/dashboard.css
@@ -0,0 +1,569 @@
+/* 市场人员业绩管理系统 - Dashboard样式 */
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ min-height: 100vh;
+ color: #333;
+}
+
+/* 页面头部 */
+.header {
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.95) 0%, rgba(118, 75, 162, 0.95) 100%);
+ color: white;
+ padding: 20px 30px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+}
+
+.header h1 {
+ font-size: 28px;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.header h1 i {
+ font-size: 32px;
+}
+
+.user-info {
+ display: flex;
+ align-items: center;
+ gap: 15px;
+ font-size: 16px;
+}
+
+.role-badge {
+ background: rgba(255, 255, 255, 0.2);
+ padding: 8px 16px;
+ border-radius: 25px;
+ font-size: 14px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ border: 1px solid rgba(255, 255, 255, 0.3);
+}
+
+.role-badge i {
+ font-size: 12px;
+}
+
+/* 数据筛选器 */
+.filter-container {
+ background: rgba(255, 255, 255, 0.95);
+ margin: 20px;
+ padding: 20px;
+ border-radius: 15px;
+ display: flex;
+ gap: 20px;
+ align-items: center;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+}
+
+.filter-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+
+.filter-item label {
+ font-weight: 600;
+ color: #374151;
+ font-size: 14px;
+}
+
+.filter-item select {
+ padding: 10px 15px;
+ border: 2px solid #e5e7eb;
+ border-radius: 8px;
+ background: white;
+ color: #374151;
+ font-size: 14px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.2s ease;
+}
+
+.filter-item select:hover {
+ border-color: #667eea;
+}
+
+.filter-item select:focus {
+ outline: none;
+ border-color: #667eea;
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
+}
+
+.btn-export {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: white;
+ border: none;
+ padding: 12px 20px;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.btn-export:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
+}
+
+/* 统计卡片容器 */
+.stats-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
+ gap: 25px;
+ padding: 0 20px 20px;
+}
+
+/* 统计卡片 */
+.stat-card {
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 20px;
+ padding: 25px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ display: flex;
+ align-items: center;
+ gap: 20px;
+ transition: all 0.3s ease;
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.stat-card:hover {
+ transform: translateY(-5px);
+ box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
+}
+
+.stat-icon {
+ width: 70px;
+ height: 70px;
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ font-size: 28px;
+ box-shadow: 0 8px 25px rgba(102, 126, 234, 0.3);
+}
+
+.stat-content {
+ flex: 1;
+}
+
+.stat-value {
+ font-size: 32px;
+ font-weight: 700;
+ color: #1f2937;
+ margin-bottom: 5px;
+}
+
+.stat-label {
+ font-size: 14px;
+ color: #6b7280;
+ font-weight: 500;
+ margin-bottom: 8px;
+}
+
+.stat-trend {
+ font-size: 13px;
+ font-weight: 600;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+.trend-up {
+ color: #10b981;
+}
+
+.trend-down {
+ color: #ef4444;
+}
+
+/* 区域容器 */
+.section {
+ padding: 20px;
+}
+
+.section h2 {
+ font-size: 24px;
+ font-weight: 700;
+ color: white;
+ margin-bottom: 25px;
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+
+.section h2 i {
+ font-size: 28px;
+}
+
+/* 图表容器 */
+.charts-container {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
+ gap: 25px;
+}
+
+.chart-card {
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 20px;
+ padding: 25px;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.chart-card h3 {
+ font-size: 16px;
+ font-weight: 600;
+ color: #374151;
+ margin-bottom: 20px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.chart-card h3 i {
+ font-size: 18px;
+ color: #667eea;
+}
+
+/* 团队总览 */
+.team-overview {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
+ gap: 20px;
+ margin-bottom: 30px;
+}
+
+.overview-card {
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 15px;
+ padding: 20px;
+ text-align: center;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+ transition: all 0.3s ease;
+}
+
+.overview-card:hover {
+ transform: translateY(-3px);
+ box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
+}
+
+.overview-card h3 {
+ font-size: 14px;
+ color: #6b7280;
+ margin-bottom: 10px;
+ font-weight: 600;
+}
+
+.overview-card h3 i {
+ font-size: 16px;
+ color: #667eea;
+ margin-bottom: 5px;
+}
+
+.overview-value {
+ font-size: 36px;
+ font-weight: 700;
+ color: #1f2937;
+ margin-bottom: 5px;
+}
+
+.overview-desc {
+ font-size: 12px;
+ color: #9ca3af;
+ font-weight: 500;
+}
+
+/* 排名区域 */
+.ranking-section h3 {
+ font-size: 18px;
+ font-weight: 600;
+ color: white;
+ margin-bottom: 20px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.ranking-section h3 i {
+ font-size: 20px;
+}
+
+.ranking-table {
+ background: rgba(255, 255, 255, 0.95);
+ border-radius: 15px;
+ overflow: hidden;
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
+ backdrop-filter: blur(10px);
+ border: 1px solid rgba(255, 255, 255, 0.2);
+}
+
+.ranking-table table {
+ width: 100%;
+ border-collapse: collapse;
+}
+
+.ranking-table th,
+.ranking-table td {
+ padding: 15px 20px;
+ text-align: left;
+ border-bottom: 1px solid rgba(0, 0, 0, 0.05);
+}
+
+.ranking-table th {
+ background: rgba(102, 126, 234, 0.1);
+ font-weight: 600;
+ color: #374151;
+ font-size: 14px;
+}
+
+.ranking-table th i {
+ margin-right: 5px;
+ color: #667eea;
+}
+
+.ranking-table td {
+ font-size: 14px;
+ color: #374151;
+ font-weight: 500;
+}
+
+.ranking-table tr:hover {
+ background: rgba(102, 126, 234, 0.05);
+}
+
+.ranking-table tr:last-child td {
+ border-bottom: none;
+}
+
+/* 排名徽章 */
+.rank-badge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ font-size: 12px;
+ font-weight: 700;
+ color: white;
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
+}
+
+.rank-first {
+ background: linear-gradient(135deg, #ffd700, #ffed4e);
+ color: #92400e;
+}
+
+.rank-second {
+ background: linear-gradient(135deg, #c0c0c0, #e5e5e5);
+ color: #374151;
+}
+
+.rank-third {
+ background: linear-gradient(135deg, #cd7f32, #e5a25d);
+ color: white;
+}
+
+.rank-other {
+ background: linear-gradient(135deg, #6b7280, #9ca3af);
+}
+
+/* 响应式设计 */
+@media (max-width: 1200px) {
+ .stats-container {
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
+ }
+
+ .charts-container {
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
+ }
+}
+
+@media (max-width: 768px) {
+ .header {
+ flex-direction: column;
+ gap: 15px;
+ text-align: center;
+ }
+
+ .filter-container {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 15px;
+ }
+
+ .stats-container {
+ grid-template-columns: 1fr;
+ padding: 0 15px 15px;
+ }
+
+ .charts-container {
+ grid-template-columns: 1fr;
+ }
+
+ .team-overview {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .ranking-table {
+ overflow-x: auto;
+ }
+
+ .ranking-table table {
+ min-width: 600px;
+ }
+}
+
+@media (max-width: 480px) {
+ .header h1 {
+ font-size: 24px;
+ }
+
+ .user-info {
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .stat-card {
+ flex-direction: column;
+ text-align: center;
+ gap: 15px;
+ }
+
+ .stat-icon {
+ width: 60px;
+ height: 60px;
+ font-size: 24px;
+ }
+
+ .team-overview {
+ grid-template-columns: 1fr;
+ }
+
+ .overview-value {
+ font-size: 28px;
+ }
+}
+
+/* 动画效果 */
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translateY(30px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.stat-card,
+.chart-card,
+.overview-card {
+ animation: fadeInUp 0.6s ease-out;
+}
+
+.chart-card:nth-child(2) {
+ animation-delay: 0.1s;
+}
+
+.chart-card:nth-child(3) {
+ animation-delay: 0.2s;
+}
+
+.chart-card:nth-child(4) {
+ animation-delay: 0.3s;
+}
+
+/* 滚动条样式 */
+::-webkit-scrollbar {
+ width: 8px;
+}
+
+::-webkit-scrollbar-track {
+ background: rgba(255, 255, 255, 0.1);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.3);
+ border-radius: 4px;
+}
+
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.5);
+}
+
+/* 加载动画 */
+.loading-spinner {
+ text-align: center;
+}
+
+.loading-spinner i {
+ font-size: 48px;
+ color: #667eea;
+ animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* 特殊效果 */
+.stat-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, #667eea, #764ba2, #667eea);
+ border-radius: 20px 20px 0 0;
+ opacity: 0.7;
+}
+
+.overview-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 2px;
+ background: linear-gradient(90deg, #667eea, #764ba2);
+ border-radius: 15px 15px 0 0;
+ opacity: 0.5;
+}
\ No newline at end of file