feat(dashboard): 从专业财富观角度重构升级后台收益与分成统计仪表盘
这个提交包含在:
@@ -47,12 +47,12 @@ class Config extends Common
|
||||
'status' => $this->auth->check('shopro/config/rechargewithdraw')
|
||||
],
|
||||
[
|
||||
'label' => '分销配置',
|
||||
'label' => '分销基础',
|
||||
'name' => 'shopro/config/commission',
|
||||
'status' => $this->auth->check('shopro/config/commission')
|
||||
],
|
||||
[
|
||||
'label' => '合伙股东',
|
||||
'label' => '合伙股东规则',
|
||||
'name' => 'shopro/config/partner',
|
||||
'status' => $this->auth->check('shopro/config/commission')
|
||||
],
|
||||
|
||||
@@ -7,7 +7,15 @@ use app\admin\model\shopro\order\OrderItem;
|
||||
use app\admin\model\shopro\goods\Goods;
|
||||
use app\admin\model\shopro\user\User;
|
||||
use app\admin\model\shopro\Share;
|
||||
use app\admin\model\shopro\commission\Agent;
|
||||
use app\admin\model\shopro\partner\Reward;
|
||||
use app\admin\model\shopro\partner\DailyReport;
|
||||
use app\admin\model\shopro\partner\RateLog;
|
||||
use app\admin\model\shopro\partner\VoucherPlan;
|
||||
use addons\shopro\service\SearchHistory;
|
||||
use addons\shopro\service\partner\RateService;
|
||||
use addons\shopro\service\partner\IdentityService;
|
||||
use addons\shopro\service\partner\PartnerConfig;
|
||||
|
||||
class Dashboard extends Common
|
||||
{
|
||||
@@ -38,7 +46,7 @@ class Dashboard extends Common
|
||||
|
||||
public function total()
|
||||
{
|
||||
// 用户数据
|
||||
// 1. 基础用户数据 (保持向下兼容)
|
||||
$userData = [
|
||||
'total' => User::count(),
|
||||
'today' => User::whereTime('createtime', 'today')->count(),
|
||||
@@ -49,19 +57,18 @@ class Dashboard extends Common
|
||||
})
|
||||
];
|
||||
|
||||
// -- commission code start --
|
||||
// 2. 分销商/合伙人基础数据 (保持向下兼容)
|
||||
$agentData = [
|
||||
'total' => \app\admin\model\shopro\commission\Agent::count(),
|
||||
'today' => \app\admin\model\shopro\commission\Agent::whereTime('createtime', 'today')->count(),
|
||||
'week' => \app\admin\model\shopro\commission\Agent::whereTime('createtime', 'week')->count(),
|
||||
'array' => collection(\app\admin\model\shopro\commission\Agent::field('user_id,createtime')->whereTime('createtime', 'today')->select())->each(function ($agent) {
|
||||
'total' => Agent::count(),
|
||||
'today' => Agent::whereTime('createtime', 'today')->count(),
|
||||
'week' => Agent::whereTime('createtime', 'week')->count(),
|
||||
'array' => collection(Agent::field('user_id,createtime')->whereTime('createtime', 'today')->select())->each(function ($agent) {
|
||||
$agent->counter = 1;
|
||||
$agent->createtime_unix = $agent->getData('createtime') * 1000;
|
||||
})
|
||||
];
|
||||
// -- commission code end --
|
||||
|
||||
// 分享数据
|
||||
// 3. 分享数据 (保持向下兼容)
|
||||
$shareData = [
|
||||
'total' => Share::count(),
|
||||
'today' => Share::whereTime('createtime', 'today')->count(),
|
||||
@@ -72,10 +79,102 @@ class Dashboard extends Common
|
||||
})
|
||||
];
|
||||
|
||||
// 4. 全局财富大盘与资金流 (专业财富指标)
|
||||
$paidStatuses = [Order::STATUS_PAID, Order::STATUS_COMPLETED];
|
||||
$totalGmv = (float) Order::whereIn('status', $paidStatuses)->sum('pay_fee');
|
||||
$todayGmv = (float) Order::whereIn('status', $paidStatuses)->whereTime('createtime', 'today')->sum('pay_fee');
|
||||
$paidOrderCount = (int) Order::whereIn('status', $paidStatuses)->count();
|
||||
$todayPaidOrderCount = (int) Order::whereIn('status', $paidStatuses)->whereTime('createtime', 'today')->count();
|
||||
$avgOrderAmount = $paidOrderCount > 0 ? round($totalGmv / $paidOrderCount, 2) : 0.00;
|
||||
|
||||
// 现金分成总支出 (status = 1 真实入账)
|
||||
$totalPayout = (float) Reward::where('status', 1)->sum('amount');
|
||||
$todayPayout = (float) Reward::where('status', 1)->whereTime('createtime', 'today')->sum('amount');
|
||||
$netRetention = round($totalGmv - $totalPayout, 2);
|
||||
$payoutRate = $totalGmv > 0 ? round(($totalPayout / $totalGmv) * 100, 2) : 0.00;
|
||||
|
||||
$wealthData = [
|
||||
'total_gmv' => number_format($totalGmv, 2, '.', ''),
|
||||
'today_gmv' => number_format($todayGmv, 2, '.', ''),
|
||||
'total_payout' => number_format($totalPayout, 2, '.', ''),
|
||||
'today_payout' => number_format($todayPayout, 2, '.', ''),
|
||||
'net_retention' => number_format($netRetention, 2, '.', ''),
|
||||
'payout_rate' => number_format($payoutRate, 2, '.', ''),
|
||||
'paid_order_count' => $paidOrderCount,
|
||||
'today_paid_order_count' => $todayPaidOrderCount,
|
||||
'avg_order_amount' => number_format($avgOrderAmount, 2, '.', ''),
|
||||
];
|
||||
|
||||
// 5. 马豆生态资产蓄水池
|
||||
$totalMadow = (float) Agent::sum('madow');
|
||||
$rateService = new RateService();
|
||||
$currentRate = $rateService->currentRate();
|
||||
$totalMadowCny = round($totalMadow * $currentRate, 2);
|
||||
$historyOrderN = $rateService->countN();
|
||||
|
||||
$latestReport = DailyReport::order('settle_date', 'desc')->find();
|
||||
$yesterdayMadowPool = $latestReport ? (float) $latestReport->madow_pool : 0.00;
|
||||
|
||||
$madowData = [
|
||||
'total_madow' => IdentityService::formatMadow($totalMadow),
|
||||
'current_rate' => number_format($currentRate, 10, '.', ''),
|
||||
'rate_per_wan' => number_format($currentRate * 10000, 4, '.', ''), // 每万豆价值折算
|
||||
'total_madow_cny' => number_format($totalMadowCny, 2, '.', ''),
|
||||
'history_order_n' => $historyOrderN,
|
||||
'yesterday_madow_pool' => IdentityService::formatMadow($yesterdayMadowPool),
|
||||
];
|
||||
|
||||
// 6. 消费兑换券杠杆池
|
||||
$totalVoucher = (float) VoucherPlan::sum('total_amount');
|
||||
$releasedVoucher = (float) VoucherPlan::sum('released_amount');
|
||||
$usedVoucher = (float) Agent::sum('voucher_used');
|
||||
$pendingVoucher = max(0, round($totalVoucher - $releasedVoucher, 2));
|
||||
$availableVoucher = max(0, round($releasedVoucher - $usedVoucher, 2));
|
||||
|
||||
$voucherData = [
|
||||
'total_voucher' => number_format($totalVoucher, 2, '.', ''),
|
||||
'released_voucher' => number_format($releasedVoucher, 2, '.', ''),
|
||||
'used_voucher' => number_format($usedVoucher, 2, '.', ''),
|
||||
'pending_voucher' => number_format($pendingVoucher, 2, '.', ''),
|
||||
'available_voucher' => number_format($availableVoucher, 2, '.', ''),
|
||||
];
|
||||
|
||||
// 7. 合伙股东网络与资本活力
|
||||
$totalAgents = (int) Agent::count();
|
||||
$tasterCount = (int) Agent::where('level', 1)->count();
|
||||
$makerCount = (int) Agent::where('level', '>=', 2)->count();
|
||||
$makerLevels = [];
|
||||
for ($lv = 2; $lv <= 7; $lv++) {
|
||||
$makerLevels['V' . ($lv - 1)] = (int) Agent::where('level', $lv)->count();
|
||||
}
|
||||
$venueCount = (int) Agent::where('is_venue', 1)->count();
|
||||
$countyCount = (int) Agent::where('is_county', 1)->count();
|
||||
|
||||
$activeWindow = time() - (int) PartnerConfig::get('partner_active_days', 30) * 86400;
|
||||
$activeCount = (int) Agent::where('last_self_order_time', '>=', $activeWindow)->count();
|
||||
$inactiveCount = max(0, $totalAgents - $activeCount);
|
||||
$activeRate = $totalAgents > 0 ? round(($activeCount / $totalAgents) * 100, 1) : 0.0;
|
||||
|
||||
$partnerNetwork = [
|
||||
'total_agents' => $totalAgents,
|
||||
'taster_count' => $tasterCount,
|
||||
'maker_count' => $makerCount,
|
||||
'maker_levels' => $makerLevels,
|
||||
'venue_count' => $venueCount,
|
||||
'county_count' => $countyCount,
|
||||
'active_count' => $activeCount,
|
||||
'inactive_count' => $inactiveCount,
|
||||
'active_rate' => $activeRate,
|
||||
];
|
||||
|
||||
$this->success('获取成功', null, [
|
||||
'user_data' => $userData,
|
||||
'agent_data' => $agentData ?? null,
|
||||
'share_data' => $shareData
|
||||
'share_data' => $shareData,
|
||||
'wealth_data' => $wealthData,
|
||||
'madow_data' => $madowData,
|
||||
'voucher_data' => $voucherData,
|
||||
'partner_network' => $partnerNetwork,
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -85,6 +184,9 @@ class Dashboard extends Common
|
||||
{
|
||||
$date = $this->request->param('date', '');
|
||||
$date = array_values(array_filter(explode(' - ', $date)));
|
||||
if (empty($date) || count($date) < 2) {
|
||||
$date = [date('Y-m-d 00:00:00'), date('Y-m-d 23:59:59')];
|
||||
}
|
||||
|
||||
$orders = Order::with(['items'])->whereTime('createtime', 'between', $date)
|
||||
->order('id', 'asc')->select();
|
||||
@@ -96,22 +198,22 @@ class Dashboard extends Common
|
||||
// 支付订单(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
|
||||
$data['payOrderNum'] = 0;
|
||||
$data['payOrderArr'] = [];
|
||||
//支付金额(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
|
||||
// 支付金额(包含退款的订单, 不包含货到付款还未收货(未支付)订单)
|
||||
$data['payAmountNum'] = 0;
|
||||
$data['payAmountArr'] = [];
|
||||
|
||||
// 支付用户(一个人不管下多少单,都算一个,包含退款的订单, 不包含货到付款还未收货(未支付)订单)
|
||||
// 支付用户
|
||||
$userIds = [];
|
||||
$data['payUserNum'] = 0;
|
||||
$data['payUserArr'] = [];
|
||||
|
||||
// 代发货(包含货到付款)
|
||||
// 待发货(包含货到付款)
|
||||
$data['noSendNum'] = 0;
|
||||
$data['noSendArr'] = [];
|
||||
//售后维权
|
||||
// 售后维权
|
||||
$data['aftersaleNum'] = 0;
|
||||
$data['aftersaleArr'] = [];
|
||||
//退款订单
|
||||
// 退款订单
|
||||
$data['refundNum'] = 0;
|
||||
$data['refundArr'] = [];
|
||||
|
||||
@@ -122,9 +224,8 @@ class Dashboard extends Common
|
||||
'user_id' => $order->user_id
|
||||
];
|
||||
|
||||
// 已支付的,不包含,货到付款未支付的
|
||||
// 已支付的,不包含货到付款未支付的
|
||||
if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED])) {
|
||||
// 支付订单数
|
||||
$data['payOrderNum']++;
|
||||
|
||||
$data['payOrderArr'][] = [
|
||||
@@ -137,7 +238,7 @@ class Dashboard extends Common
|
||||
$data['payAmountNum'] = bcadd((string)$data['payAmountNum'], $order->pay_fee, 2);
|
||||
|
||||
$data['payAmountArr'][] = [
|
||||
'counter' => $order->pay_fee,
|
||||
'counter' => (float) $order->pay_fee,
|
||||
'createtime' => $order->getData('createtime') * 1000,
|
||||
];
|
||||
|
||||
@@ -152,7 +253,7 @@ class Dashboard extends Common
|
||||
}
|
||||
}
|
||||
|
||||
// 已支付的,和 货到付款未支付的
|
||||
// 已支付的和货到付款未支付的
|
||||
if (in_array($order->status, [Order::STATUS_PAID, Order::STATUS_COMPLETED]) || $order->isOffline($order)) {
|
||||
$flagnoSend = false;
|
||||
$flagaftersale = false;
|
||||
@@ -181,34 +282,141 @@ class Dashboard extends Common
|
||||
|
||||
if (!$flagaftersale && $item->aftersale_status != OrderItem::AFTERSALE_STATUS_NOAFTER) {
|
||||
$data['aftersaleNum']++;
|
||||
// $data['aftersaleArr'][] = [
|
||||
// 'counter' => 1,
|
||||
// 'createtime' => $order->getData('createtime') * 1000,
|
||||
// ];
|
||||
$flagaftersale = true;
|
||||
}
|
||||
|
||||
if (!$flagrefund && $item->refund_status > OrderItem::REFUND_STATUS_NOREFUND) {
|
||||
$data['refundNum']++;
|
||||
// $data['refundArr'][] = [
|
||||
// 'counter' => 1,
|
||||
// 'createtime' => $order->getData('createtime') * 1000,
|
||||
// ];
|
||||
$flagrefund = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$aftersaleIng && $flagnoSend) {
|
||||
// 存在正在售后中的订单,不算待发货(和订单列表保持一直)
|
||||
$data['noSendNum']++;
|
||||
// $data['noSendArr'][] = [
|
||||
// 'counter' => 1,
|
||||
// 'createtime' => $order->getData('createtime') * 1000,
|
||||
// ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================= 财富与收益分成时段穿透 (新增) =================
|
||||
$rewards = Reward::where('status', 1)
|
||||
->whereTime('createtime', 'between', $date)
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
|
||||
$payoutTotal = 0.00;
|
||||
$payoutArr = [];
|
||||
$periodMadow = 0.00000000;
|
||||
|
||||
// 四大分成板块结构
|
||||
$structureMap = [
|
||||
'push3' => [
|
||||
'key' => 'push3',
|
||||
'name' => '直推裂变(推三返一)',
|
||||
'types' => ['push3'],
|
||||
'amount' => 0.00,
|
||||
'count' => 0,
|
||||
'color' => '#806AF6',
|
||||
],
|
||||
'maker' => [
|
||||
'key' => 'maker',
|
||||
'name' => '创客团队(极差/平级/超越)',
|
||||
'types' => ['maker_diff', 'maker_peer', 'maker_overtaken'],
|
||||
'amount' => 0.00,
|
||||
'count' => 0,
|
||||
'color' => '#13c2c2',
|
||||
],
|
||||
'channel' => [
|
||||
'key' => 'channel',
|
||||
'name' => '实体渠道(馆县拆分/平级)',
|
||||
'types' => ['team_venue', 'team_county', 'venue_peer', 'county_peer'],
|
||||
'amount' => 0.00,
|
||||
'count' => 0,
|
||||
'color' => '#fa8c16',
|
||||
],
|
||||
'share' => [
|
||||
'key' => 'share',
|
||||
'name' => '全网共享(平台日结均分)',
|
||||
'types' => ['venue_share', 'county_share'],
|
||||
'amount' => 0.00,
|
||||
'count' => 0,
|
||||
'color' => '#eb2f96',
|
||||
],
|
||||
];
|
||||
|
||||
// 详细奖项映射
|
||||
$detailList = [];
|
||||
foreach (PartnerConfig::rewardTypeMap() as $typeKey => $typeName) {
|
||||
if (in_array($typeKey, ['madow_share', 'voucher_grant', 'voucher_release'])) {
|
||||
continue;
|
||||
}
|
||||
$detailList[$typeKey] = [
|
||||
'type' => $typeKey,
|
||||
'name' => $typeName,
|
||||
'amount' => 0.00,
|
||||
'count' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
foreach ($rewards as $reward) {
|
||||
$amt = (float) $reward->amount;
|
||||
$rwType = $reward->reward_type;
|
||||
|
||||
if ($rwType === 'madow_share') {
|
||||
$periodMadow = function_exists('bcadd')
|
||||
? (float) bcadd((string)$periodMadow, (string)($reward->madow ?? 0), 8)
|
||||
: ($periodMadow + (float)($reward->madow ?? 0));
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($amt > 0) {
|
||||
$payoutTotal = round($payoutTotal + $amt, 2);
|
||||
$payoutArr[] = [
|
||||
'counter' => $amt,
|
||||
'createtime' => $reward->getData('createtime') * 1000,
|
||||
'type' => $rwType,
|
||||
];
|
||||
|
||||
// 归类到板块
|
||||
foreach ($structureMap as $catKey => &$cat) {
|
||||
if (in_array($rwType, $cat['types'])) {
|
||||
$cat['amount'] = round($cat['amount'] + $amt, 2);
|
||||
$cat['count']++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unset($cat);
|
||||
|
||||
if (isset($detailList[$rwType])) {
|
||||
$detailList[$rwType]['amount'] = round($detailList[$rwType]['amount'] + $amt, 2);
|
||||
$detailList[$rwType]['count']++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化分成板块占比
|
||||
$splitStructure = [];
|
||||
foreach ($structureMap as $catKey => $cat) {
|
||||
$catAmount = (float) $cat['amount'];
|
||||
$ratio = $payoutTotal > 0 ? round(($catAmount / $payoutTotal) * 100, 1) : 0.0;
|
||||
$splitStructure[] = [
|
||||
'key' => $catKey,
|
||||
'name' => $cat['name'],
|
||||
'amount' => number_format($catAmount, 2, '.', ''),
|
||||
'count' => $cat['count'],
|
||||
'ratio' => $ratio,
|
||||
'color' => $cat['color'],
|
||||
];
|
||||
}
|
||||
|
||||
$payAmountNum = (float) $data['payAmountNum'];
|
||||
$data['payoutNum'] = number_format($payoutTotal, 2, '.', '');
|
||||
$data['payoutArr'] = $payoutArr;
|
||||
$data['retentionNum'] = number_format(max(0, round($payAmountNum - $payoutTotal, 2)), 2, '.', '');
|
||||
$data['payoutRate'] = $payAmountNum > 0 ? round(($payoutTotal / $payAmountNum) * 100, 2) : 0.00;
|
||||
$data['splitStructure'] = $splitStructure;
|
||||
$data['rewardDetails'] = array_values($detailList);
|
||||
$data['periodMadow'] = IdentityService::formatMadow($periodMadow);
|
||||
|
||||
$this->success('获取成功', null, $data);
|
||||
}
|
||||
|
||||
@@ -216,6 +424,7 @@ class Dashboard extends Common
|
||||
|
||||
public function ranking()
|
||||
{
|
||||
// 1. 商品销量排行 (原有)
|
||||
$goods = Goods::limit(5)->order('sales', 'desc')->select();
|
||||
foreach ($goods as $key => $gd) {
|
||||
$gd->append(['real_sales']);
|
||||
@@ -231,11 +440,65 @@ class Dashboard extends Common
|
||||
$gd['sale_total_money'] = $result['sale_total_money'] ?: 0;
|
||||
}
|
||||
|
||||
// 2. 热搜排行 (原有)
|
||||
$searchHistory = new SearchHistory();
|
||||
$hotSearch = $searchHistory->hotSearch();
|
||||
|
||||
// 3. 合伙股东收益排行 TOP 5 (新增)
|
||||
$partnerRanking = [];
|
||||
$rankingRows = Reward::field('user_id, sum(amount) as total_income, count(*) as reward_count')
|
||||
->where('status', 1)
|
||||
->where('user_id', '>', 0)
|
||||
->group('user_id')
|
||||
->order('total_income', 'desc')
|
||||
->limit(5)
|
||||
->select();
|
||||
|
||||
foreach ($rankingRows as $row) {
|
||||
$user = User::field('id, nickname, avatar, mobile')->where('id', $row->user_id)->find();
|
||||
$agent = Agent::where('user_id', $row->user_id)->find();
|
||||
$identity = IdentityService::build($agent);
|
||||
|
||||
$partnerRanking[] = [
|
||||
'user_id' => $row->user_id,
|
||||
'nickname' => $user ? ($user->nickname ?: '用户#' . $row->user_id) : '用户#' . $row->user_id,
|
||||
'avatar' => $user ? $user->avatar : '',
|
||||
'mobile' => $user ? $user->mobile : '',
|
||||
'total_income' => number_format((float)$row->total_income, 2, '.', ''),
|
||||
'reward_count' => (int) $row->reward_count,
|
||||
'identity_text' => $identity['identity_text'] ?: '普通会员',
|
||||
'is_partner' => $identity['is_partner'],
|
||||
'direct_order_seq' => $agent ? (int) $agent->direct_order_seq : 0,
|
||||
];
|
||||
}
|
||||
|
||||
// 4. 最新日结概览 (新增)
|
||||
$dailyReport = DailyReport::order('settle_date', 'desc')->find();
|
||||
$recentReport = null;
|
||||
if ($dailyReport) {
|
||||
$recentReport = [
|
||||
'settle_date' => $dailyReport->settle_date,
|
||||
'gmv' => number_format((float)$dailyReport->gmv, 2, '.', ''),
|
||||
'order_count' => (int)$dailyReport->order_count,
|
||||
'order_count_n' => (int)$dailyReport->order_count_n,
|
||||
'rate' => number_format((float)$dailyReport->rate, 10, '.', ''),
|
||||
'madow_pool' => IdentityService::formatMadow($dailyReport->madow_pool),
|
||||
'share_user_count' => (int)$dailyReport->share_user_count,
|
||||
'venue_pool' => number_format((float)$dailyReport->venue_pool, 2, '.', ''),
|
||||
'venue_user_count' => (int)$dailyReport->venue_user_count,
|
||||
'county_pool' => number_format((float)$dailyReport->county_pool, 2, '.', ''),
|
||||
'county_user_count' => (int)$dailyReport->county_user_count,
|
||||
'voucher_release_total' => number_format((float)$dailyReport->voucher_release_total, 2, '.', ''),
|
||||
'status' => (int)$dailyReport->status,
|
||||
'status_text' => $dailyReport->status_text,
|
||||
];
|
||||
}
|
||||
|
||||
$this->success('获取成功', null, [
|
||||
'goods' => $goods,
|
||||
'hot_search' => $searchHistory->hotSearch()
|
||||
'hot_search' => $hotSearch,
|
||||
'partner_ranking' => $partnerRanking,
|
||||
'daily_report_recent' => $recentReport,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,11 +51,30 @@ class Daily extends Common
|
||||
->whereBetween('createtime', [$yStart, $yEnd])
|
||||
->whereIn('reward_type', \addons\shopro\service\partner\PartnerConfig::instantTypes())
|
||||
->sum('amount');
|
||||
|
||||
$yesterdayReport = DailyReport::where('settle_date', $yesterday)->find();
|
||||
$yesterdayGmv = $yesterdayReport ? (float) $yesterdayReport->gmv : 0;
|
||||
$yesterdayTotalCash = $sumCash($yesterday) + $instantYesterday;
|
||||
$yesterdayPayoutRatio = $yesterdayGmv > 0 ? round(($yesterdayTotalCash / $yesterdayGmv) * 100, 2) : 0;
|
||||
|
||||
$voucherTotal = (float) \app\admin\model\shopro\partner\VoucherPlan::where('status', 1)->sum('total_amount');
|
||||
$voucherReleased = (float) \app\admin\model\shopro\partner\VoucherPlan::where('status', 1)->sum('released_amount');
|
||||
$voucherDebt = max(0, $voucherTotal - $voucherReleased);
|
||||
|
||||
$activeService = new \addons\shopro\service\partner\ActiveService();
|
||||
$venueActiveCount = count($activeService->activeUserIds($since, time(), ['is_venue' => 1]));
|
||||
$countyActiveCount = count($activeService->activeUserIds($since, time(), ['is_county' => 1]));
|
||||
|
||||
$result['kpi'] = [
|
||||
'today' => DailyReport::where('settle_date', $today)->find(),
|
||||
'yesterday' => DailyReport::where('settle_date', $yesterday)->find(),
|
||||
'yesterday' => $yesterdayReport,
|
||||
'today_cash' => number_format($todayCash + $instantToday, 2, '.', ''),
|
||||
'yesterday_cash' => number_format($sumCash($yesterday) + $instantYesterday, 2, '.', ''),
|
||||
'yesterday_cash' => number_format($yesterdayTotalCash, 2, '.', ''),
|
||||
'yesterday_payout_ratio' => $yesterdayPayoutRatio . '%',
|
||||
'yesterday_payout_ratio_val' => $yesterdayPayoutRatio,
|
||||
'voucher_debt' => number_format($voucherDebt, 2, '.', ''),
|
||||
'venue_active_count' => $venueActiveCount,
|
||||
'county_active_count' => $countyActiveCount,
|
||||
'inactive_count' => \app\admin\model\shopro\commission\Agent::where(function ($q) use ($since) {
|
||||
$q->whereNull('last_self_order_time')->whereOr('last_self_order_time', '<', $since);
|
||||
})->count(),
|
||||
@@ -63,6 +82,13 @@ class Daily extends Common
|
||||
$this->success('获取成功', null, $result);
|
||||
}
|
||||
|
||||
public function preview()
|
||||
{
|
||||
$date = $this->request->param('settle_date', date('Y-m-d', strtotime('-1 day')));
|
||||
$data = (new DailySettleService())->preview($date);
|
||||
$this->success('试算成功', null, $data);
|
||||
}
|
||||
|
||||
public function rerun()
|
||||
{
|
||||
$date = $this->request->param('settle_date');
|
||||
|
||||
@@ -92,7 +92,20 @@ class Reward extends Common
|
||||
$totalAmount += array_sum(array_column($newDatas, 'amount'));
|
||||
$totalMadow += array_sum(array_column($newDatas, 'madow'));
|
||||
if ($pages['is_last_page']) {
|
||||
$newDatas[] = ['id' => "总数:{$total};金额:{$totalAmount};马豆:{$totalMadow}"];
|
||||
$newDatas[] = [
|
||||
'id' => '财务合计',
|
||||
'createtime' => '-',
|
||||
'user_nickname' => "共 {$total} 笔流水",
|
||||
'buyer_nickname' => '-',
|
||||
'order_id' => '-',
|
||||
'reward_type_text' => '-',
|
||||
'role_text' => '-',
|
||||
'amount' => number_format($totalAmount, 2, '.', ''),
|
||||
'madow' => number_format($totalMadow, 8, '.', ''),
|
||||
'base_amount' => '-',
|
||||
'status_text' => '已汇总',
|
||||
'formula' => "总现金: {$totalAmount}, 总马豆: {$totalMadow}",
|
||||
];
|
||||
}
|
||||
return $newDatas;
|
||||
});
|
||||
@@ -109,6 +122,9 @@ class Reward extends Common
|
||||
if ($amount == 0) {
|
||||
$this->error('请输入金额');
|
||||
}
|
||||
if ($amount > 10000) {
|
||||
$this->error('单笔补发金额不能超过 10,000 元(财务风控限制)');
|
||||
}
|
||||
Db::transaction(function () use ($params, $amount) {
|
||||
$row = PartnerRewardService::write([
|
||||
'order_id' => intval($params['order_id'] ?? 0) ?: -time(),
|
||||
@@ -167,4 +183,79 @@ class Reward extends Common
|
||||
(new PartnerRewardService())->refund($orderId);
|
||||
$this->success('整单已退回');
|
||||
}
|
||||
|
||||
/**
|
||||
* 订单整单利益分配透视(财务与客服一键对账)
|
||||
*/
|
||||
public function orderTree()
|
||||
{
|
||||
$orderId = intval($this->request->param('order_id'));
|
||||
if ($orderId <= 0) {
|
||||
$this->error('订单ID不合法');
|
||||
}
|
||||
|
||||
$order = \app\admin\model\shopro\order\Order::with(['user'])->where('id', $orderId)->find();
|
||||
if (!$order) {
|
||||
$this->error('订单不存在');
|
||||
}
|
||||
|
||||
$rewards = $this->model->with(['user', 'buyer'])
|
||||
->where('order_id', $orderId)
|
||||
->order('id', 'asc')
|
||||
->select();
|
||||
|
||||
$rows = [];
|
||||
$totalCash = 0;
|
||||
$totalMadow = 0;
|
||||
foreach ($rewards as $r) {
|
||||
$rows[] = [
|
||||
'id' => $r->id,
|
||||
'reward_type' => $r->reward_type,
|
||||
'reward_type_text' => $r->reward_type_text,
|
||||
'user_id' => $r->user_id,
|
||||
'user_nickname' => $r->user ? ($r->user->nickname ?: $r->user->mobile) : '平台/买家',
|
||||
'role_text' => $r->role_text ?: $r->role,
|
||||
'amount' => $r->amount,
|
||||
'madow' => $r->madow,
|
||||
'base_amount' => $r->base_amount,
|
||||
'status' => $r->status,
|
||||
'status_text' => $r->status_text,
|
||||
'formula' => $r->formula,
|
||||
'createtime' => $r->createtime,
|
||||
];
|
||||
if ($r->status == 1) {
|
||||
if (in_array($r->reward_type, PartnerConfig::instantTypes())) {
|
||||
$totalCash = bcadd((string)$totalCash, (string)$r->amount, 2);
|
||||
}
|
||||
$totalMadow = bcadd((string)$totalMadow, (string)$r->madow, 8);
|
||||
}
|
||||
}
|
||||
|
||||
$voucherPlan = \app\admin\model\shopro\partner\VoucherPlan::where('order_id', $orderId)->find();
|
||||
$payFee = floatval($order->pay_fee);
|
||||
$payoutRatio = $payFee > 0 ? round(($totalCash / $payFee) * 100, 2) : 0;
|
||||
|
||||
$this->success('获取成功', null, [
|
||||
'order' => [
|
||||
'id' => $order->id,
|
||||
'order_sn' => $order->order_sn,
|
||||
'pay_fee' => $order->pay_fee,
|
||||
'status_text' => $order->status_text,
|
||||
'paid_time' => $order->paid_time ? date('Y-m-d H:i:s', $order->paid_time) : '-',
|
||||
'buyer_nickname' => $order->user ? ($order->user->nickname ?: $order->user->mobile) : '',
|
||||
],
|
||||
'rewards' => $rows,
|
||||
'summary' => [
|
||||
'total_cash' => number_format($totalCash, 2, '.', ''),
|
||||
'total_madow' => number_format($totalMadow, 8, '.', ''),
|
||||
'payout_ratio' => $payoutRatio . '%',
|
||||
],
|
||||
'voucher_plan' => $voucherPlan ? [
|
||||
'total_amount' => $voucherPlan->total_amount,
|
||||
'released_amount' => $voucherPlan->released_amount,
|
||||
'released_days' => $voucherPlan->released_days,
|
||||
'status_text' => $voucherPlan->status_text,
|
||||
] : null,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<el-header class="sa-header">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">分销 / 合伙股东计奖商品</div>
|
||||
<div class="left-name">计奖商品</div>
|
||||
<sa-filter-condition v-model="state.filter" @filter-delete="onChangeFilter">
|
||||
</sa-filter-condition>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<el-header class="sa-header">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">分销商等级</div>
|
||||
<div class="left-name">身份等级</div>
|
||||
<div style="font-size:12px;color:#999;margin-top:4px">
|
||||
等级 1 固定为品酒师;2–7 对应创客 V1–V6。百分比佣金在合伙股东模式下不使用。
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<el-header class="sa-header">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">分销动态</div>
|
||||
<div class="left-name">动态日志</div>
|
||||
<div class="really-status sa-flex ml-4">
|
||||
实时动态
|
||||
<el-switch class="ml-2" v-model="really.reallyStatus" :active-value="1" :inactive-value="0"
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
<el-header class="sa-header">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">分销订单</div>
|
||||
<div class="left-name">业绩订单</div>
|
||||
<sa-filter-condition v-model="state.filter" @filter-delete="onChangeFilter">
|
||||
</sa-filter-condition>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<el-header class="sa-header">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">佣金明细</div>
|
||||
<div class="left-name">历史佣金</div>
|
||||
<sa-filter-condition v-model="state.filter" @filter-delete="onChangeFilter">
|
||||
</sa-filter-condition>
|
||||
</div>
|
||||
|
||||
@@ -653,7 +653,7 @@
|
||||
<el-radio :label="2">二级</el-radio>
|
||||
<el-radio :label="3">三级</el-radio>
|
||||
</el-radio-group>
|
||||
<div class="tip">默认佣金比例请到 分销商等级 进行设置</div>
|
||||
<div class="tip">默认级差基数请到「身份等级」进行设置</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="分销自购">
|
||||
|
||||
@@ -3,10 +3,6 @@
|
||||
<script src="__CDN__/assets/addons/shopro/libs/echarts.min.js?v={$site.version|htmlentities}"></script>
|
||||
|
||||
<style>
|
||||
.dashboard-index .panel-block {
|
||||
/* background: #f1f4f6; */
|
||||
}
|
||||
|
||||
.dashboard-index .dashboard-index-main {
|
||||
--el-main-padding: 0;
|
||||
}
|
||||
@@ -19,67 +15,117 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.dashboard-index #userTotal,
|
||||
.dashboard-index #agentTotal,
|
||||
.dashboard-index #shareTotal {
|
||||
width: 100%;
|
||||
height: 66px;
|
||||
}
|
||||
|
||||
.dashboard-index .card {
|
||||
line-height: 1;
|
||||
background: var(--sa-background-assist);
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--sa-space);
|
||||
box-shadow: 0 2px 6px rgb(140 140 140 / 12%);
|
||||
padding: 12px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
|
||||
padding: 14px 16px;
|
||||
font-size: 12px;
|
||||
color: var(--sa-font);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.dashboard-index .card:hover {
|
||||
margin-bottom: 16px;
|
||||
transition: all .2s;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 1200px) {
|
||||
.dashboard-index .scale-card:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.dashboard-index .card:hover {
|
||||
box-shadow: 0 4px 14px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.dashboard-index .card .card-title .left {
|
||||
font-size: 14px;
|
||||
/* 顶部财富卡片 */
|
||||
.dashboard-index .wealth-kpi-card {
|
||||
padding: 16px 18px;
|
||||
}
|
||||
|
||||
.dashboard-index .wealth-kpi-card:hover {
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.dashboard-index .kpi-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--sa-subtitle);
|
||||
}
|
||||
|
||||
.dashboard-index .card .card-title .left .num {
|
||||
.dashboard-index .kpi-main-num {
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
margin: 12px 0 8px 0;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.dashboard-index .kpi-main-num .currency {
|
||||
font-size: 16px;
|
||||
margin-right: 2px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.dashboard-index .card .card-footer {
|
||||
margin-top: 12px;
|
||||
.dashboard-index .kpi-main-num .unit {
|
||||
font-size: 12px;
|
||||
margin-left: 2px;
|
||||
font-weight: 400;
|
||||
color: var(--sa-subfont);
|
||||
}
|
||||
|
||||
.dashboard-index .card .card-footer .left {
|
||||
margin-right: 8px;
|
||||
.dashboard-index .kpi-sub-row {
|
||||
font-size: 12px;
|
||||
color: var(--sa-subfont);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dashboard-index .card .card-footer .dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 8px;
|
||||
.dashboard-index .kpi-footer {
|
||||
padding-top: 10px;
|
||||
border-top: 1px dashed var(--sa-space);
|
||||
font-size: 12px;
|
||||
color: var(--sa-subfont);
|
||||
}
|
||||
|
||||
.dashboard-index .text-primary { color: #806af6 !important; }
|
||||
.dashboard-index .text-warning { color: #fa8c16 !important; }
|
||||
.dashboard-index .text-success { color: #52c41a !important; }
|
||||
.dashboard-index .text-cyan { color: #13c2c2 !important; }
|
||||
.dashboard-index .text-purple { color: #722ed1 !important; }
|
||||
.dashboard-index .text-danger { color: #f5222d !important; }
|
||||
.dashboard-index .text-dark { color: var(--sa-subtitle) !important; font-weight: 600; }
|
||||
.dashboard-index .font-bold { font-weight: 600; }
|
||||
.dashboard-index .cursor-pointer { cursor: pointer; }
|
||||
|
||||
/* 合伙人网络卡片 */
|
||||
.dashboard-index .network-card {
|
||||
padding: 14px 16px;
|
||||
min-height: 98px;
|
||||
}
|
||||
|
||||
.dashboard-index .network-badge {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
text-align: center;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dashboard-index .bg-primary { background: #806af6; }
|
||||
.dashboard-index .bg-cyan { background: #13c2c2; }
|
||||
.dashboard-index .bg-orange { background: #fa8c16; }
|
||||
|
||||
.dashboard-index #userTotal,
|
||||
.dashboard-index #agentTotal,
|
||||
.dashboard-index #shareTotal {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
/* 走势图面板 */
|
||||
.dashboard-index .bar-card {
|
||||
min-height: 358px;
|
||||
min-height: 388px;
|
||||
}
|
||||
|
||||
.dashboard-index #chartContent {
|
||||
width: 100%;
|
||||
height: 296px;
|
||||
height: 310px;
|
||||
}
|
||||
|
||||
.dashboard-index .tab-item {
|
||||
@@ -98,55 +144,100 @@
|
||||
.dashboard-index .tab-item.is-active {
|
||||
color: var(--sa-subtitle);
|
||||
font-weight: bold;
|
||||
border-bottom: 2px solid #806af6;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.dashboard-index .date-time .el-date-editor {
|
||||
--el-date-editor-datetimerange-width: 320px;
|
||||
}
|
||||
/* 分成透视 */
|
||||
.dashboard-index .split-panel {
|
||||
min-height: 388px;
|
||||
}
|
||||
|
||||
.dashboard-index .chart-card {
|
||||
height: 106px;
|
||||
color: var(--sa-subfont);
|
||||
.dashboard-index #splitPieContent {
|
||||
width: 100%;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.dashboard-index .chart-card .card-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
.dashboard-index .split-stat-item {
|
||||
padding: 6px 0;
|
||||
font-size: 12px;
|
||||
border-bottom: 1px solid #f6f8fa;
|
||||
}
|
||||
|
||||
.dashboard-index .chart-card .num {
|
||||
font-size: 24px;
|
||||
color: var(--sa-subtitle);
|
||||
.dashboard-index .split-stat-item:last-of-type {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.dashboard-index .chart-card .warning {
|
||||
color: var(--el-color-warning);
|
||||
.dashboard-index .split-color-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
/* 排行榜 */
|
||||
.dashboard-index .partner-ranking-card,
|
||||
.dashboard-index .goods-card,
|
||||
.dashboard-index .hot-search-card {
|
||||
height: 326px;
|
||||
min-height: 340px;
|
||||
}
|
||||
|
||||
.dashboard-index .goods-card .header,
|
||||
.dashboard-index .hot-search-card .header {
|
||||
.dashboard-index .card-header-title {
|
||||
line-height: 16px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: var(--sa-subtitle);
|
||||
margin-bottom: 16px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.dashboard-index .top {
|
||||
width: 18px;
|
||||
.dashboard-index .rank-badge {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
border-radius: 50%;
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
background: #f0f2f5;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.dashboard-index .rank-badge.top-1 { background: #fadb14; color: #fff; }
|
||||
.dashboard-index .rank-badge.top-2 { background: #bfbfbf; color: #fff; }
|
||||
.dashboard-index .rank-badge.top-3 { background: #d48806; color: #fff; }
|
||||
|
||||
.dashboard-index .todo-stat-card {
|
||||
padding: 12px 14px;
|
||||
border-radius: 6px;
|
||||
background: #fafbfc;
|
||||
border: 1px solid #ebedf0;
|
||||
cursor: pointer;
|
||||
transition: all .2s;
|
||||
}
|
||||
|
||||
.dashboard-index .todo-stat-card:hover {
|
||||
background: #f0f5ff;
|
||||
border-color: #adc6ff;
|
||||
}
|
||||
|
||||
.dashboard-index .daily-report-box {
|
||||
padding: 12px 14px;
|
||||
background: #fbfbfe;
|
||||
border: 1px solid #e8eafc;
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.dashboard-index #rankingContent {
|
||||
width: 192px;
|
||||
height: 192px;
|
||||
}
|
||||
|
||||
.dashboard-index .top {
|
||||
width: 18px;
|
||||
height: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div id="index" class="dashboard-index panel panel-default panel-intro" v-cloak>
|
||||
@@ -154,101 +245,168 @@
|
||||
<el-main class="dashboard-index-main">
|
||||
<el-scrollbar height="100%">
|
||||
{if $auth->check('shopro/dashboard/total')}
|
||||
<el-row :gutter="20">
|
||||
<!-- -- commission code start -- -->
|
||||
<!-- ================= 1. 全局财富大盘与资金安全核心 KPI ================= -->
|
||||
<el-row :gutter="16">
|
||||
<!-- 商城实收 GMV -->
|
||||
<el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">商城实收流水 (GMV)</span>
|
||||
<el-tag size="small" type="primary" effect="plain">实付总额</el-tag>
|
||||
</div>
|
||||
<div class="kpi-main-num text-primary">
|
||||
<span class="currency">¥</span>{{ state.wealth.total_gmv }}
|
||||
</div>
|
||||
<div class="kpi-sub-row sa-flex sa-row-between">
|
||||
<span>今日实收:<strong class="text-dark">¥{{ state.wealth.today_gmv }}</strong></span>
|
||||
<span>客单均价:<strong class="text-dark">¥{{ state.wealth.avg_order_amount }}</strong></span>
|
||||
</div>
|
||||
<div class="kpi-footer sa-flex sa-row-between">
|
||||
<span>累计支付单量</span>
|
||||
<span class="font-bold">{{ state.wealth.paid_order_count }} 单</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 现金分成支出与综合拨比 -->
|
||||
<el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">现金分成支出与拨比</span>
|
||||
<el-tooltip content="现金分成占实付GMV的百分比,评估平台精算模型健康度" placement="top">
|
||||
<el-tag size="small" :type="parseFloat(state.wealth.payout_rate) <= 35 ? 'success' : 'warning'" effect="plain">
|
||||
拨比 {{ state.wealth.payout_rate }}%
|
||||
</el-tag>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="kpi-main-num text-warning">
|
||||
<span class="currency">¥</span>{{ state.wealth.total_payout }}
|
||||
</div>
|
||||
<div class="kpi-sub-row sa-flex sa-row-between">
|
||||
<span>平台净留存:<strong class="text-success">¥{{ state.wealth.net_retention }}</strong></span>
|
||||
<span class="cursor-pointer text-primary" @click="onOpenReward('')">明细 →</span>
|
||||
</div>
|
||||
<div class="kpi-footer sa-flex sa-row-between">
|
||||
<span>今日分成支出</span>
|
||||
<span class="font-bold text-warning">¥{{ state.wealth.today_payout }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 马豆生态资产池 -->
|
||||
<el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">马豆生态资产蓄水池</span>
|
||||
<el-tag size="small" type="success" effect="plain">单向升值</el-tag>
|
||||
</div>
|
||||
<div class="kpi-main-num text-purple">
|
||||
{{ state.madow.total_madow }} <span class="unit">豆</span>
|
||||
</div>
|
||||
<div class="kpi-sub-row sa-flex sa-row-between">
|
||||
<span>单价:<strong>{{ state.madow.current_rate }}</strong></span>
|
||||
<span>万豆折合:<strong class="text-purple">¥{{ state.madow.rate_per_wan }}</strong></span>
|
||||
</div>
|
||||
<div class="kpi-footer sa-flex sa-row-between">
|
||||
<span>累计订单 N:<strong>{{ state.madow.history_order_n }} 单</strong></span>
|
||||
<span class="cursor-pointer text-primary" @click="onOpenDaily">日结池 →</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 消费兑换券杠杆池 -->
|
||||
<el-col :xs="24" :sm="12" :md="6" :lg="6" :xl="6">
|
||||
<div class="wealth-kpi-card card">
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span class="kpi-title">消费兑换券杠杆池</span>
|
||||
<el-tag size="small" type="info" effect="plain">1.5倍补贴</el-tag>
|
||||
</div>
|
||||
<div class="kpi-main-num text-danger">
|
||||
<span class="currency">¥</span>{{ state.voucher.total_voucher }}
|
||||
</div>
|
||||
<div class="kpi-sub-row sa-flex sa-row-between">
|
||||
<span>已释放:<strong>¥{{ state.voucher.released_voucher }}</strong></span>
|
||||
<span>待释放存量:<strong class="text-danger">¥{{ state.voucher.pending_voucher }}</strong></span>
|
||||
</div>
|
||||
<div class="kpi-footer sa-flex sa-row-between">
|
||||
<span>已抵扣消费:<strong>¥{{ state.voucher.used_voucher }}</strong></span>
|
||||
<span class="cursor-pointer text-primary" @click="onOpenVoucher">计划 →</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ================= 2. 合伙股东网络与活力矩阵 ================= -->
|
||||
<el-row :gutter="16">
|
||||
<!-- 品酒师基础会员 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<div class="scale-card card ">
|
||||
<div class="card-title sa-flex sa-row-between">
|
||||
<div class="left">{{state.total.user.title}} <span
|
||||
class="num">{{state.total.user.data.total}}</span> </div>
|
||||
<div>{{state.total.user.tip}} <span>{{state.total.user.data.today}}</span> </div>
|
||||
</div>
|
||||
<div id="userTotal"></div>
|
||||
<div class="card-footer sa-flex">
|
||||
<span class="left sa-flex">
|
||||
<span class="dot" :style="{ background: state.total.user.color }"></span>
|
||||
<span>{{ state.total.user.footer }}</span>
|
||||
<div class="network-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<span class="network-badge bg-primary mr-2">品</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">品酒师基础会员</span>
|
||||
</div>
|
||||
<span class="text-primary font-bold cursor-pointer" style="font-size: 18px;" @click="onOpenAgent(1)">
|
||||
{{ state.partner.taster_count }} <span style="font-size: 12px; color: var(--sa-subfont);">人</span>
|
||||
</span>
|
||||
<span>{{ state.total.user.data.week }}</span>
|
||||
</div>
|
||||
<div class="sa-flex sa-row-between text-muted" style="font-size: 12px;">
|
||||
<span>30日自购活跃率:<strong class="text-success">{{ state.partner.active_rate }}%</strong></span>
|
||||
<span>正常:<strong class="text-success">{{ state.partner.active_count }}</strong> 人 / 暂停:<strong class="text-muted">{{ state.partner.inactive_count }}</strong> 人</span>
|
||||
</div>
|
||||
<el-progress :percentage="state.partner.active_rate" :stroke-width="5" :show-text="false" color="#52c41a" style="margin-top: 8px;"></el-progress>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 创客合伙人梯队 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<div class="scale-card card ">
|
||||
<div class="card-title sa-flex sa-row-between">
|
||||
<div class="left">{{state.total.agent.title}} <span
|
||||
class="num">{{state.total.agent.data.total}}</span> </div>
|
||||
<div>{{state.total.agent.tip}} <span>{{state.total.agent.data.today}}</span> </div>
|
||||
</div>
|
||||
<div id="agentTotal"></div>
|
||||
<div class="card-footer sa-flex">
|
||||
<span class="left sa-flex">
|
||||
<span class="dot" :style="{ background: state.total.agent.color }"></span>
|
||||
<span>{{ state.total.agent.footer }}</span>
|
||||
<div class="network-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<span class="network-badge bg-cyan mr-2">创</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">创客合伙股东 (V1~V6)</span>
|
||||
</div>
|
||||
<span class="text-cyan font-bold cursor-pointer" style="font-size: 18px;" @click="onOpenAgent('')">
|
||||
{{ state.partner.maker_count }} <span style="font-size: 12px; color: var(--sa-subfont);">人</span>
|
||||
</span>
|
||||
<span>{{ state.total.agent.data.week }}</span>
|
||||
</div>
|
||||
<div class="sa-flex sa-flex-wrap" style="margin-top: 6px;">
|
||||
<el-tag size="small" class="mr-1 mb-1" effect="light" type="info" v-for="(cnt, lv) in state.partner.maker_levels" :key="lv">
|
||||
{{ lv }}: {{ cnt }}人
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 实体与区域渠道 -->
|
||||
<el-col :xs="24" :sm="24" :md="8" :lg="8" :xl="8">
|
||||
<div class="scale-card card ">
|
||||
<div class="card-title sa-flex sa-row-between">
|
||||
<div class="left">{{state.total.share.title}} <span
|
||||
class="num">{{state.total.share.data.total}}</span> </div>
|
||||
<div>{{state.total.share.tip}} <span>{{state.total.share.data.today}}</span> </div>
|
||||
<div class="network-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<span class="network-badge bg-orange mr-2">馆</span>
|
||||
<span class="font-bold text-dark" style="font-size: 14px;">实体与区域渠道</span>
|
||||
</div>
|
||||
<div class="sa-flex">
|
||||
<span class="text-orange font-bold mr-2" style="font-size: 15px;">酒道馆 {{ state.partner.venue_count }} 家</span>
|
||||
<span class="text-purple font-bold" style="font-size: 15px;">区县 {{ state.partner.county_count }} 个</span>
|
||||
</div>
|
||||
</div>
|
||||
<div id="shareTotal"></div>
|
||||
<div class="card-footer sa-flex">
|
||||
<span class="left sa-flex">
|
||||
<span class="dot" :style="{ background: state.total.share.color }"></span>
|
||||
<span>{{ state.total.share.footer }}</span>
|
||||
</span>
|
||||
<span>{{ state.total.share.data.week }}</span>
|
||||
<div class="sa-flex sa-row-between text-muted" style="font-size: 12px; margin-top: 10px;">
|
||||
<span>享团队拆分 (50~100元) + 平台日结均分池</span>
|
||||
<span class="cursor-pointer text-primary" @click="onOpenAgent('')">渠道档案 →</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<!-- -- commission code end -- -->
|
||||
<!-- <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<div class="scale-card card ">
|
||||
<div class="card-title sa-flex sa-row-between">
|
||||
<div class="left">{{state.total.user.title}} <span
|
||||
class="num">{{state.total.user.data.total}}</span> </div>
|
||||
<div>{{state.total.user.tip}} <span>{{state.total.user.data.today}}</span> </div>
|
||||
</div>
|
||||
<div id="userTotal"></div>
|
||||
<div class="card-footer sa-flex">
|
||||
<span class="left sa-flex">
|
||||
<span class="dot" :style="{ background: state.total.user.color }"></span>
|
||||
<span>{{ state.total.user.footer }}</span>
|
||||
</span>
|
||||
<span>{{ state.total.user.data.week }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<div class="scale-card card ">
|
||||
<div class="card-title sa-flex sa-row-between">
|
||||
<div class="left">{{state.total.share.title}} <span
|
||||
class="num">{{state.total.share.data.total}}</span> </div>
|
||||
<div>{{state.total.share.tip}} <span>{{state.total.share.data.today}}</span> </div>
|
||||
</div>
|
||||
<div id="shareTotal"></div>
|
||||
<div class="card-footer sa-flex">
|
||||
<span class="left sa-flex">
|
||||
<span class="dot" :style="{ background: state.total.share.color }"></span>
|
||||
<span>{{ state.total.share.footer }}</span>
|
||||
</span>
|
||||
<span>{{ state.total.share.data.week }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</el-col> -->
|
||||
</el-row>
|
||||
{/if}
|
||||
|
||||
{if $auth->check('shopro/dashboard/chart')}
|
||||
<el-row :gutter="20">
|
||||
<!-- ================= 3. 核心动态走势与分成结构透视 ================= -->
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧:时段动态分析图 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="16" :xl="16">
|
||||
<div class="bar-card card">
|
||||
<div class="sa-flex sa-row-between sa-flex-wrap">
|
||||
<div class="sa-flex sa-row-between sa-flex-wrap mb-2">
|
||||
<div class="sa-flex">
|
||||
<div class="tab-item" :class="chart.tabActive == key ? 'is-active' : ''"
|
||||
v-for="(value, key) in chart.tabsData" :key="key"
|
||||
@@ -268,47 +426,171 @@
|
||||
<div id="chartContent"></div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:收益分成多维透视 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="8" :xl="8">
|
||||
<el-row :gutter="20">
|
||||
<el-col :xs="12" :sm="8" :md="12" :lg="12" :xl="12" v-for="(value, key) in statistics"
|
||||
:key="key">
|
||||
<div class="chart-card scale-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<img class="card-icon"
|
||||
:src="`/assets/addons/shopro/img/dashboard/${key}.png`" />
|
||||
<div class="sa-flex" @click="onOpen(value.status)">
|
||||
<el-button type="info" link size="small">详情
|
||||
<el-icon class="ml-1">
|
||||
<arrow-right />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="split-panel card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<span class="card-header-title" style="margin-bottom: 0;">收益分成结构透视</span>
|
||||
<span class="cursor-pointer text-primary" style="font-size: 12px;" @click="onOpenReward('')">全部流水 →</span>
|
||||
</div>
|
||||
|
||||
<div class="sa-flex sa-row-center">
|
||||
<div id="splitPieContent"></div>
|
||||
</div>
|
||||
|
||||
<div class="split-stat-list mt-2">
|
||||
<div class="split-stat-item sa-flex sa-row-between" v-for="item in chart.data.splitStructure" :key="item.key">
|
||||
<div class="sa-flex">
|
||||
<span class="split-color-dot" :style="{ background: item.color }"></span>
|
||||
<span>{{ item.name }}</span>
|
||||
</div>
|
||||
<div class="num mb-1">{{ value.num }}</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span>{{ value.text }}</span>
|
||||
<el-popover popper-class="sa-popper" placement="top" trigger="hover"
|
||||
:content="value.tip">
|
||||
<template #reference>
|
||||
<el-icon class="warning">
|
||||
<Warning />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-popover>
|
||||
<div class="sa-flex">
|
||||
<span class="font-bold mr-2">¥{{ item.amount }}</span>
|
||||
<el-tag size="small" type="info" effect="plain">{{ item.ratio }}%</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div class="sa-flex sa-row-between mt-3 pt-2" style="border-top: 1px dashed var(--sa-space); font-size: 12px; color: var(--sa-subfont);">
|
||||
<span>区间马豆分红:<strong class="text-purple">{{ chart.data.periodMadow }} 豆</strong></span>
|
||||
<el-button type="primary" link size="small" @click="onOpenDaily">日结池详情</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
{/if}
|
||||
|
||||
{if $auth->check('shopro/dashboard/ranking')}
|
||||
<el-row :gutter="20">
|
||||
<!-- ================= 4. 合伙人收益排行 & 电商运营与风控监控 ================= -->
|
||||
<el-row :gutter="16">
|
||||
<!-- 左侧:合伙股东收益排行 TOP 5 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
|
||||
<div class="partner-ranking-card card">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="card-header-title" style="margin-bottom: 0;">
|
||||
<span>合伙股东收益排行榜 (TOP 5)</span>
|
||||
</div>
|
||||
<span class="cursor-pointer text-primary" style="font-size: 12px;" @click="onOpenReward('')">查看全部 →</span>
|
||||
</div>
|
||||
|
||||
<el-table class="sa-table" :data="ranking.partner" stripe>
|
||||
<el-table-column label="排名" width="55" align="center">
|
||||
<template #default="scope">
|
||||
<span class="rank-badge" :class="`top-${scope.$index + 1}`">
|
||||
{{ scope.$index + 1 }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="股东会员" min-width="140">
|
||||
<template #default="scope">
|
||||
<div class="sa-flex">
|
||||
<sa-image :url="scope.row.avatar" size="24" style="border-radius: 50%;"></sa-image>
|
||||
<div class="ml-2">
|
||||
<div class="sa-table-line-1 font-bold">{{ scope.row.nickname }}</div>
|
||||
<div style="font-size: 11px; color: var(--sa-subfont);">{{ scope.row.mobile || '' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="身份" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-tag size="small" :type="scope.row.is_partner ? 'danger' : 'info'" effect="light">
|
||||
{{ scope.row.identity_text }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="累计收益" width="110" align="right">
|
||||
<template #default="scope">
|
||||
<span class="text-warning font-bold">¥{{ scope.row.total_income }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:电商运营风控 & 日结监控 -->
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
|
||||
<div class="card" style="min-height: 340px;">
|
||||
<div class="card-header-title">电商运营与日结对账监控</div>
|
||||
|
||||
<!-- 待处理订单指标卡 -->
|
||||
<el-row :gutter="12" class="mb-3">
|
||||
<el-col :span="8">
|
||||
<div class="todo-stat-card" @click="onOpen('nosend')">
|
||||
<div style="color: var(--sa-subfont); margin-bottom: 4px;">待发货订单</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 20px; font-weight: 700;" :class="statistics.noSend.num > 0 ? 'text-warning' : 'text-dark'">
|
||||
{{ statistics.noSend.num }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="todo-stat-card" @click="onOpen('aftersale')">
|
||||
<div style="color: var(--sa-subfont); margin-bottom: 4px;">售后维权</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 20px; font-weight: 700;" :class="statistics.aftersale.num > 0 ? 'text-danger' : 'text-dark'">
|
||||
{{ statistics.aftersale.num }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="todo-stat-card" @click="onOpen('refund')">
|
||||
<div style="color: var(--sa-subfont); margin-bottom: 4px;">退款订单</div>
|
||||
<div class="sa-flex sa-row-between">
|
||||
<span style="font-size: 20px; font-weight: 700;">
|
||||
{{ statistics.refund.num }}
|
||||
</span>
|
||||
<el-icon><arrow-right /></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 最新日结对账卡 -->
|
||||
<div class="daily-report-box" v-if="ranking.recentReport">
|
||||
<div class="sa-flex sa-row-between mb-2">
|
||||
<div class="sa-flex">
|
||||
<el-icon class="mr-1 text-primary"><Calendar /></el-icon>
|
||||
<strong class="text-dark">最近一次日结概览 ({{ ranking.recentReport.settle_date }})</strong>
|
||||
</div>
|
||||
<el-tag size="small" :type="ranking.recentReport.status == 1 ? 'success' : 'danger'" effect="plain">
|
||||
{{ ranking.recentReport.status_text }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<el-row :gutter="10" style="color: var(--sa-subfont); line-height: 1.8;">
|
||||
<el-col :span="12">
|
||||
<div>合格单量:<strong class="text-dark">{{ ranking.recentReport.order_count }} 单</strong></div>
|
||||
<div>当日 GMV:<strong class="text-dark">¥{{ ranking.recentReport.gmv }}</strong></div>
|
||||
<div>全网累计 N:<strong>{{ ranking.recentReport.order_count_n }}</strong></div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div>马豆均分:<strong class="text-purple">{{ ranking.recentReport.madow_pool }} 豆</strong> ({{ ranking.recentReport.share_user_count }}人)</div>
|
||||
<div>酒道馆奖池:<strong class="text-warning">¥{{ ranking.recentReport.venue_pool }}</strong> ({{ ranking.recentReport.venue_user_count }}人)</div>
|
||||
<div>区县奖池:<strong class="text-warning">¥{{ ranking.recentReport.county_pool }}</strong> ({{ ranking.recentReport.county_user_count }}人)</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div class="sa-flex sa-row-right mt-2">
|
||||
<el-button type="primary" link size="small" @click="onOpenDaily">查看日结对账全记录 →</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="text-muted sa-flex sa-row-center" style="padding: 24px 0;">
|
||||
暂无日结对账数据
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- ================= 5. 商品热销榜与热搜榜 (保持原有生态) ================= -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
|
||||
<div class="goods-card card">
|
||||
<div class="header">
|
||||
<span>销量榜</span>
|
||||
</div>
|
||||
<div class="card-header-title">热销商品排行榜</div>
|
||||
<el-table class="sa-table" :data="ranking.goods" stripe>
|
||||
<el-table-column label="排名" width="60" align="center">
|
||||
<template #default="scope">
|
||||
@@ -316,7 +598,7 @@
|
||||
:src="`/assets/addons/shopro/img/dashboard/top${scope.$index + 1}.png`" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="200">
|
||||
<el-table-column label="商品名称" min-width="200">
|
||||
<template #default="scope">
|
||||
<div class="sa-flex">
|
||||
<sa-image :url="scope.row.image" size="20"></sa-image>
|
||||
@@ -326,42 +608,36 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="销量" width="100">
|
||||
<el-table-column label="销量" width="90" align="right">
|
||||
<template #default="scope">
|
||||
<div class="sa-table-line-1">
|
||||
{{ scope.row.real_sales || '-' }}
|
||||
</div>
|
||||
<span>{{ scope.row.real_sales || '-' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
|
||||
<div class="hot-search-card card">
|
||||
<div class="header">
|
||||
<span>热搜榜</span>
|
||||
</div>
|
||||
<div class="card-header-title">关键词热搜榜</div>
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :sm="24" :md="16" :lg="16" :xl="16">
|
||||
<el-table class="sa-table" :data="ranking.hot_search" stripe>
|
||||
<el-table-column label="排名" width="60" align="center">
|
||||
<el-table-column label="排名" width="50" align="center">
|
||||
<template #default="scope">
|
||||
<img class="top"
|
||||
:src="`/assets/addons/shopro/img/dashboard/top${scope.$index + 1}.png`" />
|
||||
<span class="rank-badge" :class="`top-${scope.$index + 1}`">
|
||||
{{ scope.$index + 1 }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="名称" min-width="200">
|
||||
<el-table-column label="搜索词" min-width="160">
|
||||
<template #default="scope">
|
||||
<div class="sa-table-line-1">
|
||||
{{ scope.row.keyword || '-' }}
|
||||
</div>
|
||||
<div class="sa-table-line-1">{{ scope.row.keyword || '-' }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="搜索量(次)" width="100">
|
||||
<el-table-column label="频次" width="80" align="right">
|
||||
<template #default="scope">
|
||||
<div class="sa-table-line-1">
|
||||
{{ scope.row.num }}
|
||||
</div>
|
||||
<span>{{ scope.row.num }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
@@ -2,21 +2,25 @@
|
||||
|
||||
<div id="index" class="reward-index panel panel-default panel-intro" v-cloak>
|
||||
<el-container class="panel-block">
|
||||
<el-header class="sa-header">
|
||||
<el-header class="sa-header" style="height:auto;padding-bottom:12px;">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">日结对账</div>
|
||||
<div class="sa-flex" style="gap:16px;margin-top:8px;font-size:13px">
|
||||
<span>昨日单数 {{ state.kpi.yesterday?.order_count || 0 }}</span>
|
||||
<span>昨日GMV {{ state.kpi.yesterday?.gmv || '0.00' }}</span>
|
||||
<span>昨日现金 {{ state.kpi.yesterday_cash || '0.00' }}</span>
|
||||
<span>昨日马豆池 {{ state.kpi.yesterday?.madow_pool || '0' }}</span>
|
||||
<span>今日单数 {{ state.kpi.today?.order_count || 0 }}</span>
|
||||
<span>今日现金 {{ state.kpi.today_cash || '0.00' }}</span>
|
||||
<span>暂停领取 {{ state.kpi.inactive_count || 0 }} 人</span>
|
||||
<div class="left-name">日结对账与财务监控</div>
|
||||
<div class="sa-flex" style="gap:14px;margin-top:8px;font-size:13px;flex-wrap:wrap;">
|
||||
<span>昨日合格单 <b>{{ state.kpi.yesterday?.order_count || 0 }}</b> 单</span>
|
||||
<span>昨日 GMV <b>¥ {{ state.kpi.yesterday?.gmv || '0.00' }}</b></span>
|
||||
<span>昨日现金总拨发 <b style="color:#f56c6c;">¥ {{ state.kpi.yesterday_cash || '0.00' }}</b></span>
|
||||
<span>昨日综合拨比 <b :style="{color: (state.kpi.yesterday_payout_ratio_val > 70 ? '#e6a23c' : '#67c23a')}">{{ state.kpi.yesterday_payout_ratio || '0%' }}</b></span>
|
||||
<span>兑换券待履约负债 <b style="color:#e6a23c;">¥ {{ state.kpi.voucher_debt || '0.00' }}</b></span>
|
||||
<span>活跃馆/县 <b>{{ state.kpi.venue_active_count || 0 }}</b> 馆 / <b>{{ state.kpi.county_active_count || 0 }}</b> 县</span>
|
||||
<span>今日累计现金 <b>¥ {{ state.kpi.today_cash || '0.00' }}</b></span>
|
||||
<span style="color:#909399;">暂停领取 <b>{{ state.kpi.inactive_count || 0 }}</b> 人</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sa-title-right">
|
||||
<div class="sa-title-right sa-flex" style="gap:8px;">
|
||||
{if $auth->check('shopro/partner/daily/preview')}
|
||||
<el-button type="primary" icon="DataAnalysis" @click="onOpenPreview">日结预跑试算 (Dry Run)</el-button>
|
||||
{/if}
|
||||
<el-button class="sa-button-refresh" icon="RefreshRight" @click="getData"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -57,4 +61,96 @@
|
||||
<sa-pagination v-model="pagination" @pagination-change="getData"></sa-pagination>
|
||||
</el-footer>
|
||||
</el-container>
|
||||
|
||||
<!-- 日结预跑试算弹窗 (Dry Run) -->
|
||||
<el-dialog v-model="previewDialog.visible" title="日结分润试算 (Dry Run 预跑监控)" width="820px" destroy-on-close>
|
||||
<div style="margin-bottom:16px;" class="sa-flex sa-row-between">
|
||||
<div class="sa-flex" style="gap:10px;">
|
||||
<span style="font-size:13px;color:#606266;">试算日期:</span>
|
||||
<el-date-picker v-model="previewDialog.date" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" size="small" style="width:160px;"></el-date-picker>
|
||||
<el-button type="primary" size="small" :loading="previewDialog.loading" @click="runPreview">开始模拟试算</el-button>
|
||||
</div>
|
||||
<div v-if="previewDialog.result">
|
||||
<el-tag v-if="previewDialog.result.is_settled" type="success">该日已执行正式日结</el-tag>
|
||||
<el-tag v-else type="warning">尚未正式结算(以下为只读预估)</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="previewDialog.result" v-loading="previewDialog.loading">
|
||||
<!-- 财务现金拨比盘 -->
|
||||
<div style="background:#fdf6ec;border:1px solid #faecd8;border-radius:6px;padding:14px;margin-bottom:16px;">
|
||||
<div style="font-weight:600;font-size:14px;color:#e6a23c;margin-bottom:10px;">综合现金拨比与安全监控</div>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<div style="font-size:12px;color:#909399;">当日GMV总额</div>
|
||||
<div style="font-size:16px;font-weight:bold;margin-top:2px;">¥ {{ previewDialog.result.gmv }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="font-size:12px;color:#909399;">当日即时奖 (推三/极差等)</div>
|
||||
<div style="font-size:16px;font-weight:bold;color:#f56c6c;margin-top:2px;">¥ {{ previewDialog.result.financial?.instant_cash }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="font-size:12px;color:#909399;">日结均分现金 (馆/县)</div>
|
||||
<div style="font-size:16px;font-weight:bold;color:#f56c6c;margin-top:2px;">¥ {{ previewDialog.result.financial?.daily_cash }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="font-size:12px;color:#909399;">综合现金拨比率</div>
|
||||
<div style="font-size:18px;font-weight:bold;margin-top:2px;" :style="{color: previewDialog.result.financial?.is_alert ? '#f56c6c' : '#67c23a'}">
|
||||
{{ previewDialog.result.financial?.payout_ratio }}
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="previewDialog.result.financial?.is_alert" style="margin-top:8px;font-size:12px;color:#f56c6c;">
|
||||
⚠️ 注意:当日综合现金拨比超过 70% 财务风控警示线,建议关注极差/平级奖金释放与均分池规模。
|
||||
</div>
|
||||
<div style="margin-top:8px;font-size:12px;color:#67c23a;">
|
||||
💡 沉淀资金效益:因分母为0留存平台的酒道馆/区县资金为 <b>¥ {{ previewDialog.result.financial?.saved_cash }}</b>。
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 均分池测算卡片 -->
|
||||
<el-row :gutter="16" style="margin-bottom:16px;">
|
||||
<el-col :span="8">
|
||||
<div style="background:#f4f4f5;padding:12px;border-radius:6px;">
|
||||
<div style="font-weight:600;font-size:13px;margin-bottom:6px;">酒道馆均分池</div>
|
||||
<div style="font-size:12px;color:#606266;line-height:1.8;">
|
||||
奖池总额:¥ <b>{{ previewDialog.result.venue?.pool }}</b><br>
|
||||
合格人数:<b>{{ previewDialog.result.venue?.user_count }}</b> 人<br>
|
||||
人均分润:¥ <b>{{ previewDialog.result.venue?.each }}</b><br>
|
||||
沉淀金额:¥ <b>{{ previewDialog.result.venue?.saved }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div style="background:#f4f4f5;padding:12px;border-radius:6px;">
|
||||
<div style="font-weight:600;font-size:13px;margin-bottom:6px;">区县代理均分池</div>
|
||||
<div style="font-size:12px;color:#606266;line-height:1.8;">
|
||||
奖池总额:¥ <b>{{ previewDialog.result.county?.pool }}</b><br>
|
||||
合格人数:<b>{{ previewDialog.result.county?.user_count }}</b> 人<br>
|
||||
人均分润:¥ <b>{{ previewDialog.result.county?.each }}</b><br>
|
||||
沉淀金额:¥ <b>{{ previewDialog.result.county?.saved }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div style="background:#f4f4f5;padding:12px;border-radius:6px;">
|
||||
<div style="font-weight:600;font-size:13px;margin-bottom:6px;">马豆分红池 (1:1 GMV)</div>
|
||||
<div style="font-size:12px;color:#606266;line-height:1.8;">
|
||||
马豆总池:<b>{{ previewDialog.result.madow?.pool }}</b> 豆<br>
|
||||
合格人数:<b>{{ previewDialog.result.madow?.user_count }}</b> 人<br>
|
||||
人均分豆:<b>{{ previewDialog.result.madow?.each }}</b><br>
|
||||
即时汇率:<b>{{ previewDialog.result.rate }}</b>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="background:#fafafa;border:1px solid #ebeef5;padding:10px 14px;border-radius:4px;font-size:12px;color:#606266;">
|
||||
<span>📅 兑换券释放预估:当日按计划释放兑换券总额约 <b>¥ {{ previewDialog.result.voucher?.release_estimate }}</b>(以365天逐日释放)。</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="previewDialog.visible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
@@ -36,9 +36,15 @@
|
||||
<sa-user-profile :user="scope.row.buyer" :id="scope.row.buyer_id"></sa-user-profile>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="订单" min-width="180">
|
||||
<el-table-column label="订单" min-width="190">
|
||||
<template #default="scope">
|
||||
{{ scope.row.order?.order_sn || scope.row.order_id }}
|
||||
<div v-if="scope.row.order_id > 0" class="sa-flex sa-row-between">
|
||||
<el-link type="primary" :underline="false" @click="onOrderTree(scope.row)">
|
||||
{{ scope.row.order?.order_sn || scope.row.order_id }}
|
||||
</el-link>
|
||||
<el-button type="primary" link size="small" @click="onOrderTree(scope.row)">透视</el-button>
|
||||
</div>
|
||||
<span v-else>{{ scope.row.order?.order_sn || scope.row.order_id }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reward_type_text" label="奖项" min-width="120"></el-table-column>
|
||||
@@ -58,8 +64,9 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="formula" label="公式摘要" min-width="200"></el-table-column>
|
||||
<el-table-column label="操作" min-width="120" fixed="right">
|
||||
<el-table-column label="操作" min-width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button v-if="scope.row.order_id > 0" type="primary" link @click="onOrderTree(scope.row)">整单透视</el-button>
|
||||
<el-button type="primary" link @click="onViewExt(scope.row)">查看公式</el-button>
|
||||
{if $auth->check('shopro/partner/reward/back')}
|
||||
<el-button v-if="scope.row.status==1 && !scope.row.settle_date" type="danger" link
|
||||
@@ -94,4 +101,79 @@
|
||||
<el-button type="primary" @click="onReissue">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="orderTreeDialog.visible" title="订单分润透视(利益分配树)" width="860px" destroy-on-close>
|
||||
<div v-loading="orderTreeDialog.loading">
|
||||
<div style="background:#f8f9fa;padding:14px;border-radius:6px;margin-bottom:16px;">
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="6">
|
||||
<div style="color:#909399;font-size:12px;">订单编号</div>
|
||||
<div style="font-weight:600;font-size:13px;margin-top:4px;">{{ orderTreeDialog.data.order?.order_sn || '-' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="color:#909399;font-size:12px;">买家信息</div>
|
||||
<div style="font-weight:600;font-size:13px;margin-top:4px;">{{ orderTreeDialog.data.order?.buyer_nickname || '-' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="color:#909399;font-size:12px;">实付金额</div>
|
||||
<div style="font-weight:600;font-size:15px;color:#e6a23c;margin-top:4px;">¥ {{ orderTreeDialog.data.order?.pay_fee || '0.00' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<div style="color:#909399;font-size:12px;">状态 / 付款时间</div>
|
||||
<div style="font-size:12px;margin-top:4px;">
|
||||
<span class="sa-color--success">{{ orderTreeDialog.data.order?.status_text }}</span> / {{ orderTreeDialog.data.order?.paid_time }}
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider style="margin:12px 0"></el-divider>
|
||||
<el-row :gutter="16">
|
||||
<el-col :span="8">
|
||||
<div style="color:#909399;font-size:12px;">本单现金分润总额</div>
|
||||
<div style="font-weight:bold;font-size:18px;color:#f56c6c;margin-top:2px;">¥ {{ orderTreeDialog.data.summary?.total_cash || '0.00' }}</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div style="color:#909399;font-size:12px;">现金综合拨比率</div>
|
||||
<div style="font-weight:bold;font-size:18px;margin-top:2px;" :style="{color: parseFloat(orderTreeDialog.data.summary?.payout_ratio || 0) > 70 ? '#e6a23c' : '#67c23a'}">
|
||||
{{ orderTreeDialog.data.summary?.payout_ratio || '0%' }}
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div style="color:#909399;font-size:12px;">分红马豆 (1:1 GMV)</div>
|
||||
<div style="font-weight:bold;font-size:18px;color:#409eff;margin-top:2px;">{{ orderTreeDialog.data.summary?.total_madow || '0.00000000' }}</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<div v-if="orderTreeDialog.data.voucher_plan" style="margin-top:12px;background:#fff;padding:8px 12px;border-radius:4px;border:1px dashed #dcdfe6;font-size:12px;color:#606266;">
|
||||
<span style="font-weight:600;color:#303133;">1.5倍兑换券履约计划:</span>
|
||||
总额 ¥ {{ orderTreeDialog.data.voucher_plan.total_amount }},已释放 ¥ {{ orderTreeDialog.data.voucher_plan.released_amount }} (已释放 {{ orderTreeDialog.data.voucher_plan.released_days }}/365天),状态:{{ orderTreeDialog.data.voucher_plan.status_text }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="font-weight:600;margin-bottom:8px;font-size:14px;">利益链分配明细</div>
|
||||
<el-table :data="orderTreeDialog.data.rewards || []" size="small" border stripe max-height="320">
|
||||
<el-table-column prop="role_text" label="受益角色" width="90"></el-table-column>
|
||||
<el-table-column label="受益人" min-width="130">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.user_nickname }} (ID:{{ scope.row.user_id }})</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="reward_type_text" label="奖项" width="130"></el-table-column>
|
||||
<el-table-column label="现金金额" width="100">
|
||||
<template #default="scope">
|
||||
<span style="font-weight:600;color:#f56c6c;">¥ {{ scope.row.amount }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="madow" label="马豆" width="100"></el-table-column>
|
||||
<el-table-column prop="status_text" label="状态" width="80">
|
||||
<template #default="scope">
|
||||
<span :class="scope.row.status == 1 ? 'sa-color--success' : (scope.row.status == -2 ? 'sa-color--danger' : 'sa-color--info')">
|
||||
{{ scope.row.status_text }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="formula" label="计算规则/说明" min-width="180"></el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="orderTreeDialog.visible = false">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<el-header class="sa-header">
|
||||
<div class="sa-title sa-flex sa-row-between">
|
||||
<div class="sa-title-left">
|
||||
<div class="left-name">兑换券计划</div>
|
||||
<div class="left-name">兑换券</div>
|
||||
</div>
|
||||
<div class="sa-title-right">
|
||||
<el-button class="sa-button-refresh" icon="RefreshRight" @click="getData"></el-button>
|
||||
|
||||
在新工单中引用
屏蔽一个用户