From 5b616634ab522e4d6602b621c9c5b5aa84aec9bc Mon Sep 17 00:00:00 2001 From: MiAn Date: Fri, 30 Oct 2026 17:39:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=87=8D=E6=9E=84=E8=BF=90=E8=90=A5?= =?UTF-8?q?=E7=9C=8B=E6=9D=BF=EF=BC=8C=E6=96=B0=E5=A2=9E=E5=A4=9A=E7=BB=B4?= =?UTF-8?q?=E5=BA=A6=E6=95=B0=E6=8D=AE=E5=B1=95=E7=A4=BA=E4=B8=8E=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增同城便民生活服务平台标识,重构看板为业务总览与运营看板 新增待办督办中心、营收构成分析、热门分类与区域排行等模块 新增近7/14/30天多时段趋势切换,支持发布量/订单额/用户/曝光量指标切换 新增平台商业与生态资产统计模块,新增待办预警与空状态提示 新增趋势数据汇总与明细表联动,优化响应式布局与交互样式 新增商家管理、举报投诉等语言翻译项,新增登录推荐图 --- application/admin/controller/fc/Dashboard.php | 394 +++-- application/admin/lang/zh-cn/fc/dashboard.php | 113 +- .../admin/view/fc/dashboard/index.html | 470 ++++-- application/admin/view/index/login.html | 1405 +++++++++++++---- public/assets/css/backend-theme.css | 747 +++++++-- public/assets/img/login-recommend.jpg | Bin 0 -> 63847 bytes public/assets/js/backend/fc/dashboard.js | 396 +++-- 7 files changed, 2723 insertions(+), 802 deletions(-) create mode 100644 public/assets/img/login-recommend.jpg diff --git a/application/admin/controller/fc/Dashboard.php b/application/admin/controller/fc/Dashboard.php index 2acfeba..51403cb 100644 --- a/application/admin/controller/fc/Dashboard.php +++ b/application/admin/controller/fc/Dashboard.php @@ -6,124 +6,334 @@ use app\common\controller\Backend; use think\Db; /** - * 同城-数据总览 + * 同城分类信息 - 数据总览与运营看板 * - * 后台核心看板:今日/累计发布量、待审核数、今日/累计订单额、会员数、商家数、 - * 分销佣金(待结算/已结算)、提现待审数,以及近 14 天趋势(发布量 / 订单额 / 新增注册用户)。 - * 数据一律由本控制器直接查库(fa_fc_* / fa_user),不在视图里写 SQL,也不走 /api/v1/admin/**。 + * 后台核心运营仪表盘: + * 1. 今日核心指标与昨日对比(发布量 / 订单额 / 新增用户 / 浏览曝光量) + * 2. 待办督办中心(帖子待审 / 商家待审 / 提现待审 / 举报待处理) + * 3. 多时段多维趋势分析(近 7 天 / 14 天 / 30 天,支持发布量、订单额、新增用户、浏览量平滑折线图) + * 4. 营收来源构成分析(会员购买 / 发帖付费 / 置顶推广 / 付费查看等占比环形图) + * 5. 热门分类发帖分布与同城区域活跃排行 + * 6. 用户、会员、商户与分销生态资产全景 * * @icon fa fa-dashboard */ class Dashboard extends Backend { - /** 趋势统计天数 */ - const TREND_DAYS = 14; - /** - * 查看 + * 数据总览首页入口与数据下发 + * + * @return string|\think\response\Json */ public function index() { $todayStart = strtotime('today'); $todayEnd = $todayStart + 86400 - 1; + $yesterdayStart = $todayStart - 86400; + $yesterdayEnd = $todayStart - 1; + + /* ========================================================= + * 1. 核心 KPI(今日、较昨日对比、累计总量) + * ========================================================= */ + // 发布量 + $todayPost = (int)Db::name('fc_post')->where('createtime', 'between', [$todayStart, $todayEnd])->count(); + $yesterdayPost = (int)Db::name('fc_post')->where('createtime', 'between', [$yesterdayStart, $yesterdayEnd])->count(); + $totalPost = (int)Db::name('fc_post')->count(); + + // 订单额与笔数(仅统计已支付订单) + $todayOrderAmount = round((float)Db::name('fc_order')->where('status', 'paid') + ->whereTime('paytime|createtime', 'between', [$todayStart, $todayEnd])->sum('amount'), 2); + $yesterdayOrderAmount = round((float)Db::name('fc_order')->where('status', 'paid') + ->whereTime('paytime|createtime', 'between', [$yesterdayStart, $yesterdayEnd])->sum('amount'), 2); + $totalOrderAmount = round((float)Db::name('fc_order')->where('status', 'paid')->sum('amount'), 2); + $todayOrderCount = (int)Db::name('fc_order')->where('status', 'paid') + ->whereTime('paytime|createtime', 'between', [$todayStart, $todayEnd])->count(); + $totalOrderCount = (int)Db::name('fc_order')->where('status', 'paid')->count(); + + // 用户与会员 + $todayUser = (int)Db::name('user')->where('createtime', 'between', [$todayStart, $todayEnd])->count(); + $yesterdayUser = (int)Db::name('user')->where('createtime', 'between', [$yesterdayStart, $yesterdayEnd])->count(); + $totalUser = (int)Db::name('user')->count(); + $memberCount = (int)Db::name('fc_user_ext')->where('is_member', 1)->count(); + $merchantCount = (int)Db::name('fc_merchant')->count(); + $recommendMerchantCount = (int)Db::name('fc_merchant')->where('recommend', 1)->count(); + + // 浏览量 PV + $todayViews = (int)Db::name('fc_view_log')->where('createtime', 'between', [$todayStart, $todayEnd])->count(); + $yesterdayViews = (int)Db::name('fc_view_log')->where('createtime', 'between', [$yesterdayStart, $yesterdayEnd])->count(); + $totalViews = (int)Db::name('fc_post')->sum('views'); - /* ---------- 核心指标 ---------- */ $metrics = [ - // 发布量 - 'todayPost' => (int)Db::name('fc_post')->where('createtime', 'between', [$todayStart, $todayEnd])->count(), - 'totalPost' => (int)Db::name('fc_post')->count(), - 'pendingPost' => (int)Db::name('fc_post')->where('audit_status', 0)->count(), - // 订单额(仅统计已支付订单;paytime 缺失时回落 createtime) - 'todayOrderAmount' => round((float)Db::name('fc_order')->where('status', 'paid') - ->whereTime('paytime|createtime', 'between', [$todayStart, $todayEnd])->sum('amount'), 2), - 'totalOrderAmount' => round((float)Db::name('fc_order')->where('status', 'paid')->sum('amount'), 2), - // 用户 / 商家 - 'memberCount' => (int)Db::name('fc_user_ext')->where('is_member', 1)->count(), - 'merchantCount' => (int)Db::name('fc_merchant')->count(), - 'userCount' => (int)Db::name('user')->count(), - // 分销佣金 - 'commissionPending' => round((float)Db::name('fc_commission')->where('status', 0)->sum('amount'), 2), - 'commissionSettled' => round((float)Db::name('fc_commission')->where('status', 1)->sum('amount'), 2), - // 提现待审核 - 'withdrawAuditing' => (int)Db::name('fc_withdraw')->where('status', 'auditing')->count(), + 'todayPost' => $todayPost, + 'yesterdayPost' => $yesterdayPost, + 'postDelta' => $todayPost - $yesterdayPost, + 'totalPost' => $totalPost, + + 'todayOrderAmount' => $todayOrderAmount, + 'yesterdayOrderAmount' => $yesterdayOrderAmount, + 'orderDelta' => round($todayOrderAmount - $yesterdayOrderAmount, 2), + 'totalOrderAmount' => $totalOrderAmount, + 'todayOrderCount' => $todayOrderCount, + 'totalOrderCount' => $totalOrderCount, + + 'todayUser' => $todayUser, + 'yesterdayUser' => $yesterdayUser, + 'userDelta' => $todayUser - $yesterdayUser, + 'totalUser' => $totalUser, + 'memberCount' => $memberCount, + 'merchantCount' => $merchantCount, + 'recommendMerchant' => $recommendMerchantCount, + + 'todayViews' => $todayViews, + 'yesterdayViews' => $yesterdayViews, + 'viewsDelta' => $todayViews - $yesterdayViews, + 'totalViews' => $totalViews, ]; - /* ---------- 近 14 天趋势 ---------- */ - $dateStart = $todayStart - (self::TREND_DAYS - 1) * 86400; - $days = []; - for ($i = 0; $i < self::TREND_DAYS; $i++) { - $days[] = date('Y-m-d', $dateStart + $i * 86400); - } + /* ========================================================= + * 2. 待办督办中心(待审核事项与预警) + * ========================================================= */ + $pendingPost = (int)Db::name('fc_post')->where('audit_status', 0)->count(); + $pendingMerchant = (int)Db::name('fc_merchant')->where('audit_status', 0)->count(); + $pendingWithdraw = (int)Db::name('fc_withdraw')->where('status', 'auditing')->count(); + $pendingWithdrawAmount = round((float)Db::name('fc_withdraw')->where('status', 'auditing')->sum('amount'), 2); + $pendingReport = (int)Db::name('fc_report')->where('status', 'pending')->count(); + $pendingTotal = $pendingPost + $pendingMerchant + $pendingWithdraw + $pendingReport; - // 每天发布量 - $postsMap = []; - $posts = Db::name('fc_post') - ->where('createtime', 'between time', [$dateStart, $todayEnd]) - ->fieldRaw("FROM_UNIXTIME(`createtime`,'%Y-%m-%d') AS day, COUNT(*) AS num") - ->group('day') - ->select(); - foreach ($posts as $row) { - $postsMap[$row['day']] = (int)$row['num']; - } + $pending = [ + 'total' => $pendingTotal, + 'post' => $pendingPost, + 'merchant' => $pendingMerchant, + 'withdraw' => $pendingWithdraw, + 'withdrawAmount' => $pendingWithdrawAmount, + 'report' => $pendingReport, + ]; - // 每天订单额(已支付;paytime 为空时按 createtime 归属) - $ordersMap = []; - $orders = Db::name('fc_order') + /* ========================================================= + * 3. 平台生态与分销资产 + * ========================================================= */ + $commissionPending = round((float)Db::name('fc_commission')->where('status', 0)->sum('amount'), 2); + $commissionSettled = round((float)Db::name('fc_commission')->where('status', 1)->sum('amount'), 2); + $distributorCount = (int)Db::name('fc_dist_relation')->distinct(true)->field('parent_id')->count(); + + $ecosystem = [ + 'commissionPending' => $commissionPending, + 'commissionSettled' => $commissionSettled, + 'distributorCount' => $distributorCount, + 'memberRate' => $totalUser > 0 ? round(($memberCount / $totalUser) * 100, 1) : 0, + ]; + + /* ========================================================= + * 4. 营收构成分析(按订单类型统计已支付实收) + * ========================================================= */ + $orderTypeLabels = [ + 'member' => 'VIP会员购买', + 'post' => '发布信息付费', + 'payview' => '付费查看电话', + 'recharge' => '钱包充值', + 'top' => '置顶推广套餐', + 'refresh' => '信息刷新', + ]; + + $rawRevenue = Db::name('fc_order') ->where('status', 'paid') - ->whereTime('paytime|createtime', 'between', [$dateStart, $todayEnd]) - ->fieldRaw("FROM_UNIXTIME(IFNULL(`paytime`,`createtime`),'%Y-%m-%d') AS day, SUM(`amount`) AS num") - ->group('day') + ->fieldRaw("type, count(*) as count, sum(amount) as amount") + ->group('type') + ->order('amount', 'desc') ->select(); - foreach ($orders as $row) { - $ordersMap[$row['day']] = round((float)$row['num'], 2); - } - // 每天新增注册用户(fa_user.createtime) - $membersMap = []; - $members = Db::name('user') - ->where('createtime', 'between time', [$dateStart, $todayEnd]) - ->fieldRaw("FROM_UNIXTIME(`createtime`,'%Y-%m-%d') AS day, COUNT(*) AS num") - ->group('day') - ->select(); - foreach ($members as $row) { - $membersMap[$row['day']] = (int)$row['num']; - } - - /* ---------- 今日较昨日的变化(用于首页涨跌标识) ---------- */ - $yesterday = date('Y-m-d', $todayStart - 86400); - $metrics['yesterdayPost'] = isset($postsMap[$yesterday]) ? $postsMap[$yesterday] : 0; - $metrics['yesterdayOrder'] = isset($ordersMap[$yesterday]) ? $ordersMap[$yesterday] : 0; - $metrics['postDelta'] = $metrics['todayPost'] - $metrics['yesterdayPost']; - $metrics['orderDelta'] = round($metrics['todayOrderAmount'] - $metrics['yesterdayOrder'], 2); - - $trend = [ - 'dates' => $days, - 'posts' => [], - 'orders' => [], - 'members' => [], - ]; - $trendRows = []; - foreach ($days as $day) { - $postNum = isset($postsMap[$day]) ? $postsMap[$day] : 0; - $orderNum = isset($ordersMap[$day]) ? $ordersMap[$day] : 0; - $memberNum = isset($membersMap[$day]) ? $membersMap[$day] : 0; - - $trend['posts'][] = $postNum; - $trend['orders'][] = $orderNum; - $trend['members'][] = $memberNum; - - $trendRows[] = [ - 'date' => $day, - 'posts' => $postNum, - 'orders' => $orderNum, - 'members' => $memberNum, + $revenueTotalSum = 0; + $revenueTypes = []; + foreach ($rawRevenue as $r) { + $amt = round((float)$r['amount'], 2); + $cnt = (int)$r['count']; + $label = isset($orderTypeLabels[$r['type']]) ? $orderTypeLabels[$r['type']] : ($r['type'] ? $r['type'] : '其他服务'); + $revenueTotalSum += $amt; + $revenueTypes[] = [ + 'type' => $r['type'], + 'label' => $label, + 'count' => $cnt, + 'amount' => $amt, ]; } + foreach ($revenueTypes as &$item) { + $item['percent'] = $revenueTotalSum > 0 ? round(($item['amount'] / $revenueTotalSum) * 100, 1) : 0; + } + unset($item); + + /* ========================================================= + * 5. 热门分类发帖分布与同城区域排行 + * ========================================================= */ + // 一级分类发帖分布 + $rawCategories = Db::name('fc_category') + ->alias('c') + ->join('__FC_POST__ p', '(p.category_id = c.id OR p.category_id IN (SELECT sub.id FROM ' . config('database.prefix') . 'fc_category sub WHERE sub.pid = c.id))', 'LEFT') + ->where('c.pid', 0) + ->field('c.id, c.name, count(p.id) as post_count') + ->group('c.id') + ->order('post_count', 'desc') + ->select(); + + $categoryTotal = 0; + $categoryRanks = []; + foreach ($rawCategories as $c) { + $cnt = (int)$c['post_count']; + $categoryTotal += $cnt; + $categoryRanks[] = [ + 'id' => $c['id'], + 'name' => $c['name'], + 'count' => $cnt, + ]; + } + foreach ($categoryRanks as &$cItem) { + $cItem['percent'] = $categoryTotal > 0 ? round(($cItem['count'] / $categoryTotal) * 100, 1) : 0; + } + unset($cItem); + + // 同城热门地区 Top 5 + $rawAreas = Db::name('fc_post') + ->fieldRaw("IF(area != '', area, city) as region_name, count(*) as count") + ->where("area != '' OR city != ''") + ->group('region_name') + ->order('count', 'desc') + ->limit(5) + ->select(); + $areaRanks = []; + $areaMaxCount = 1; + foreach ($rawAreas as $ar) { + $cnt = (int)$ar['count']; + if ($cnt > $areaMaxCount) { + $areaMaxCount = $cnt; + } + $areaRanks[] = [ + 'region_name' => $ar['region_name'] ? $ar['region_name'] : '未知区域', + 'count' => $cnt, + ]; + } + foreach ($areaRanks as &$arItem) { + $arItem['percent'] = round(($arItem['count'] / $areaMaxCount) * 100, 1); + } + unset($arItem); + + /* ========================================================= + * 6. 多时段趋势数据计算(近 7 天 / 14 天 / 30 天) + * ========================================================= */ + $maxTrendDays = 30; + $trendStart = $todayStart - ($maxTrendDays - 1) * 86400; + + // 发帖趋势 + $postsMap = []; + $postsQuery = Db::name('fc_post') + ->where('createtime', 'between', [$trendStart, $todayEnd]) + ->fieldRaw("FROM_UNIXTIME(`createtime`,'%Y-%m-%d') AS day, COUNT(*) AS num") + ->group('day') + ->select(); + foreach ($postsQuery as $row) { + $postsMap[$row['day']] = (int)$row['num']; + } + + // 订单额趋势 + $ordersMap = []; + $ordersQuery = Db::name('fc_order') + ->where('status', 'paid') + ->whereTime('paytime|createtime', 'between', [$trendStart, $todayEnd]) + ->fieldRaw("FROM_UNIXTIME(IFNULL(`paytime`,`createtime`),'%Y-%m-%d') AS day, SUM(`amount`) AS num") + ->group('day') + ->select(); + foreach ($ordersQuery as $row) { + $ordersMap[$row['day']] = round((float)$row['num'], 2); + } + + // 新增用户趋势 + $usersMap = []; + $usersQuery = Db::name('user') + ->where('createtime', 'between', [$trendStart, $todayEnd]) + ->fieldRaw("FROM_UNIXTIME(`createtime`,'%Y-%m-%d') AS day, COUNT(*) AS num") + ->group('day') + ->select(); + foreach ($usersQuery as $row) { + $usersMap[$row['day']] = (int)$row['num']; + } + + // 浏览量趋势 + $viewsMap = []; + $viewsQuery = Db::name('fc_view_log') + ->where('createtime', 'between', [$trendStart, $todayEnd]) + ->fieldRaw("FROM_UNIXTIME(`createtime`,'%Y-%m-%d') AS day, COUNT(*) AS num") + ->group('day') + ->select(); + foreach ($viewsQuery as $row) { + $viewsMap[$row['day']] = (int)$row['num']; + } + + // 计算 7/14/30 天数据包 + $ranges = [7, 14, 30]; + $allTrends = []; + foreach ($ranges as $span) { + $startTs = $todayStart - ($span - 1) * 86400; + $dates = []; + $posts = []; + $orders = []; + $users = []; + $views = []; + $rows = []; + for ($i = 0; $i < $span; $i++) { + $day = date('Y-m-d', $startTs + $i * 86400); + $p = isset($postsMap[$day]) ? $postsMap[$day] : 0; + $o = isset($ordersMap[$day]) ? $ordersMap[$day] : 0; + $u = isset($usersMap[$day]) ? $usersMap[$day] : 0; + $v = isset($viewsMap[$day]) ? $viewsMap[$day] : 0; + + $dates[] = $day; + $posts[] = $p; + $orders[] = $o; + $users[] = $u; + $views[] = $v; + $rows[] = [ + 'date' => $day, + 'posts' => $p, + 'orders' => $o, + 'users' => $u, + 'views' => $v, + ]; + } + $allTrends[$span] = [ + 'dates' => $dates, + 'posts' => $posts, + 'orders' => $orders, + 'users' => $users, + 'views' => $views, + 'rows' => array_reverse($rows), + ]; + } + + // 如果是 AJAX 请求局部刷新趋势,可直接返回对应时段数据 + if ($this->request->isAjax() && $this->request->get('action') === 'trend') { + $days = (int)$this->request->get('days', 14); + $trendData = isset($allTrends[$days]) ? $allTrends[$days] : $allTrends[14]; + return json(['code' => 1, 'msg' => 'ok', 'data' => $trendData]); + } + + // 默认 14 天用于模板首屏初始化 + $defaultTrend = $allTrends[14]; + + /* ========================================================= + * 7. 视图变量绑定与前端配置传递 + * ========================================================= */ $this->view->assign('metrics', $metrics); - $this->view->assign('trendRows', $trendRows); + $this->view->assign('pending', $pending); + $this->view->assign('ecosystem', $ecosystem); + $this->view->assign('revenueTypes', $revenueTypes); + $this->view->assign('categoryRanks', $categoryRanks); + $this->view->assign('areaRanks', $areaRanks); + $this->view->assign('trendRows', $defaultTrend['rows']); $this->view->assign('updatedAt', date('Y-m-d H:i')); - $this->assignconfig('trend', $trend); + + // 向前端 RequireJS 传入完整时段数据包及分类分布,供平滑渲染交互 + $this->assignconfig('allTrends', $allTrends); + $this->assignconfig('revenueTypes', $revenueTypes); + $this->assignconfig('categoryRanks', $categoryRanks); return $this->view->fetch(); } diff --git a/application/admin/lang/zh-cn/fc/dashboard.php b/application/admin/lang/zh-cn/fc/dashboard.php index 8e6a5a0..79fb3fc 100644 --- a/application/admin/lang/zh-cn/fc/dashboard.php +++ b/application/admin/lang/zh-cn/fc/dashboard.php @@ -2,49 +2,92 @@ return [ '核心看板' => '核心看板', - '全部指标由后台直接统计;订单额仅统计「已支付」订单,佣金不含已驳回部分,提现为「待审核」笔数。' - => '全部指标由后台直接统计;订单额仅统计「已支付」订单,佣金不含已驳回部分,提现为「待审核」笔数。', - '业务总览' => '业务总览', + '业务总览与运营看板' => '业务总览与运营看板', + '同城便民生活服务平台' => '同城便民生活服务平台', '数据更新于' => '数据更新于', - '订单额仅统计已支付订单' => '订单额仅统计已支付订单', + '订单额仅统计已支付实收口径' => '订单额仅统计已支付实收口径', '内容管理' => '内容管理', + '商家管理' => '商家管理', '提现审核' => '提现审核', - '刷新数据' => '刷新数据', + '举报投诉' => '举报投诉', + '刷新看板' => '刷新看板', + '刷新' => '刷新', + '运营待办' => '运营待办', + '当前有' => '当前有', + '项待处理事项需要审核跟进:' => '项待处理事项需要审核跟进:', + '待审核信息' => '待审核信息', + '信息待审核' => '信息待审核', + '商家入驻待审' => '商家入驻待审', + '提现申请待审' => '提现申请待审', + '提现待审核' => '提现待审核', + '共' => '共', + '违规举报待处理' => '违规举报待处理', + '举报待处理' => '举报待处理', + '当前所有待审核帖子、商家、提现与举报均已处理完毕,平台健康运行中' + => '当前所有待审核帖子、商家、提现与举报均已处理完毕,平台健康运行中', '今日核心指标' => '今日核心指标', - '较昨日' => '较昨日', - '与昨日持平' => '与昨日持平', - '去审核' => '去审核', - '去处理' => '去处理', - '暂无待审核' => '暂无待审核', '今日发布量' => '今日发布量', - '累计发布量' => '累计发布量', - '待审核信息数' => '待审核信息数', - '今日订单额' => '今日订单额', - '累计订单额' => '累计订单额', - '会员数' => '会员数', - '商家数' => '商家数', - '注册用户数' => '注册用户数', - '平台累计' => '平台累计', - '分销佣金' => '分销佣金', - '待结算佣金' => '待结算佣金', - '已结算佣金' => '已结算佣金', - '待审核提现数' => '待审核提现数', - '近 14 天趋势' => '近 14 天趋势', + '篇' => '篇', + '较昨日' => '较昨日', + '持平' => '持平', + '累计' => '累计', + '今日订单实收' => '今日订单实收', + '今日新增用户' => '今日新增用户', + '人' => '人', + '累计注册' => '累计注册', + '今日信息曝光量' => '今日信息曝光量', + '次' => '次', + '累计曝光' => '累计曝光', + '业务多维趋势透视' => '业务多维趋势透视', + '发布量按发布时间归属,订单额按实际支付时间归属' + => '发布量按发布时间归属,订单额按实际支付时间归属', + '选择时段' => '选择时段', + '近 7 天' => '近 7 天', '近 14 天' => '近 14 天', - '趋势图' => '趋势图', - '近 14 天合计' => '近 14 天合计', + '近 30 天' => '近 30 天', '切换趋势指标' => '切换趋势指标', - '发布量按发布时间统计,订单额按支付时间统计' - => '发布量按发布时间统计,订单额按支付时间统计', - '近 14 天明细' => '近 14 天明细', - '展开查看每日数据' => '展开查看每日数据', - '收起明细' => '收起明细', - '近 14 天每日发布量、订单额与新增注册用户' - => '近 14 天每日发布量、订单额与新增注册用户', - '日期' => '日期', '发布量' => '发布量', '订单额' => '订单额', - '订单额(元)' => '订单额(元)', '新增用户' => '新增用户', - '新增注册用户' => '新增注册用户', + '信息曝光' => '信息曝光', + '时段累计总量' => '时段累计总量', + '日均水平' => '日均水平', + '单日最高峰值' => '单日最高峰值', + '业务趋势图' => '业务趋势图', + '营收来源构成' => '营收来源构成', + '实收合计' => '实收合计', + '营收构成环形图' => '营收构成环形图', + '笔' => '笔', + '暂无已支付订单数据' => '暂无已支付订单数据', + '热门分类发帖分布' => '热门分类发帖分布', + '累计发帖' => '累计发帖', + '暂无分类数据' => '暂无分类数据', + '同城活跃区域 Top 5' => '同城活跃区域 Top 5', + '基于信息发布所处行政区/县的热度分布' => '基于信息发布所处行政区/县的热度分布', + '查看帖子列表' => '查看帖子列表', + '全部帖子' => '全部帖子', + '暂无地区发布数据' => '暂无地区发布数据', + '平台商业与生态资产' => '平台商业与生态资产', + '会员沉淀、商户入驻及分销裂变体系' => '会员沉淀、商户入驻及分销裂变体系', + 'VIP会员体系' => 'VIP会员体系', + '有效VIP会员' => '有效VIP会员', + '会员转化率' => '会员转化率', + '商户入驻生态' => '商户入驻生态', + '入驻商户数' => '入驻商户数', + '推荐商家数' => '推荐商家数', + '二级分销裂变' => '二级分销裂变', + '推广分销员' => '推广分销员', + '已结佣金' => '已结佣金', + '待结佣金' => '待结佣金', + '每日数据明细 (近 14 天)' => '每日数据明细 (近 14 天)', + '每日数据明细 (近 %s 天)' => '每日数据明细 (近 %s 天)', + '展开明细' => '展开明细', + '收起明细' => '收起明细', + '日期' => '日期', + '发布量 (篇)' => '发布量 (篇)', + '订单实收 (元)' => '订单实收 (元)', + '新增注册用户 (人)' => '新增注册用户 (人)', + '信息曝光量 (次)' => '信息曝光量 (次)', + '暂无明细数据' => '暂无明细数据', + '元' => '元', ]; diff --git a/application/admin/view/fc/dashboard/index.html b/application/admin/view/fc/dashboard/index.html index a754fcb..4814e48 100644 --- a/application/admin/view/fc/dashboard/index.html +++ b/application/admin/view/fc/dashboard/index.html @@ -1,188 +1,424 @@
+
-
-

{:__('业务总览')}

+
+

{:__('业务总览与运营看板')}

- {:__('数据更新于')} {$updatedAt} · {:__('订单额仅统计已支付订单')} + {:__('同城便民生活服务平台')} · {:__('数据更新于')} {$updatedAt} · {:__('订单额仅统计已支付实收口径')}

+ + {if $pending.total > 0} +
+
+ {:__('运营待办')} + {:__('当前有')} {$pending.total} {:__('项待处理事项需要审核跟进:')} +
+ +
+ {else} +
+ + {:__('当前所有待审核帖子、商家、提现与举报均已处理完毕,平台健康运行中')} +
+ {/if} + +
+
{:__('今日发布量')} - + +
+

{$metrics.todayPost} {:__('篇')}

+
+
+ {if $metrics.postDelta > 0} + {$metrics.postDelta} + {:__('较昨日')} + {elseif $metrics.postDelta < 0} + {:abs($metrics['postDelta'])} + {:__('较昨日')} + {else} + {:__('持平')} + {:__('较昨日')} + {/if} +
+
+ {:__('累计')} {$metrics.totalPost} {:__('篇')} +
-

{$metrics.todayPost}

-

- {if $metrics.postDelta > 0} - {$metrics.postDelta} - {:__('较昨日')} - {elseif $metrics.postDelta < 0} - {:abs($metrics['postDelta'])} - {:__('较昨日')} - {else} - {:__('与昨日持平')} - {/if} -

+
- {:__('今日订单额')} + {:__('今日订单实收')}

¥{$metrics.todayOrderAmount|number_format=2}

-

- {if $metrics.orderDelta > 0} - {$metrics.orderDelta|number_format=2} - {:__('较昨日')} - {elseif $metrics.orderDelta < 0} - {:number_format(abs($metrics['orderDelta']), 2)} - {:__('较昨日')} - {else} - {:__('与昨日持平')} - {/if} -

+
+
+ {if $metrics.orderDelta > 0} + ¥{$metrics.orderDelta|number_format=2} + {:__('较昨日')} + {elseif $metrics.orderDelta < 0} + ¥{:number_format(abs($metrics['orderDelta']), 2)} + {:__('较昨日')} + {else} + {:__('持平')} + {:__('较昨日')} + {/if} +
+
+ {:__('累计')} ¥{$metrics.totalOrderAmount|number_format=2} +
+
+
- {:__('待审核信息数')} - + {:__('今日新增用户')} + +
+

{$metrics.todayUser} {:__('人')}

+
+
+ {if $metrics.userDelta > 0} + {$metrics.userDelta} + {:__('较昨日')} + {elseif $metrics.userDelta < 0} + {:abs($metrics['userDelta'])} + {:__('较昨日')} + {else} + {:__('持平')} + {:__('较昨日')} + {/if} +
+
+ {:__('累计注册')} {$metrics.totalUser} {:__('人')} +
-

{$metrics.pendingPost}

-

- {if $metrics.pendingPost > 0 && $auth->check('fc/post')} - {:__('去审核')} - {else} - {:__('暂无待审核')} - {/if} -

+
- {:__('待审核提现数')} - + {:__('今日信息曝光量')} + +
+

{$metrics.todayViews} {:__('次')}

+
+
+ {if $metrics.viewsDelta > 0} + {$metrics.viewsDelta} + {:__('较昨日')} + {elseif $metrics.viewsDelta < 0} + {:abs($metrics['viewsDelta'])} + {:__('较昨日')} + {else} + {:__('持平')} + {:__('较昨日')} + {/if} +
+
+ {:__('累计曝光')} {$metrics.totalViews} {:__('次')} +
-

{$metrics.withdrawAuditing}

-

- {if $metrics.withdrawAuditing > 0 && $auth->check('fc/withdraw')} - {:__('去处理')} - {else} - {:__('暂无待审核')} - {/if} -

-
+ +
+ +
+
+
+

{:__('业务多维趋势透视')}

+

{:__('发布量按发布时间归属,订单额按实际支付时间归属')}

+
+
+ +
+ + + +
+ +
+ + + + +
+
+
+ + +
+
+ {:__('时段累计总量')} + — +
+
+ {:__('日均水平')} + — +
+
+ {:__('单日最高峰值')} + — +
+
+ + + +
+ + +
+ +
+
+

{:__('营收来源构成')}

+ {:__('实收合计')} ¥{$metrics.totalOrderAmount|number_format=2} +
+
+
+ +
+
+ {if count($revenueTypes) > 0} + {foreach $revenueTypes as $rev} +
+
+ {$rev.label} + ({$rev.count}{:__('笔')}) +
+
+ ¥{$rev.amount|number_format=2} + {$rev.percent}% +
+
+ {/foreach} + {else} +
{:__('暂无已支付订单数据')}
+ {/if} +
+
+
+ + +
+
+

{:__('热门分类发帖分布')}

+ {:__('累计发帖')} {$metrics.totalPost} {:__('篇')} +
+
+ {if count($categoryRanks) > 0} + {foreach $categoryRanks as $cat} +
+
+ {$cat.name} + {$cat.count} {:__('篇')} ({$cat.percent}%) +
+
+
+
+
+ {/foreach} + {else} +
{:__('暂无分类数据')}
+ {/if} +
+
+
+
+ + +
+
-

{:__('近 14 天趋势')}

-

{:__('发布量按发布时间统计,订单额按支付时间统计')}

-
-
- - - +

{:__('同城活跃区域 Top 5')}

+

{:__('基于信息发布所处行政区/县的热度分布')}

+ {:__('全部帖子')}
- -

- {:__('近 14 天合计')} - — -

+
+ {if count($areaRanks) > 0} +
+ {foreach $areaRanks as $idx => $area} +
+ {$idx + 1} + {$area.region_name} +
+
+
+ {$area.count} {:__('篇')} +
+ {/foreach} +
+ {else} +
{:__('暂无地区发布数据')}
+ {/if} +
+
-

{:__('平台累计')}

+
+

{:__('平台商业与生态资产')}

+

{:__('会员沉淀、商户入驻及分销裂变体系')}

+
-
-
-
{:__('累计发布量')}
-
{$metrics.totalPost}
-
-
-
{:__('累计订单额')}
-
¥{$metrics.totalOrderAmount|number_format=2}
-
-
-
{:__('注册用户数')}
-
{$metrics.userCount}
-
-
-
{:__('会员数')}
-
{$metrics.memberCount}
-
-
-
{:__('商家数')}
-
{$metrics.merchantCount}
-
-
+
+
+ +
+
{:__('VIP会员体系')}
+
+
+ {$metrics.memberCount} + {:__('有效VIP会员')} +
+
+ {$ecosystem.memberRate}% + {:__('会员转化率')} +
+
+
-
-

{:__('分销佣金')}

-
-
-
-
{:__('待结算佣金')}
-
¥{$metrics.commissionPending|number_format=2}
+ +
+
{:__('商户入驻生态')}
+
+
+ {$metrics.merchantCount} + {:__('入驻商户数')} +
+
+ {$metrics.recommendMerchant} + {:__('推荐商家数')} +
+
+
+ + +
+
{:__('二级分销裂变')}
+
+
+ {$ecosystem.distributorCount} + {:__('推广分销员')} +
+
+ ¥{$ecosystem.commissionSettled|number_format=2} + {:__('已结佣金')} +
+
+ ¥{$ecosystem.commissionPending|number_format=2} + {:__('待结佣金')} +
+
+
-
-
{:__('已结算佣金')}
-
¥{$metrics.commissionSettled|number_format=2}
-
-
+
-
+ +
- {:__('近 14 天明细')} +
+ + {:__('每日数据明细 (近 14 天)')} +
- {:__('展开查看每日数据')} + {:__('展开明细')} {:__('收起明细')}
- - +
{:__('近 14 天每日发布量、订单额与新增注册用户')}
- - - + + + + - + {foreach $trendRows as $item} - + - - + + + {/foreach} diff --git a/application/admin/view/index/login.html b/application/admin/view/index/login.html index 66d32de..8666ef9 100755 --- a/application/admin/view/index/login.html +++ b/application/admin/view/index/login.html @@ -2,403 +2,1226 @@ {include file="common/meta" /} - 山间小仓 · 同城分类信息平台 — 后台登录 + 山间小仓 · 同城农品供求信息发布与管理后台 - - {if $background} - - {/if} - -'); + return; + } + + $.each(rows, function (idx, item) { + var orderStr = '¥' + Number(item.orders || 0).toLocaleString('zh-CN', { + minimumFractionDigits: 2, + maximumFractionDigits: 2 + }); + var tr = '' + + '' + + '' + + '' + + '' + + '' + + ''; + $tbody.append(tr); + }); + }; + + /** + * 初始化营收构成环形饼图 + */ + var initRevenueChart = function () { + if (!$revenueChartEl.length || !Echarts) { + return; + } + + revenueChartInstance = Echarts.init($revenueChartEl[0]); + + if (!revenueTypes || revenueTypes.length === 0) { + return; + } + + var chartData = $.map(revenueTypes, function (item) { + return { + name: item.label, + value: item.amount + }; }); - $(document).on('click', '.fc-seg__btn', function () { - applyMetric($(this).data('metric')); - }); + var palette = ['#00D26A', '#F5A623', '#3B82F6', '#8B5CF6', '#EC4899', '#14B8A6']; + + var option = { + color: palette, + tooltip: { + trigger: 'item', + backgroundColor: '#FFFFFF', + borderColor: '#E2E8F0', + borderWidth: 1, + padding: [8, 12], + textStyle: {color: '#1E293B', fontSize: 12}, + extraCssText: 'border-radius:8px;box-shadow:0 6px 18px rgba(15,23,42,0.08);', + formatter: function (p) { + return p.name + '
¥' + Number(p.value).toFixed(2) + ' (' + p.percent + '%)'; + } + }, + legend: { + show: false + }, + series: [{ + type: 'pie', + radius: ['52%', '76%'], + center: ['50%', '50%'], + avoidLabelOverlap: false, + itemStyle: { + borderRadius: 6, + borderColor: '#FFFFFF', + borderWidth: 2 + }, + label: { + show: false + }, + emphasis: { + scale: true, + scaleSize: 6, + label: { + show: true, + fontSize: 13, + fontWeight: 'bold', + formatter: '{b}\n{d}%' + } + }, + labelLine: {show: false}, + data: chartData + }] + }; + + revenueChartInstance.setOption(option); + }; + + // 初始化趋势主图 + if ($trendChartEl.length && Echarts) { + trendChartInstance = Echarts.init($trendChartEl[0]); + renderTrend(); } + + // 初始化营收环形图 + initRevenueChart(); + + // 监听窗口尺寸变化自适应 + $(window).on('resize', function () { + if (trendChartInstance) { + trendChartInstance.resize(); + } + if (revenueChartInstance) { + revenueChartInstance.resize(); + } + }); + + // 时段切换点击(近 7 / 14 / 30 天) + $(document).on('click', '.fc-range-btn', function () { + var $btn = $(this); + var days = parseInt($btn.data('days'), 10) || 14; + if (days === currentDays) { + return; + } + currentDays = days; + $('.fc-range-btn').removeClass('is-active'); + $btn.addClass('is-active'); + renderTrend(); + }); + + // 指标切换点击(发布量 / 订单额 / 新增用户 / 信息曝光) + $(document).on('click', '.fc-metric-btn', function () { + var $btn = $(this); + var metric = $btn.data('metric'); + if (metric === currentMetric) { + return; + } + currentMetric = metric; + $('.fc-metric-btn').removeClass('is-active'); + $btn.addClass('is-active'); + renderTrend(); + }); } };
{:__('日期')}{:__('发布量')}{:__('订单额')}{:__('新增注册用户')}{:__('发布量 (篇)')}{:__('订单实收 (元)')}{:__('新增注册用户 (人)')}{:__('信息曝光量 (次)')}
{$item.date}{$item.date} {$item.posts}¥{$item.orders|number_format=2}{$item.members}¥{$item.orders|number_format=2}{$item.users}{$item.views}
' + __('暂无明细数据') + '
' + item.date + '' + (item.posts || 0) + '' + orderStr + '' + (item.users || 0) + '' + (item.views || 0) + '