Files
saasshop/resources/views/admin/platform_leads/index.blade.php

238 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('admin.layouts.app')
@section('title', '开通线索')
@section('page_title', '开通线索')
@section('content')
@php
// back 安全护栏(全页通用):
// - 仅允许站内相对路径(/ 开头)
// - 拒绝引号/尖括号(由于本页大量 href 采用 `{!! !!}` 原样输出,必须严控注入风险)
// - 拒绝 nested back=(避免 URL 膨胀/绕过)
$incomingBack = (string) request()->query('back', '');
$safeBackForLinks = \App\Support\BackUrl::sanitizeForLinks($incomingBack);
// back 参数用于“返回上一页(保留上下文)”,但 back 本身不应再包含 back避免无限嵌套导致 URL 膨胀)
$selfWithoutBack = \App\Support\BackUrl::selfWithoutBack();
// 从线索直达“创建平台订单”入口:预填 plan_id/remark并带 back 回到当前线索列表(保留筛选)
// 备注:如果线索 meta 中包含 merchant_id也会一并预填用于“已存在站点但需补单/续费”的场景)
$buildCreatePlatformOrderUrl = function (\App\Models\PlatformLead $l) use ($selfWithoutBack) {
$remarkParts = array_filter([
'线索#' . $l->id,
$l->name,
$l->mobile,
$l->company,
$l->email,
// 后续可用于“从线索追溯来源页/入口”
$l->source ? ('src:' . $l->source) : '',
], fn ($v) => (string) $v !== '');
$q = [
'order_type' => 'new_purchase',
'back' => $selfWithoutBack,
'remark' => mb_substr(implode(' / ', $remarkParts), 0, 220),
// 轻量联动:从线索创建订单后,把线索标记为已转化(后续可再升级为“创建成功后回写”)
'lead_id' => (int) $l->id,
];
if ((int) $l->plan_id > 0) {
$q['plan_id'] = (int) $l->plan_id;
}
$meta = is_array($l->meta) ? $l->meta : [];
$merchantId = (int) ($meta['merchant_id'] ?? 0);
if ($merchantId > 0) {
$q['merchant_id'] = $merchantId;
}
return '/admin/platform-orders/create?' . \Illuminate\Support\Arr::query($q);
};
// 从线索直达“创建续费订单”入口:本质是 require_subscription 治理链路。
// 线索自身通常没有订阅ID因此这里不直接传 site_subscription_id而是引导先去订阅管理选择订阅再返回。
$buildCreateRenewalOrderUrl = function (\App\Models\PlatformLead $l) use ($selfWithoutBack) {
$q = [];
if ((int) $l->plan_id > 0) {
$q['plan_id'] = (int) $l->plan_id;
}
$meta = is_array($l->meta) ? $l->meta : [];
$merchantId = (int) ($meta['merchant_id'] ?? 0);
if ($merchantId > 0) {
$q['merchant_id'] = $merchantId;
}
// require_subscription用于“续费必须先选订阅”的治理 UI 口径
$q['require_subscription'] = '1';
$q['lead_id'] = (int) $l->id;
$q['back'] = $selfWithoutBack;
return '/admin/platform-orders/create?' . \Illuminate\Support\Arr::query($q);
};
@endphp
<div class="page-header mb-20" data-page="admin.platform_leads.index">
<div class="page-header-main">
<div>
<div class="page-header-title">开通线索</div>
<div class="page-header-subtitle">对外平台(/platform收集的开通意向线索用于前期人工运营承接并可联动创建平台订单/续费订单形成收费闭环。</div>
</div>
<div class="page-header-actions">
@if($safeBackForLinks !== '')
<a href="{!! $safeBackForLinks !!}" class="btn btn-secondary btn-sm"> 返回上一页(保留上下文)</a>
@endif
</div>
</div>
<div class="page-header-meta">
<div>后续将逐步接入:一键生成站点/订阅/平台订单、跟进记录、转化漏斗与治理提示。</div>
</div>
</div>
<details class="card mb-20 collapsible filters-card" data-role="collapsible" data-storage-key="admin.platform_leads.filters" open>
<summary class="collapsible-summary filters-summary">
<div class="flex-between items-center">
<div>
<h3 class="mb-0">开通线索</h3>
<div class="muted muted-xs">对外平台(/platform收集的开通意向线索用于前期人工运营承接。</div>
</div>
<span class="muted muted-xs">点击收起/展开</span>
</div>
<div class="muted muted-xs mt-6">线索筛选与状态治理入口(可折叠)。</div>
</summary>
<div class="collapsible-body filters-body">
<h3 class="mt-0">筛选</h3>
<form method="get" action="/admin/platform-leads" class="grid-3 filters-grid">
@if($safeBackForLinks !== '')
<input type="hidden" name="back" value="{!! $safeBackForLinks !!}">
@endif
<select name="status">
<option value="">全部状态</option>
@foreach(($statusLabels ?? []) as $v => $label)
<option value="{{ $v }}" @selected(($filters['status'] ?? '') === $v)>{{ $label }}</option>
@endforeach
</select>
<input name="keyword" placeholder="关键词:姓名/手机号/邮箱/公司" value="{{ $filters['keyword'] ?? '' }}">
<input type="number" name="lead_id" placeholder="线索ID可选" value="{{ $filters['lead_id'] ?? '' }}">
<div>
<button type="submit" class="btn btn-sm">应用筛选</button>
<a class="btn btn-secondary btn-sm" href="/admin/platform-leads">重置</a>
</div>
</form>
</div>
</details>
<div class="card list-card">
<div class="list-card-header">
<div>
<h3 class="list-card-title">线索列表</h3>
<div class="muted muted-xs mt-6">当前阶段仅提供查询与筛选;已补“从线索创建订单”入口,先把收费闭环跑起来。</div>
</div>
</div>
<div class="list-card-body">
<table class="list-card-table table">
<thead>
<tr>
<th>ID</th>
<th>状态</th>
<th>姓名</th>
<th>手机号</th>
<th>邮箱</th>
<th>公司</th>
<th>套餐ID</th>
<th>来源</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@forelse($leads as $l)
<tr>
<td>{{ $l->id }}</td>
<td>
<div class="actions">
<span>{{ $statusLabels[$l->status] ?? $l->status }}</span>
@php
// 轻量运营流转:仅展示“下一步可能动作”,减少噪音/误点。
$nextActions = [];
switch ((string) $l->status) {
case 'new':
$nextActions = ['contacted', 'converted', 'closed'];
break;
case 'contacted':
$nextActions = ['qualified', 'converted', 'closed'];
break;
case 'qualified':
$nextActions = ['converted', 'closed'];
break;
case 'converted':
$nextActions = ['closed'];
break;
default:
$nextActions = [];
}
$actionLabels = [
'contacted' => ['text' => '标记已联系', 'class' => 'btn-secondary btn-sm'],
'qualified' => ['text' => '标记已确认需求', 'class' => 'btn-secondary btn-sm'],
'converted' => ['text' => '标记已转化', 'class' => 'btn btn-sm'],
'closed' => ['text' => '关闭', 'class' => 'btn-danger btn-sm'],
];
@endphp
@foreach($nextActions as $status)
<form method="post" action="/admin/platform-leads/{{ $l->id }}/set-status" class="inline-form">
@csrf
<input type="hidden" name="status" value="{{ $status }}">
<button type="submit" class="{{ $actionLabels[$status]['class'] ?? 'btn-secondary btn-sm' }}">{{ $actionLabels[$status]['text'] ?? $status }}</button>
</form>
@endforeach
</div>
</td>
<td>{{ $l->name }}</td>
<td>{{ $l->mobile }}</td>
<td>{{ $l->email }}</td>
<td>{{ $l->company }}</td>
<td>{{ $l->plan_id ?: '-' }}</td>
<td>{{ $l->source }}</td>
<td>{{ optional($l->created_at)->format('Y-m-d H:i:s') }}</td>
<td>
<div class="actions">
<a class="btn btn-sm" href="{!! $buildCreatePlatformOrderUrl($l) !!}">创建订单</a>
<a class="btn btn-secondary btn-sm" href="{!! $buildCreateRenewalOrderUrl($l) !!}">创建续费订单</a>
@php
$viewOrdersUrl = '/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'lead_id' => $l->id,
'back' => $selfWithoutBack,
]);
$viewRenewalMissingSubOrdersUrl = '/admin/platform-orders?' . \Illuminate\Support\Arr::query([
'lead_id' => $l->id,
'renewal_missing_subscription' => '1',
'back' => $selfWithoutBack,
]);
@endphp
<a class="btn-secondary btn-sm" href="{!! $viewOrdersUrl !!}">查看订单</a>
<a class="btn-secondary btn-sm" href="{!! $viewRenewalMissingSubOrdersUrl !!}">续费缺订阅</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="10" class="muted table-empty">暂无线索</td>
</tr>
@endforelse
</tbody>
</table>
{{ $leads->links('pagination.admin') }}
</div>
</div>
@endsection