chore: init saasshop repo + sql migrations runner + gitee go

This commit is contained in:
萝卜
2026-03-10 11:31:02 +00:00
commit 50f15cdea8
210 changed files with 29534 additions and 0 deletions

View File

@@ -0,0 +1,25 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>站点后台登录 - SaaSShop</title>
<link rel="stylesheet" href="/css/admin-base.css">
<link rel="stylesheet" href="/css/site-admin.css">
</head>
<body class="login-page">
<div class="login-card">
<h1>站点后台登录</h1>
<p class="muted">当前入口面向站点运营方。第一阶段复用已绑定商家的管理员账号作为站点管理员演示登录。</p>
@if ($errors->any())
<div class="error-inline">{{ $errors->first() }}</div>
@endif
<form class="login-form" method="post" action="/site-admin/login">
@csrf
<input type="email" name="email" placeholder="邮箱" value="{{ old('email', 'merchant.admin@demo.local') }}">
<input type="password" name="password" placeholder="密码" value="Merchant@123456">
<button type="submit">登录站点后台</button>
</form>
</div>
</body>
</html>

View File

@@ -0,0 +1,18 @@
@extends('site_admin.layouts.app')
@section('title', '站点后台仪表盘')
@section('page_title', '站点后台仪表盘')
@section('content')
<div class="card card-spaced">
<p>当前站点:<strong>{{ $site->name }}</strong>{{ $site->slug }})。这里是站点运营视角后台,第一阶段先提供站点级总览、商品、订单与商家入口骨架。</p>
<p class="muted mb-0">缓存状态:{{ $cacheMeta['store'] }} / TTL {{ $cacheMeta['ttl'] }}</p>
</div>
<div class="grid">
<div class="card"><div class="muted">站点管理员</div><div class="num">{{ $stats['admins'] }}</div></div>
<div class="card"><div class="muted">站点用户</div><div class="num">{{ $stats['users'] }}</div></div>
<div class="card"><div class="muted">站点商品</div><div class="num">{{ $stats['products'] }}</div></div>
<div class="card"><div class="muted">站点订单</div><div class="num">{{ $stats['orders'] }}</div></div>
<div class="card"><div class="muted">待处理订单</div><div class="num">{{ $stats['pending_orders'] }}</div></div>
</div>
@endsection

View File

@@ -0,0 +1,61 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'SaaSShop 站点后台')</title>
<link rel="stylesheet" href="/css/admin-base.css">
<link rel="stylesheet" href="/css/site-admin.css">
</head>
<body>
<div class="layout">
<aside class="sidebar">
<h2 class="sidebar-brand">SaaSShop</h2>
<div class="muted">站点后台 / Site Console</div>
<div class="group-title">总览</div>
<a class="sidebar-link" href="/site-admin">站点仪表盘</a>
<div class="group-title">站点运营</div>
<a class="sidebar-link" href="/site-admin/merchants">站点商家</a>
<a class="sidebar-link" href="/site-admin/products">站点商品</a>
<a class="sidebar-link" href="/site-admin/orders">站点订单</a>
<div class="group-title">切换入口</div>
<a class="sidebar-link" href="/admin">总台管理</a>
<a class="sidebar-link" href="/merchant-admin">商家后台</a>
<a class="sidebar-link" href="/">返回首页</a>
<form class="sidebar-logout" method="post" action="/site-admin/logout">
@csrf
<button type="submit">退出登录</button>
</form>
</aside>
<main class="content">
<div class="top">
<div>
<h1 class="page-title">@yield('page_title', '站点后台')</h1>
<div class="muted">当前登录:{{ session('admin_name') }}{{ session('admin_email') }} / 站点 ID{{ session('admin_site_id') }}</div>
<div class="badge badge-scope">当前作用域:站点管理员</div>
</div>
</div>
@if(session('success'))
<div class="flash">{{ session('success') }}</div>
@endif
@if(session('warning'))
<div class="warning">{{ session('warning') }}</div>
@endif
@if($errors->any())
<div class="error-box">
<strong>提交失败:</strong>
<ul class="ml-18">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@yield('content')
</main>
</div>
</body>
</html>

View File

