186 lines
7.8 KiB
PHP
186 lines
7.8 KiB
PHP
@extends('admin.layouts.app')
|
||
|
||
@section('title', '开通线索')
|
||
@section('page_title', '开通线索')
|
||
|
||
@section('content')
|
||
@php
|
||
// back 安全护栏(全页通用):
|
||
// - 仅允许站内相对路径(/ 开头)
|
||
// - 拒绝引号/尖括号(由于本页大量 href 采用 `{!! !!}` 原样输出,必须严控注入风险)
|
||
// - 拒绝 nested back=(避免 URL 膨胀/绕过)
|
||
$incomingBack = (string) request()->query('back', '');
|
||
$safeBackForLinks = (str_starts_with($incomingBack, '/')
|
||
&& !preg_match('/["\'<>]/', $incomingBack)
|
||
&& !preg_match('/(?:^|[?&])back=/', $incomingBack))
|
||
? $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);
|
||
};
|
||
@endphp
|
||
|
||
<div class="card mb-20">
|
||
<p class="muted muted-tight">对外平台(/platform)收集的开通意向线索,用于前期 A(站点开通型)人工运营承接。</p>
|
||
|
||
@if($safeBackForLinks !== '')
|
||
<div class="mt-10">
|
||
<a href="{!! $safeBackForLinks !!}" class="muted">← 返回上一页(保留上下文)</a>
|
||
</div>
|
||
@endif
|
||
<p class="muted">后续会在此处逐步接入:一键生成站点/订阅/平台订单、跟进记录、转化漏斗与治理提示。</p>
|
||
</div>
|
||
|
||
<div class="card mb-20">
|
||
<h3>筛选</h3>
|
||
<form method="get" action="/admin/platform-leads" class="grid-3">
|
||
@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">应用筛选</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<div class="card">
|
||
<h3>线索列表</h3>
|
||
<div class="muted mb-10">当前阶段仅提供查询与筛选;已补“从线索创建订单”入口,先把收费闭环跑起来。</div>
|
||
|
||
<table class="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>
|
||
|
||
@php
|
||
$viewOrdersUrl = '/admin/platform-orders?' . \Illuminate\Support\Arr::query([
|
||
'lead_id' => $l->id,
|
||
'back' => $selfWithoutBack,
|
||
]);
|
||
@endphp
|
||
<a class="btn-secondary btn-sm" href="{!! $viewOrdersUrl !!}">查看订单</a>
|
||
</div>
|
||
</td>
|
||
</tr>
|
||
@empty
|
||
<tr>
|
||
<td colspan="10" class="muted">暂无线索</td>
|
||
</tr>
|
||
@endforelse
|
||
</tbody>
|
||
</table>
|
||
|
||
<div class="mt-10">
|
||
{{ $leads->links() }}
|
||
</div>
|
||
</div>
|
||
@endsection
|