改为独立座驾模块
This commit is contained in:
@@ -84,6 +84,10 @@
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.shop.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
{!! '🛒 商店管理' !!}
|
||||
</a>
|
||||
<a href="{{ route('admin.rides.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.rides.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
🚘 座驾管理
|
||||
</a>
|
||||
<a href="{{ route('admin.marriages.index') }}"
|
||||
class="block px-4 py-3 rounded-md transition {{ request()->routeIs('admin.marriages.*') ? 'bg-indigo-600 font-bold' : 'hover:bg-white/10' }}">
|
||||
{!! '💒 婚姻管理' !!}
|
||||
|
||||
@@ -0,0 +1,270 @@
|
||||
{{--
|
||||
文件功能:后台座驾独立管理页面
|
||||
支持查看、新增、编辑、上下架切换、删除座驾,以及配置价格、使用天数和欢迎语。
|
||||
--}}
|
||||
|
||||
@extends('admin.layouts.app')
|
||||
|
||||
@section('title', '🚘 座驾管理')
|
||||
|
||||
@section('content')
|
||||
@php require resource_path('views/admin/partials/list-theme.php'); @endphp
|
||||
|
||||
@php
|
||||
$isSuperAdmin = Auth::id() === 1;
|
||||
@endphp
|
||||
|
||||
<div x-data="{
|
||||
showForm: false,
|
||||
editing: null,
|
||||
form: {
|
||||
name: '',
|
||||
slug: 'ride_',
|
||||
effect_key: '',
|
||||
icon: '🚘',
|
||||
description: '',
|
||||
price: 1000,
|
||||
duration_days: 7,
|
||||
welcome_message: '',
|
||||
sort_order: 80,
|
||||
is_active: true,
|
||||
},
|
||||
|
||||
openCreate() {
|
||||
this.editing = null;
|
||||
this.form = {
|
||||
name: '',
|
||||
slug: 'ride_',
|
||||
effect_key: '',
|
||||
icon: '🚘',
|
||||
description: '',
|
||||
price: 1000,
|
||||
duration_days: 7,
|
||||
welcome_message: '',
|
||||
sort_order: 80,
|
||||
is_active: true,
|
||||
};
|
||||
this.showForm = true;
|
||||
this.$nextTick(() => this.$refs.nameInput?.focus());
|
||||
},
|
||||
|
||||
openEdit(ride) {
|
||||
this.editing = ride;
|
||||
this.form = {
|
||||
name: ride.name,
|
||||
slug: ride.slug,
|
||||
effect_key: ride.effect_key,
|
||||
icon: ride.icon,
|
||||
description: ride.description || '',
|
||||
price: ride.price,
|
||||
duration_days: ride.duration_days || 7,
|
||||
welcome_message: ride.welcome_message || '',
|
||||
sort_order: ride.sort_order,
|
||||
is_active: ride.is_active,
|
||||
};
|
||||
this.showForm = true;
|
||||
this.$nextTick(() => this.$refs.nameInput?.focus());
|
||||
},
|
||||
|
||||
closeForm() {
|
||||
this.showForm = false;
|
||||
this.editing = null;
|
||||
}
|
||||
}">
|
||||
<div class="{{ $adminListHeaderCardClass }} mb-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="{{ $adminListHeaderTitleClass }}">聊天室座驾列表</h2>
|
||||
<p class="{{ $adminListHeaderSubtitleClass }}">单独管理座驾价格、使用天数、入场欢迎语和全屏特效 key。</p>
|
||||
</div>
|
||||
@if ($isSuperAdmin)
|
||||
<button type="button" @click="openCreate()" class="{{ $adminListPrimaryButtonClass }}">
|
||||
+ 新增座驾
|
||||
</button>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="{{ $adminListCardClass }}">
|
||||
<table class="{{ $adminListTableClass }}">
|
||||
<thead class="{{ $adminListTableHeadRowClass }}">
|
||||
<tr>
|
||||
<th class="{{ $adminListTableHeadCellClass }}">座驾</th>
|
||||
<th class="{{ $adminListTableHeadCellClass }}">特效 Key</th>
|
||||
<th class="{{ $adminListTableHeadCellClass }} text-right">价格</th>
|
||||
<th class="{{ $adminListTableHeadCellClass }} text-center">使用天数</th>
|
||||
<th class="{{ $adminListTableHeadCellClass }} text-center">排序</th>
|
||||
<th class="{{ $adminListTableHeadCellClass }} text-center">状态</th>
|
||||
<th class="{{ $adminListTableHeadCellClass }} text-center">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="{{ $adminListTableBodyClass }}">
|
||||
@forelse ($rides as $ride)
|
||||
<tr class="{{ $adminListTableRowClass }} {{ $ride->is_active ? '' : 'opacity-50' }}">
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center gap-3">
|
||||
<span class="text-2xl leading-none">{{ $ride->icon }}</span>
|
||||
<div>
|
||||
<p class="{{ $adminListPrimaryTextClass }}">{{ $ride->name }}</p>
|
||||
<p class="{{ $adminListSecondaryTextClass }} font-mono">{{ $ride->slug }}</p>
|
||||
@if ($ride->description)
|
||||
<p class="mt-0.5 max-w-xs truncate text-xs text-gray-500" title="{{ $ride->description }}">
|
||||
{{ $ride->description }}
|
||||
</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="px-4 py-3 font-mono text-xs text-gray-600">{{ $ride->effect_key }}</td>
|
||||
<td class="px-4 py-3 text-right font-mono font-bold text-amber-600">
|
||||
{{ number_format($ride->price) }} 金
|
||||
</td>
|
||||
<td class="px-4 py-3 text-center text-xs text-gray-500">{{ $ride->duration_days }} 天</td>
|
||||
<td class="px-4 py-3 text-center font-mono text-xs text-gray-400">{{ $ride->sort_order }}</td>
|
||||
<td class="px-4 py-3 text-center">
|
||||
<form method="POST" action="{{ route('admin.rides.toggle', $ride) }}" class="inline">
|
||||
@csrf @method('PATCH')
|
||||
<button type="submit"
|
||||
class="rounded-full px-2.5 py-1 text-xs font-bold transition {{ $ride->is_active ? 'bg-emerald-100 text-emerald-700 hover:bg-emerald-200' : 'bg-gray-100 text-gray-500 hover:bg-gray-200' }}">
|
||||
{{ $ride->is_active ? '上架中' : '已下架' }}
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td class="px-4 py-3">
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<button
|
||||
@click="openEdit({{ json_encode([
|
||||
'id' => $ride->id,
|
||||
'name' => $ride->name,
|
||||
'slug' => $ride->slug,
|
||||
'effect_key' => $ride->effect_key,
|
||||
'icon' => $ride->icon,
|
||||
'description' => $ride->description,
|
||||
'price' => $ride->price,
|
||||
'duration_days' => $ride->duration_days,
|
||||
'welcome_message' => $ride->welcome_message,
|
||||
'sort_order' => $ride->sort_order,
|
||||
'is_active' => (bool) $ride->is_active,
|
||||
], JSON_UNESCAPED_UNICODE) }})"
|
||||
class="{{ $adminListActionButtonClass }} text-indigo-600 hover:bg-indigo-50 hover:text-indigo-800">
|
||||
编辑
|
||||
</button>
|
||||
@if ($isSuperAdmin)
|
||||
<form method="POST" action="{{ route('admin.rides.destroy', $ride) }}"
|
||||
data-admin-confirm="确定要删除「{{ $ride->name }}」吗?此操作不可撤销!">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit"
|
||||
class="{{ $adminListActionButtonClass }} text-red-500 hover:bg-red-50 hover:text-red-700">
|
||||
删除
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="7" class="{{ $adminListEmptyClass }}">暂无座驾数据</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div x-show="showForm" x-cloak class="fixed inset-0 z-50 flex items-center justify-center"
|
||||
style="background: rgba(0,0,0,0.45);">
|
||||
<div @click.stop class="max-h-[90vh] w-full max-w-xl overflow-y-auto rounded-2xl bg-white shadow-2xl"
|
||||
x-transition:enter="transition ease-out duration-200" x-transition:enter-start="translate-y-4 opacity-0"
|
||||
x-transition:enter-end="translate-y-0 opacity-100">
|
||||
<div class="flex items-center justify-between border-b px-6 py-4">
|
||||
<h3 class="text-lg font-bold text-gray-800" x-text="editing ? '编辑座驾:' + editing.name : '新增座驾'"></h3>
|
||||
<button type="button" @click="closeForm()" class="text-2xl leading-none text-gray-400 hover:text-gray-600">×</button>
|
||||
</div>
|
||||
|
||||
<form method="POST"
|
||||
:action="editing ? '{{ url('admin/rides') }}/' + editing.id : '{{ route('admin.rides.store') }}'"
|
||||
class="space-y-4 px-6 py-5">
|
||||
@csrf
|
||||
<template x-if="editing"><input type="hidden" name="_method" value="PUT"></template>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div class="col-span-2">
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">座驾名称 <span class="text-red-500">*</span></label>
|
||||
<input x-ref="nameInput" type="text" name="name" x-model="form.name" required maxlength="100"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">Slug <span class="text-red-500">*</span></label>
|
||||
<input type="text" name="slug" x-model="form.slug" required maxlength="100"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
<p class="mt-1 text-[11px] text-gray-500">格式:ride_j35、ride_df5c。</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">特效 Key <span class="text-red-500">*</span></label>
|
||||
<input type="text" name="effect_key" x-model="form.effect_key" required maxlength="50"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 font-mono text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
<p class="mt-1 text-[11px] text-gray-500">对应 resources/js/effects/<key>.js。</p>
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">图标 <span class="text-red-500">*</span></label>
|
||||
<input type="text" name="icon" x-model="form.icon" required maxlength="20"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">价格(金币)<span class="text-red-500">*</span></label>
|
||||
<input type="number" name="price" x-model="form.price" required min="0"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">座驾描述</label>
|
||||
<textarea name="description" x-model="form.description" rows="2" maxlength="500"
|
||||
class="w-full resize-none rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">使用天数 <span class="text-red-500">*</span></label>
|
||||
<input type="number" name="duration_days" x-model="form.duration_days" required min="1"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">排序权重</label>
|
||||
<input type="number" name="sort_order" x-model="form.sort_order" min="0"
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold text-gray-600">入场欢迎语句</label>
|
||||
<textarea name="welcome_message" x-model="form.welcome_message" rows="2" maxlength="255"
|
||||
placeholder="支持 {name} 用户名、{ride} 座驾名,例如:【{name}】驾驶【{ride}】震撼入场!"
|
||||
class="w-full resize-none rounded-lg border border-gray-300 px-3 py-2 text-sm outline-none focus:border-indigo-400 focus:ring-2 focus:ring-indigo-400"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2">
|
||||
<input type="hidden" name="is_active" :value="form.is_active ? 1 : 0">
|
||||
<label class="relative inline-flex cursor-pointer items-center">
|
||||
<input type="checkbox" x-model="form.is_active" class="peer sr-only">
|
||||
<div class="h-5 w-10 rounded-full bg-gray-200 transition peer-checked:bg-indigo-500 peer-focus:ring-2 peer-focus:ring-indigo-300"></div>
|
||||
<div class="absolute left-0.5 top-0.5 h-4 w-4 rounded-full bg-white shadow transition peer-checked:translate-x-5"></div>
|
||||
</label>
|
||||
<span class="text-sm text-gray-600" x-text="form.is_active ? '上架显示' : '下架隐藏'"></span>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 flex justify-end gap-3 border-t pt-2">
|
||||
<button type="button" @click="closeForm()"
|
||||
class="rounded-lg border border-gray-300 px-5 py-2 text-sm text-gray-600 transition hover:bg-gray-50">
|
||||
取消
|
||||
</button>
|
||||
<button type="submit"
|
||||
class="rounded-lg bg-indigo-600 px-6 py-2 text-sm font-bold text-white shadow transition hover:bg-indigo-700">
|
||||
<span x-text="editing ? '保存修改' : '创建座驾'"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -26,7 +26,6 @@
|
||||
'msg_name_color' => ['label' => '昵称颜色', 'color' => 'bg-pink-100 text-pink-700'],
|
||||
'msg_text_color' => ['label' => '文字颜色', 'color' => 'bg-cyan-100 text-cyan-700'],
|
||||
'avatar_frame' => ['label' => '头像框', 'color' => 'bg-amber-100 text-amber-700'],
|
||||
'ride' => ['label' => '聊天室座驾', 'color' => 'bg-slate-900 text-white'],
|
||||
];
|
||||
$isSuperAdmin = Auth::id() === 1;
|
||||
@endphp
|
||||
@@ -45,7 +44,6 @@
|
||||
duration_minutes: 0,
|
||||
intimacy_bonus: 0,
|
||||
charm_bonus: 0,
|
||||
welcome_message: '',
|
||||
sort_order: 0,
|
||||
is_active: true,
|
||||
},
|
||||
@@ -63,7 +61,6 @@
|
||||
duration_minutes: 0,
|
||||
intimacy_bonus: 0,
|
||||
charm_bonus: 0,
|
||||
welcome_message: '',
|
||||
sort_order: 0,
|
||||
is_active: true,
|
||||
};
|
||||
@@ -84,7 +81,6 @@
|
||||
duration_minutes: item.duration_minutes || 0,
|
||||
intimacy_bonus: item.intimacy_bonus || 0,
|
||||
charm_bonus: item.charm_bonus || 0,
|
||||
welcome_message: item.welcome_message || '',
|
||||
sort_order: item.sort_order,
|
||||
is_active: item.is_active,
|
||||
};
|
||||
@@ -196,7 +192,6 @@
|
||||
'duration_minutes' => $item->duration_minutes,
|
||||
'intimacy_bonus' => $item->intimacy_bonus,
|
||||
'charm_bonus' => $item->charm_bonus,
|
||||
'welcome_message' => $item->welcome_message,
|
||||
'sort_order' => $item->sort_order,
|
||||
'is_active' => (bool) $item->is_active,
|
||||
]) }})"
|
||||
@@ -300,19 +295,10 @@
|
||||
<option value="msg_name_color">msg_name_color — 昵称颜色</option>
|
||||
<option value="msg_text_color">msg_text_color — 文字颜色</option>
|
||||
<option value="avatar_frame">avatar_frame — 头像框</option>
|
||||
<option value="ride">ride — 聊天室座驾</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div x-show="form.type === 'ride'">
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">座驾欢迎语句</label>
|
||||
<textarea name="welcome_message" x-model="form.welcome_message" rows="2" maxlength="255"
|
||||
placeholder="支持 {name} 用户名、{ride} 座驾名,例如:【{name}】驾驶【{ride}】震撼入场!"
|
||||
class="w-full border border-gray-300 rounded-lg px-3 py-2 text-sm focus:ring-2 focus:ring-indigo-400 focus:border-indigo-400 outline-none resize-none"></textarea>
|
||||
<p class="mt-1 text-[11px] text-gray-500">仅座驾类型生效;不填写时使用系统默认欢迎语。</p>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label class="block text-xs font-semibold text-gray-600 mb-1">有效天数</label>
|
||||
|
||||
Reference in New Issue
Block a user