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,74 @@
@extends('admin.layouts.app')
@section('title', $plan->exists ? '编辑套餐' : '新建套餐')
@section('page_title', $plan->exists ? '编辑套餐' : '新建套餐')
@section('content')
<div class="card mb-20">
<p class="muted muted-tight">套餐是平台授权与收费的基础单位,这里维护套餐的基本信息与售卖口径。</p>
<p class="muted">当前阶段先提交套餐主数据,后续再补授权项、配额与订阅联动。</p>
</div>
<form method="post" action="{{ $formAction }}" class="card form-grid">
@csrf
<label>
<span>套餐名称</span>
<input name="name" value="{{ old('name', $plan->name) }}" required>
</label>
<label>
<span>套餐编码</span>
<input name="code" value="{{ old('code', $plan->code) }}" required>
<small class="muted">仅限字母、数字、短横线和下划线,用于接口/内部引用。</small>
</label>
<label>
<span>计费周期</span>
<select name="billing_cycle" required>
@foreach($billingCycleLabels as $value => $label)
<option value="{{ $value }}" @selected(old('billing_cycle', $plan->billing_cycle) === $value)>{{ $label }}</option>
@endforeach
</select>
</label>
<label>
<span>售价</span>
<input type="number" step="0.01" min="0" name="price" value="{{ old('price', $plan->price) }}" required>
</label>
<label>
<span>划线价</span>
<input type="number" step="0.01" min="0" name="list_price" value="{{ old('list_price', $plan->list_price) }}">
</label>
<label>
<span>状态</span>
<select name="status" required>
@foreach($statusLabels as $value => $label)
<option value="{{ $value }}" @selected(old('status', $plan->status) === $value)>{{ $label }}</option>
@endforeach
</select>
</label>
<label>
<span>排序</span>
<input type="number" min="0" name="sort" value="{{ old('sort', $plan->sort ?? 0) }}">
</label>
<label class="full">
<span>发布时间</span>
<input type="datetime-local" name="published_at" value="{{ old('published_at', optional($plan->published_at)->format('Y-m-d\TH:i')) }}">
</label>
<label class="full">
<span>套餐说明</span>
<textarea name="description" rows="4" placeholder="可描述套餐包含的能力与适用场景">{{ old('description', $plan->description) }}</textarea>
</label>
<div class="form-actions">
<a href="/admin/plans" class="btn-secondary">返回</a>
<button type="submit">保存套餐</button>
</div>
</form>
@endsection

View File