@@ -0,0 +1,308 @@
@extends('site_admin.layouts.app')
@section('title', '站点商家')
@section('page_title', '站点商家')
@section('content')
@php
$productWorkbenchUrl = '/site-admin/products?sort=stock_desc&status=published';
$lowStockWorkbenchUrl = '/site-admin/products?sort=stock_asc&status=published';
$latestProductWorkbenchUrl = '/site-admin/products?sort=latest';
$orderWorkbenchUrl = '/site-admin/orders?sort=pay_amount_desc&payment_status=paid';
$pendingOrderWorkbenchUrl = '/site-admin/orders?sort=latest&payment_status=unpaid';
$failedOrderWorkbenchUrl = '/site-admin/orders?sort=latest&payment_status=failed';
@endphp
<div class="card card-spaced">
<p class="muted mt-0">当前阶段继续复用 merchant 承接站点关系,但页面已先补最小运营查看能力,方便后续从“承接记录”平滑过渡到真实站点治理。</p>
@php
$merchantExportQuery = http_build_query(array_filter([
'keyword' => $filters['keyword'] ?? '',
'status' => $filters['status'] ?? '',
'plan' => $filters['plan'] ?? '',
'sort' => $filters['sort'] ?? 'latest',
], fn ($value) => $value !== null && $value !== ''));
@endphp
<div class="mb-12">
<a href="{{ '/site-admin/merchants/export' . ($merchantExportQuery ? ('?' . $merchantExportQuery) : '') }}">导出当前筛选结果 CSV</a>
</div>
<h3>筛选条件</h3>
<form method="get" action="/site-admin/merchants">
<div class="grid-4 form-grid grid-align-end">
<input type="text" name="keyword" placeholder="站点名称 / slug / 联系人 / 手机 / 邮箱" value="{{ $filters['keyword'] ?? '' }}">
<select name="status">
<option value="">全部状态</option>
@foreach(($filterOptions['statuses'] ?? []) as $status)
<option value="{{ $status }}" @selected(($filters['status'] ?? '') === $status)>{{ $statusLabels[$status] ?? $status }}</option>
@endforeach
</select>
<select name="plan">
<option value="">全部套餐</option>
@foreach(($filterOptions['plans'] ?? []) as $plan)
<option value="{{ $plan }}" @selected(($filters['plan'] ?? '') === $plan)>{{ $planLabels[$plan] ?? $plan }}</option>
@endforeach
</select>
<select name="sort">
@foreach(($filterOptions['sortOptions'] ?? []) as $value => $label)
<option value="{{ $value }}" @selected(($filters['sort'] ?? 'latest') === $value)>{{ $label }}</option>
@endforeach
</select>
</div>
<div class="actions mt-12">
<button type="submit">应用筛选</button>
<a href="/site-admin/merchants">重置</a>
</div>
</form>
</div>
<div class="card card-dark card-spaced">
<h3 class="mt-0">当前筛选摘要</h3>
<div class="grid-4">
@foreach(($activeFilterSummary ?? []) as $label => $value)
<div class="summary-box">
<div class="muted">{{ $label }}</div>
<strong class="text-md">{{ $value }}</strong>
</div>
@endforeach
</div>
</div>
<div class="card card-spaced">
<h3 class="mt-0">站点运营摘要</h3>
<div class="grid-4">
<div class="stat-box">
<div class="muted">承接站点数</div>
<strong class="num-md">{{ $summaryStats['site_count'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">启用中站点</div>
<strong class="num-md">{{ $summaryStats['active_site_count'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">站点管理员数</div>
<strong class="num-md">{{ $summaryStats['admin_count'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">站点用户数</div>
<strong class="num-md">{{ $summaryStats['user_count'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">站点商品数</div>
<strong class="num-md">{{ $summaryStats['product_count'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">站点订单数</div>
<strong class="num-md">{{ $summaryStats['order_count'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">商品分类数</div>
<strong class="num-md">{{ $summaryStats['category_count'] ?? 0 }}</strong>
</div>
</div>
</div>
<div class="card card-dark card-spaced">
<h3 class="mt-0">运营关注项</h3>
<div class="grid-3">
<div class="focus-box">
<div class="muted">商品侧</div>
<strong class="text-md">
@if(($summaryStats['product_count'] ?? 0) <= 0)
当前站点暂无商品,建议优先补齐基础商品数据。
@elseif(($summaryStats['product_count'] ?? 0) < 3)
当前站点商品仍较少,建议优先查看最近新增与基础信息是否完整。
@else
当前站点已有较稳定商品沉淀,建议优先巡检低库存与高库存商品结构。
@endif
</strong>
<div class="muted mt-8">建议动作</div>
<div class="inline-links mt-6">
@if(($summaryStats['product_count'] ?? 0) <= 0)
<a href="{{ $latestProductWorkbenchUrl }}">先看商品空白情况</a>
@elseif(($summaryStats['product_count'] ?? 0) < 3)
<a href="{{ $latestProductWorkbenchUrl }}">去看最近新增商品</a>
<a href="{{ $productWorkbenchUrl }}">去看已上架商品</a>
@else
<a href="{{ $lowStockWorkbenchUrl }}">去看低库存商品</a>
<a href="{{ $productWorkbenchUrl }}">去看高库存商品</a>
@endif
</div>
</div>
<div class="focus-box">
<div class="muted">订单侧</div>
<strong class="text-md">
@if(($summaryStats['order_count'] ?? 0) <= 0)
当前站点暂无订单,建议先确认交易链路与下单链路是否已完成联调。
@elseif(($summaryStats['order_count'] ?? 0) < 5)
当前站点已有少量订单沉淀,建议先查看待支付与最近订单走势。
@else
当前站点订单已形成基础规模,建议优先关注待支付、支付失败与高金额订单。
@endif
</strong>
<div class="muted mt-8">建议动作</div>
<div class="inline-links mt-6">
@if(($summaryStats['order_count'] ?? 0) <= 0)
<a href="{{ $orderWorkbenchUrl }}">先看订单整体情况</a>
@elseif(($summaryStats['order_count'] ?? 0) < 5)
<a href="{{ $pendingOrderWorkbenchUrl }}">去看待支付订单</a>
<a href="{{ $orderWorkbenchUrl }}">去看已支付订单</a>
@else
<a href="{{ $pendingOrderWorkbenchUrl }}">去看待支付订单</a>
<a href="{{ $failedOrderWorkbenchUrl }}">去看支付失败订单</a>
@endif
</div>
</div>
<div class="focus-box">
<div class="muted">联系信息</div>
<strong class="text-md">
@if(! $site->contact_name && ! $site->contact_phone && ! $site->contact_email)
联系信息仍为空,建议尽快补齐联系人、手机与邮箱,避免后续运营跟进断点。
@elseif(! $site->contact_name)
当前缺少联系人姓名,建议先补联系人主体,再继续做日常跟进。
@elseif(! $site->contact_phone && ! $site->contact_email)
当前缺少联系方式,建议至少补齐手机号或邮箱中的一项。
@else
联系人信息已具备,可继续作为站点日常跟进入口。
@endif
</strong>
<div class="muted mt-8">建议动作</div>
<div class="inline-links mt-6">
<a href="#site-profile">去看站点资料</a>
@if(! $site->contact_name && ! $site->contact_phone && ! $site->contact_email)
<a href="#site-profile">优先补联系人与联系方式</a>
@elseif(! $site->contact_name)
<a href="#site-profile">优先补联系人</a>
@elseif(! $site->contact_phone && ! $site->contact_email)
<a href="#site-profile">优先补联系方式</a>
@else
<a href="#site-records">去看承接记录</a>
@endif
</div>
</div>
</div>
</div>
<div class="card card-spaced">
<h3 class="mt-0">工作台导航</h3>
<div class="grid-3">
<div class="nav-box">
<div class="muted mb-8">商品工作台</div>
<div class="inline-links">
<a href="{{ $productWorkbenchUrl }}">高库存已上架</a>
<a href="{{ $lowStockWorkbenchUrl }}">低库存补货</a>
<a href="{{ $latestProductWorkbenchUrl }}">最近新增</a>
</div>
</div>
<div class="nav-box">
<div class="muted mb-8">订单工作台</div>
<div class="inline-links">
<a href="{{ $orderWorkbenchUrl }}">高金额已支付</a>
<a href="{{ $pendingOrderWorkbenchUrl }}">待支付跟进</a>
<a href="{{ $failedOrderWorkbenchUrl }}">支付失败排查</a>
</div>
</div>
<div class="nav-box">
<div class="muted mb-8">站点信息</div>
<div class="inline-links">
<a href="#site-profile">查看当前站点资料</a>
<a href="#site-records">查看承接记录</a>
<a href="{{ '/site-admin/merchants' . ($merchantExportQuery ? ('?' . $merchantExportQuery) : '') }}">返回当前筛选视图</a>
</div>
</div>
</div>
</div>
<div class="card card-spaced" id="site-profile">
<h3 class="mt-0">当前站点资料</h3>
<div class="grid-3">
<div class="profile-box">
<div class="muted">站点名称</div>
<strong class="num-sm">{{ $site->name }}</strong>
</div>
<div class="profile-box">
<div class="muted">站点标识</div>
<strong class="num-sm">{{ $site->slug }}</strong>
</div>
<div class="profile-box">
<div class="muted">当前状态</div>
<strong class="num-sm"><span class="badge badge-success">{{ $statusLabels[$site->status] ?? $site->status }}</span></strong>
</div>
<div class="profile-box">
<div class="muted">当前套餐</div>
<strong class="num-sm"><span class="badge badge-info">{{ $planLabels[$site->plan] ?? ($site->plan ?: '未设置') }}</span></strong>
</div>
<div class="profile-box">
<div class="muted">联系人</div>
<strong class="num-sm">{{ $site->contact_name ?: '未设置' }}</strong>
</div>
<div class="profile-box">
<div class="muted">联系电话</div>
<strong class="num-sm">{{ $site->contact_phone ?: '未设置' }}</strong>
</div>
<div class="profile-box">
<div class="muted">联系邮箱</div>
<strong class="num-sm">{{ $site->contact_email ?: '未设置' }}</strong>
</div>
<div class="profile-box">
<div class="muted">激活时间</div>
<strong class="num-sm">{{ $site->activated_at?->format('Y-m-d H:i') ?? '未激活' }}</strong>
</div>
<div class="profile-box span-3">
<div class="muted">站点承接说明</div>
<strong class="text-md">当前阶段由站点后台承接商品、订单与联系人信息查看;后续若拆出“站点 -> 商家”实体关系,这里将平滑升级为真正的站点运营台。</strong>
</div>
</div>
<div class="actions mt-12">
<a href="{{ $productWorkbenchUrl }}">查看站点商品</a>
<a href="{{ $orderWorkbenchUrl }}">查看站点订单</a>
<a href="{{ $lowStockWorkbenchUrl }}">查看低库存商品</a>
<a href="{{ $pendingOrderWorkbenchUrl }}">查看待支付订单</a>
</div>
<div class="card card-dark card-dashed mt-12">
<div class="muted mb-8">运营建议</div>
<div class="inline-links">
<a href="{{ $productWorkbenchUrl }}">高库存已上架商品</a>
<a href="{{ $lowStockWorkbenchUrl }}">低库存补货视角</a>
<a href="{{ $latestProductWorkbenchUrl }}">最近新增商品</a>
<a href="{{ $orderWorkbenchUrl }}">高金额已支付订单</a>
<a href="{{ $pendingOrderWorkbenchUrl }}">待支付订单跟进</a>
<a href="{{ $failedOrderWorkbenchUrl }}">支付失败订单排查</a>
</div>
</div>
<p class="muted helper-text">快捷入口默认带入更适合运营查看的排序与状态条件:商品优先看已上架高库存,订单优先看已支付高金额;同时也补了低库存、待支付、支付失败等常用运营视角。</p>
</div>
<div class="card" id="site-records">
<h3 class="mt-0">当前站点承接记录</h3>
<table>
<thead><tr><th>ID</th><th>名称</th><th>Slug</th><th>状态</th><th>套餐</th><th>联系人</th><th>管理员数</th><th>用户数</th><th>商品数</th><th>订单数</th><th>操作</th></tr></thead>
<tbody>
@forelse($merchants as $merchant)
<tr>
<td>{{ $merchant->id }}</td>
<td>{{ $merchant->name }}</td>
<td>{{ $merchant->slug }}</td>
<td><span class="badge badge-success">{{ $statusLabels[$merchant->status] ?? $merchant->status }}</span></td>
<td><span class="badge badge-info">{{ $planLabels[$merchant->plan] ?? ($merchant->plan ?: '未设置') }}</span></td>
<td>{{ ($merchant->contact_name ?: '未设置') }} / {{ ($merchant->contact_phone ?: '未设置') }}</td>
<td>{{ $merchant->admins_count ?? 0 }}</td>
<td>{{ $merchant->users_count ?? 0 }}</td>
<td>{{ $merchant->products_count ?? 0 }}</td>
<td>{{ $merchant->orders_count ?? 0 }}</td>
<td>
<div class="inline-links">
<a href="{{ $productWorkbenchUrl }}">查看商品</a>
<a href="{{ $orderWorkbenchUrl }}">查看订单</a>
<a href="{{ $lowStockWorkbenchUrl }}">低库存</a>
<a href="{{ $failedOrderWorkbenchUrl }}">支付失败</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="11" class="muted table-empty">暂无站点承接记录</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endsection

View File

@@ -0,0 +1,206 @@
@extends('site_admin.layouts.app')
@section('title', '站点订单')
@section('page_title', '站点订单')
@section('content')
<div class="card card-spaced">
<p class="muted mt-0">当前页面展示站点范围内的订单列表,已补最小筛选与导出入口,便于站点侧先做基础运营查看。</p>
@if(!empty($filters['validation_errors'] ?? []))
<div class="error-box mt-12">
<strong>筛选条件有误:</strong>
<ul class="ml-18">
@foreach(($filters['validation_errors'] ?? []) as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@php
$orderExportQuery = http_build_query(array_filter([
'keyword' => $filters['keyword'] ?? '',
'status' => $filters['status'] ?? '',
'payment_status' => $filters['payment_status'] ?? '',
'platform' => $filters['platform'] ?? '',
'device_type' => $filters['device_type'] ?? '',
'payment_channel' => $filters['payment_channel'] ?? '',
'min_pay_amount' => $filters['min_pay_amount'] ?? '',
'max_pay_amount' => $filters['max_pay_amount'] ?? '',
'sort' => $filters['sort'] ?? 'latest',
], fn ($value) => $value !== null && $value !== ''));
@endphp
<h3>筛选条件</h3>
<form method="get" action="/site-admin/orders">
<div class="grid-5 form-grid">
<input type="text" name="keyword" placeholder="关键词:订单号 / 买家信息" value="{{ $filters['keyword'] ?? '' }}">
<select name="status">
<option value="">全部订单状态</option>
@foreach(($filterOptions['statuses'] ?? []) as $status)
<option value="{{ $status }}" @selected(($filters['status'] ?? '') === $status)>{{ ($statusLabels[$status] ?? $status) }}</option>
@endforeach
</select>
<select name="payment_status">
<option value="">全部支付状态</option>
@foreach(($filterOptions['paymentStatuses'] ?? []) as $status)
<option value="{{ $status }}" @selected(($filters['payment_status'] ?? '') === $status)>{{ ($paymentStatusLabels[$status] ?? $status) }}</option>
@endforeach
</select>
<select name="platform">
<option value="">全部平台</option>
@foreach(($filterOptions['platforms'] ?? []) as $platform)
<option value="{{ $platform }}" @selected(($filters['platform'] ?? '') === $platform)>{{ ($platformLabels[$platform] ?? $platform) }}</option>
@endforeach
</select>
<select name="device_type">
<option value="">全部设备类型</option>
@foreach(['desktop','mobile','mini-program','mobile-webview','app-api'] as $deviceType)
<option value="{{ $deviceType }}" @selected(($filters['device_type'] ?? '') === $deviceType)>{{ $deviceTypeLabels[$deviceType] ?? $deviceType }}</option>
@endforeach
</select>
<select name="payment_channel">
<option value="">全部支付渠道</option>
@foreach(($filterOptions['paymentChannels'] ?? []) as $paymentChannel)
<option value="{{ $paymentChannel }}" @selected(($filters['payment_channel'] ?? '') === $paymentChannel)>{{ ($paymentChannelLabels[$paymentChannel] ?? $paymentChannel) }}</option>
@endforeach
</select>
<select name="sort">
@foreach(($filterOptions['sortOptions'] ?? []) as $value => $label)
<option value="{{ $value }}" @selected(($filters['sort'] ?? 'latest') === $value)>{{ $label }}</option>
@endforeach
</select>
<input type="number" step="0.01" min="0" name="min_pay_amount" placeholder="最低实付金额" value="{{ $filters['min_pay_amount'] ?? '' }}">
<input type="number" step="0.01" min="0" name="max_pay_amount" placeholder="最高实付金额" value="{{ $filters['max_pay_amount'] ?? '' }}">
</div>
<div class="actions mt-12">
<button type="submit">应用筛选</button>
<a href="/site-admin/orders">重置</a>
<a href="{{ '/site-admin/orders/export' . ($orderExportQuery ? ('?' . $orderExportQuery) : '') }}">导出当前筛选结果 CSV</a>
</div>
</form>
</div>
<div class="card card-spaced">
<h3 class="mt-0">当前筛选摘要</h3>
<div class="grid-3">
@foreach(($activeFilterSummary ?? []) as $label => $value)
<div class="summary-box">
<div class="muted">{{ $label }}</div>
<strong class="text-md">{{ $value }}</strong>
</div>
@endforeach
</div>
</div>
<div class="card card-spaced">
<h3 class="mt-0">订单汇总</h3>
<div class="grid-5">
<div class="stat-box">
<div class="muted">订单总数</div>
<strong class="num-md">{{ $summaryStats['total_orders'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">实付总额</div>
<strong class="num-md">¥{{ number_format($summaryStats['total_pay_amount'] ?? 0, 2) }}</strong>
</div>
<div class="stat-box">
<div class="muted">平均客单价</div>
<strong class="num-md">¥{{ number_format($summaryStats['average_order_amount'] ?? 0, 2) }}</strong>
</div>
<div class="stat-box">
<div class="muted">已支付订单数</div>
<strong class="num-md">{{ $summaryStats['paid_orders'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">支付失败订单</div>
<strong class="num-md">{{ $summaryStats['failed_payment_orders'] ?? 0 }}</strong>
</div>
</div>
</div>
<div class="card card-dark card-spaced">
<h3 class="mt-0">运营关注项</h3>
<div class="grid-3">
<div class="focus-box">
<div class="muted">订单盘面</div>
<strong class="text-md">{{ $operationsFocus['headline'] ?? '当前站点订单运营信息已就绪。' }}</strong>
<div class="muted mt-8">建议动作</div>
<div class="inline-links mt-6">
@foreach(($operationsFocus['actions'] ?? []) as $action)
<a href="{{ $action['url'] }}">{{ $action['label'] }}</a>
@endforeach
</div>
</div>
<div class="focus-box">
<div class="muted">当前信号</div>
<div class="grid-3 mt-8">
@foreach(($operationsFocus['signals'] ?? []) as $label => $value)
<div class="summary-box">
<div class="muted">{{ $label }}</div>
<strong class="num-sm">{{ $value }}</strong>
</div>
@endforeach
</div>
</div>
<div class="focus-box">
<div class="muted">工作台导航</div>
<div class="inline-links mt-8">
@foreach(($operationsFocus['workbench'] ?? []) as $label => $url)
<a href="{{ $url }}">{{ $label }}</a>
@endforeach
</div>
</div>
</div>
</div>
<div class="card card-dark card-spaced">
<h3 class="mt-0">订单状态统计</h3>
@php
$orderBaseQuery = [
'payment_status' => ($filters['payment_status'] ?? '') ?: null,
'platform' => ($filters['platform'] ?? '') ?: null,
'device_type' => ($filters['device_type'] ?? '') ?: null,
'payment_channel' => ($filters['payment_channel'] ?? '') ?: null,
'keyword' => ($filters['keyword'] ?? '') ?: null,
'min_pay_amount' => ($filters['min_pay_amount'] ?? '') ?: null,
'max_pay_amount' => ($filters['max_pay_amount'] ?? '') ?: null,
'sort' => ($filters['sort'] ?? 'latest') !== 'latest' ? ($filters['sort'] ?? 'latest') : null,
];
@endphp
<div class="grid-6">
<a class="status-card {{ ($filters['status'] ?? '') === '' ? 'status-card-active' : '' }}" href="{{ '/site-admin/orders?' . http_build_query(array_filter($orderBaseQuery, fn ($value) => $value !== null && $value !== '')) }}">
<div class="muted">全部</div>
<strong class="num-md">{{ $statusStats['all'] ?? 0 }}</strong>
</a>
@foreach(($filterOptions['statuses'] ?? []) as $status)
<a class="status-card {{ ($filters['status'] ?? '') === $status ? 'status-card-active' : '' }}" href="{{ '/site-admin/orders?' . http_build_query(array_filter(array_merge($orderBaseQuery, ['status' => $status]), fn ($value) => $value !== null && $value !== '')) }}">
<div class="muted">{{ ($statusLabels[$status] ?? $status) }}</div>
<strong class="num-md">{{ $statusStats[$status] ?? 0 }}</strong>
</a>
@endforeach
</div>
</div>
<div class="card">
<h3>订单列表</h3>
<table>
<thead><tr><th>ID</th><th>订单号</th><th>状态</th><th>支付状态</th><th>平台</th><th>设备</th><th>支付渠道</th><th>实付金额</th></tr></thead>
<tbody>
@forelse($orders as $order)
<tr>
<td>{{ $order->id }}</td>
<td>{{ $order->order_no }}</td>
<td>{{ $statusLabels[$order->status] ?? $order->status }}</td>
<td>{{ $paymentStatusLabels[$order->payment_status] ?? $order->payment_status }}</td>
<td>{{ $platformLabels[$order->platform] ?? $order->platform }}</td>
<td>{{ $deviceTypeLabels[$order->device_type] ?? $order->device_type }}</td>
<td>{{ $paymentChannelLabels[$order->payment_channel] ?? $order->payment_channel }}</td>
<td>{{ number_format((float) $order->pay_amount, 2) }}</td>
</tr>
@empty
<tr><td colspan="8" class="muted">暂无订单</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-16">{{ $orders->links() }}</div>
@endsection

View File

@@ -0,0 +1,183 @@
@extends('site_admin.layouts.app')
@section('title', '站点商品')
@section('page_title', '站点商品')
@section('content')
<div class="card card-spaced">
<p class="muted mt-0">当前页面展示站点范围内的商品列表,已补最小筛选与导出入口,便于站点侧先做基础运营查看。</p>
@if(!empty($filters['validation_errors'] ?? []))
<div class="error-box mt-12">
<strong>筛选条件有误:</strong>
<ul class="ml-18">
@foreach(($filters['validation_errors'] ?? []) as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
@php
$productExportQuery = http_build_query(array_filter([
'keyword' => $filters['keyword'] ?? '',
'status' => $filters['status'] ?? '',
'category_id' => $filters['category_id'] ?? '',
'min_price' => $filters['min_price'] ?? '',
'max_price' => $filters['max_price'] ?? '',
'min_stock' => $filters['min_stock'] ?? '',
'max_stock' => $filters['max_stock'] ?? '',
'sort' => $filters['sort'] ?? 'latest',
], fn ($value) => $value !== null && $value !== ''));
@endphp
<h3>筛选条件</h3>
<form method="get" action="/site-admin/products">
<div class="grid-4 form-grid">
<input type="text" name="keyword" placeholder="关键词:标题 / SKU / slug" value="{{ $filters['keyword'] ?? '' }}">
<select name="status">
<option value="">全部状态</option>
@foreach(($filterOptions['statuses'] ?? []) as $status)
<option value="{{ $status }}" @selected(($filters['status'] ?? '') === $status)>{{ ($statusLabels[$status] ?? $status) }}</option>
@endforeach
</select>
<select name="category_id">
<option value="">全部分类</option>
@foreach($categories as $category)
<option value="{{ $category->id }}" @selected((string) ($filters['category_id'] ?? '') === (string) $category->id)>{{ $category->name }}</option>
@endforeach
</select>
<select name="sort">
@foreach(($filterOptions['sortOptions'] ?? []) as $value => $label)
<option value="{{ $value }}" @selected(($filters['sort'] ?? 'latest') === $value)>{{ $label }}</option>
@endforeach
</select>
<input type="number" step="0.01" min="0" name="min_price" placeholder="最低价格" value="{{ $filters['min_price'] ?? '' }}">
<input type="number" step="0.01" min="0" name="max_price" placeholder="最高价格" value="{{ $filters['max_price'] ?? '' }}">
<input type="number" min="0" name="min_stock" placeholder="最低库存" value="{{ $filters['min_stock'] ?? '' }}">
<input type="number" min="0" name="max_stock" placeholder="最高库存" value="{{ $filters['max_stock'] ?? '' }}">
</div>
<div class="actions mt-12">
<button type="submit">应用筛选</button>
<a href="/site-admin/products">重置</a>
<a href="{{ '/site-admin/products/export' . ($productExportQuery ? ('?' . $productExportQuery) : '') }}">导出当前筛选结果 CSV</a>
</div>
</form>
</div>
<div class="card card-spaced">
<h3 class="mt-0">当前筛选摘要</h3>
<div class="grid-3">
@foreach(($activeFilterSummary ?? []) as $label => $value)
<div class="summary-box">
<div class="muted">{{ $label }}</div>
<strong class="text-md">{{ $value }}</strong>
</div>
@endforeach
</div>
</div>
<div class="card card-spaced">
<h3 class="mt-0">商品运营汇总</h3>
<div class="grid-4">
<div class="stat-box">
<div class="muted">商品总数</div>
<strong class="num-md">{{ $summaryStats['total_products'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">总库存</div>
<strong class="num-md">{{ $summaryStats['total_stock'] ?? 0 }}</strong>
</div>
<div class="stat-box">
<div class="muted">总货值</div>
<strong class="num-md">¥{{ number_format($summaryStats['total_stock_value'] ?? 0, 2) }}</strong>
</div>
<div class="stat-box">
<div class="muted">平均售价</div>
<strong class="num-md">¥{{ number_format($summaryStats['average_price'] ?? 0, 2) }}</strong>
</div>
</div>
</div>
<div class="card card-dark card-spaced">
<h3 class="mt-0">运营关注项</h3>
<div class="grid-3">
<div class="focus-box">
<div class="muted">商品盘面</div>
<strong class="text-md">{{ $operationsFocus['headline'] ?? '当前站点商品运营信息已就绪。' }}</strong>
<div class="muted mt-8">建议动作</div>
<div class="inline-links mt-6">
@foreach(($operationsFocus['actions'] ?? []) as $action)
<a href="{{ $action['url'] }}">{{ $action['label'] }}</a>
@endforeach
</div>
</div>
<div class="focus-box">
<div class="muted">当前信号</div>
<div class="grid-3 mt-8">
@foreach(($operationsFocus['signals'] ?? []) as $label => $value)
<div class="summary-box">
<div class="muted">{{ $label }}</div>
<strong class="num-sm">{{ $value }}</strong>
</div>
@endforeach
</div>
</div>
<div class="focus-box">
<div class="muted">工作台导航</div>
<div class="inline-links mt-8">
@foreach(($operationsFocus['workbench'] ?? []) as $label => $url)
<a href="{{ $url }}">{{ $label }}</a>
@endforeach
</div>
</div>
</div>
</div>
<div class="card card-dark card-spaced">
<h3 class="mt-0">商品状态统计</h3>
@php
$productBaseQuery = [
'keyword' => ($filters['keyword'] ?? '') ?: null,
'category_id' => ($filters['category_id'] ?? '') ?: null,
'min_price' => ($filters['min_price'] ?? '') ?: null,
'max_price' => ($filters['max_price'] ?? '') ?: null,
'min_stock' => ($filters['min_stock'] ?? '') ?: null,
'max_stock' => ($filters['max_stock'] ?? '') ?: null,
'sort' => ($filters['sort'] ?? 'latest') !== 'latest' ? ($filters['sort'] ?? 'latest') : null,
];
@endphp
<div class="grid-4">
<a class="status-card {{ ($filters['status'] ?? '') === '' ? 'status-card-active' : '' }}" href="{{ '/site-admin/products?' . http_build_query(array_filter($productBaseQuery, fn ($value) => $value !== null && $value !== '')) }}">
<div class="muted">全部</div>
<strong class="num-md">{{ $statusStats['all'] ?? 0 }}</strong>
</a>
@foreach(($filterOptions['statuses'] ?? []) as $status)
<a class="status-card {{ ($filters['status'] ?? '') === $status ? 'status-card-active' : '' }}" href="{{ '/site-admin/products?' . http_build_query(array_filter(array_merge($productBaseQuery, ['status' => $status]), fn ($value) => $value !== null && $value !== '')) }}">
<div class="muted">{{ ($statusLabels[$status] ?? $status) }}</div>
<strong class="num-md">{{ $statusStats[$status] ?? 0 }}</strong>
</a>
@endforeach
</div>
</div>
<div class="card">
<h3>商品列表</h3>
<table>
<thead><tr><th>ID</th><th>标题</th><th>SKU</th><th>分类</th><th>价格</th><th>库存</th><th>状态</th></tr></thead>
<tbody>
@forelse($products as $product)
<tr>
<td>{{ $product->id }}</td>
<td>{{ $product->title }}</td>
<td>{{ $product->sku }}</td>
<td>{{ $product->category?->name ?? '-' }}</td>
<td>{{ number_format((float) $product->price, 2) }}</td>
<td>{{ $product->stock }}</td>
<td>{{ $statusLabels[$product->status] ?? $product->status }}</td>
</tr>
@empty
<tr><td colspan="7" class="muted">暂无商品</td></tr>
@endforelse
</tbody>
</table>
</div>
<div class="mt-16">{{ $products->links() }}</div>
@endsection