@@ -0,0 +1,139 @@
@extends('admin.layouts.app')
@section('title', '套餐管理')
@section('page_title', '套餐管理')
@section('content')
<div class="card mb-20">
<p class="muted muted-tight">这里是总台视角的套餐目录页,用于沉淀平台可售卖的标准能力包。</p>
<p class="muted">当前阶段先完成套餐主数据可见、可筛与口径收拢,后续再接授权项、售价规则与上下架动作。</p>
</div>
<div class="card mb-20">
<h3>工具</h3>
<form method="get" action="/admin/plans/export">
<input type="hidden" name="status" value="{{ $filters['status'] ?? '' }}">
<input type="hidden" name="published" value="{{ $filters['published'] ?? '' }}">
<input type="hidden" name="billing_cycle" value="{{ $filters['billing_cycle'] ?? '' }}">
<input type="hidden" name="keyword" value="{{ $filters['keyword'] ?? '' }}">
<button type="submit">导出当前筛选结果CSV</button>
</form>
</div>
<div class="card mb-20">
<h3>筛选条件</h3>
<form method="get" action="/admin/plans" class="grid-3">
<select name="status">
<option value="">全部状态</option>
@foreach(($filterOptions['statuses'] ?? []) as $value => $label)
<option value="{{ $value }}" @selected(($filters['status'] ?? '') === $value)>{{ $label }}</option>
@endforeach
</select>
<select name="published">
<option value="">全部发布状态</option>
<option value="published" @selected(($filters['published'] ?? '') === 'published')>已发布</option>
<option value="unpublished" @selected(($filters['published'] ?? '') === 'unpublished')>未发布</option>
</select>
<select name="billing_cycle">
<option value="">全部计费周期</option>
@foreach(($filterOptions['billingCycles'] ?? []) as $value => $label)
<option value="{{ $value }}" @selected(($filters['billing_cycle'] ?? '') === $value)>{{ $label }}</option>
@endforeach
</select>
<input name="keyword" placeholder="搜索套餐名称 / 编码 / 描述" value="{{ $filters['keyword'] ?? '' }}">
<div>
<button type="submit">应用筛选</button>
</div>
</form>
</div>
<div class="grid-4 mb-20">
<div class="card">
<h3>套餐总数</h3>
<div class="num-md">{{ $summaryStats['total_plans'] ?? 0 }}</div>
</div>
<div class="card">
<h3>启用中套餐</h3>
<div class="num-md">{{ $summaryStats['active_plans'] ?? 0 }}</div>
</div>
<div class="card">
<h3>月付套餐</h3>
<div class="num-md">{{ $summaryStats['monthly_plans'] ?? 0 }}</div>
</div>
<div class="card">
<h3>年付套餐</h3>
<div class="num-md">{{ $summaryStats['yearly_plans'] ?? 0 }}</div>
</div>
<div class="card">
<h3>已发布</h3>
<div class="num-md">{{ $summaryStats['published_plans'] ?? 0 }}</div>
</div>
<div class="card">
<h3>未发布</h3>
<div class="num-md">{{ $summaryStats['unpublished_plans'] ?? 0 }}</div>
</div>
</div>
<div class="card">
<div class="flex-between">
<div>
<h3>套餐列表</h3>
<p class="muted muted-xs">后续将从这里进入套餐详情、授权项与订阅联动。</p>
</div>
<a href="/admin/plans/create" class="btn">新建套餐</a>
</div>
<table>
<thead>
<tr>
<th>ID</th>
<th>套餐名称</th>
<th>编码</th>
<th>计费周期</th>
<th>售价</th>
<th>划线价</th>
<th>状态</th>
<th>排序</th>
<th>发布时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@forelse($plans as $plan)
<tr>
<td>{{ $plan->id }}</td>
<td>
<strong>{{ $plan->name }}</strong>
<div class="muted muted-xs">{{ $plan->description ?: '暂无说明' }}</div>
</td>
<td>{{ $plan->code }}</td>
<td>{{ $billingCycleLabels[$plan->billing_cycle] ?? $plan->billing_cycle }}</td>
<td>¥{{ number_format((float) $plan->price, 2) }}</td>
<td>¥{{ number_format((float) $plan->list_price, 2) }}</td>
<td>{{ $statusLabels[$plan->status] ?? $plan->status }}</td>
<td>{{ $plan->sort }}</td>
<td>{{ optional($plan->published_at)->format('Y-m-d H:i:s') ?: '-' }}</td>
<td>
<a href="/admin/plans/{{ $plan->id }}/edit" class="link">编辑</a>
<form method="post" action="/admin/plans/{{ $plan->id }}/set-status" style="margin-top:6px;">
@csrf
<select name="status" onchange="this.form.submit()" style="width:140px;">
@foreach(($filterOptions['statuses'] ?? []) as $value => $label)
<option value="{{ $value }}" @selected($plan->status === $value)>{{ $label }}</option>
@endforeach
</select>
<noscript><button type="submit">更新状态</button></noscript>
</form>
</td>
</tr>
@empty
<tr>
<td colspan="10" class="muted">暂无套餐数据,当前阶段先把套餐主表与总台目录立起来,后续可继续接套餐创建、授权项与订阅关联。</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="pagination-wrap">{{ $plans->links() }}</div>
@endsection