remove: 座位模板页面和控制器(被场馆配置覆盖)
- 删除 SeatTemplateList/Save/Delete/Enable 四个方法(194行) - 从 Hook.php AdminSidebarInit 移除座位模板菜单项 - 删除 view/admin/seat_template/ 目录(list.html + save.html) - SeatSkuService/BaseService 等仍保留 seat_map 逻辑,供场馆配置使用feat/b-verification-clean
parent
a5b2d00c8e
commit
cd0e70e9d5
|
|
@ -68,16 +68,7 @@ class Hook
|
||||||
'url' => PluginsAdminUrl('vr_ticket', 'admin', 'VenueList'),
|
'url' => PluginsAdminUrl('vr_ticket', 'admin', 'VenueList'),
|
||||||
'power' => 'vr_ticket-venueList',
|
'power' => 'vr_ticket-venueList',
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'id' => 'plugins-vr_ticket-seat',
|
|
||||||
'name' => '座位模板',
|
|
||||||
'title' => '座位模板',
|
|
||||||
'is_show' => 1,
|
|
||||||
'control' => 'admin',
|
|
||||||
'action' => 'SeatTemplateList',
|
|
||||||
'url' => PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList'),
|
|
||||||
'power' => 'vr_ticket-seatTemplateList',
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'id' => 'plugins-vr_ticket-ticket',
|
'id' => 'plugins-vr_ticket-ticket',
|
||||||
'name' => '电子票',
|
'name' => '电子票',
|
||||||
|
|
|
||||||
|
|
@ -110,200 +110,6 @@ class Admin extends Common
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// 座位模板(SeatTemplate)
|
|
||||||
// 视图: admin/view/seat_template/{action}.html
|
|
||||||
// ============================================================
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 座位模板列表
|
|
||||||
* URL: /plugins/vr_ticket/admin/seatTemplateList
|
|
||||||
* → PluginsService ucfirst('admin')=Admin + ucfirst('seatTemplateList')=SeatTemplateList
|
|
||||||
*/
|
|
||||||
public function SeatTemplateList()
|
|
||||||
{
|
|
||||||
$where = [];
|
|
||||||
|
|
||||||
$name = input('name', '', null);
|
|
||||||
if ($name !== '') {
|
|
||||||
$where[] = ['name', 'like', "%{$name}%"];
|
|
||||||
}
|
|
||||||
|
|
||||||
$status = input('status', '', null);
|
|
||||||
if ($status !== '' && $status !== null) {
|
|
||||||
$where[] = ['status', '=', intval($status)];
|
|
||||||
}
|
|
||||||
|
|
||||||
$list = \think\facade\Db::name('vr_seat_templates')
|
|
||||||
->where($where)
|
|
||||||
->order('id', 'desc')
|
|
||||||
->paginate(20);
|
|
||||||
$list_data = $list->toArray();
|
|
||||||
|
|
||||||
// 关联分类名
|
|
||||||
$category_ids = array_filter(array_column($list_data['data'], 'category_id'));
|
|
||||||
if (!empty($category_ids)) {
|
|
||||||
$categories = \think\facade\Db::name('GoodsCategory')
|
|
||||||
->where('id', 'in', $category_ids)
|
|
||||||
->column('name', 'id');
|
|
||||||
foreach ($list_data['data'] as &$item) {
|
|
||||||
$item['category_name'] = $categories[$item['category_id']] ?? '未知分类';
|
|
||||||
$item['seat_count'] = $this->countSeats($item['seat_map']);
|
|
||||||
}
|
|
||||||
unset($item);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Leading / = ThinkPHP absolute path resolved from app/admin/view/default/
|
|
||||||
// Files are at: app/admin/view/default/plugins/view/vr_ticket/admin/view/seat_template/list.html
|
|
||||||
return MyView('../../../plugins/vr_ticket/view/admin/seat_template/list', [
|
|
||||||
'list' => $list_data['data'],
|
|
||||||
'page' => $list->render() ?: '',
|
|
||||||
'count' => $list_data['total'],
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加/编辑座位模板
|
|
||||||
*/
|
|
||||||
public function SeatTemplateSave()
|
|
||||||
{
|
|
||||||
$id = input('id', 0, 'intval');
|
|
||||||
|
|
||||||
if ((request()->isAjax() && request()->isPost())) {
|
|
||||||
$data = [
|
|
||||||
'name' => input('name', '', null, 'trim'),
|
|
||||||
'category_id' => input('category_id', 0, 'intval'),
|
|
||||||
'seat_map' => input('seat_map', '', null, 'trim'),
|
|
||||||
'spec_base_id_map' => input('spec_base_id_map', '', null, 'trim'),
|
|
||||||
'status' => input('status', 1, 'intval'),
|
|
||||||
'upd_time' => time(),
|
|
||||||
];
|
|
||||||
|
|
||||||
if (empty($data['name'])) {
|
|
||||||
return DataReturn('模板名称不能为空', -1);
|
|
||||||
}
|
|
||||||
if (empty($data['category_id'])) {
|
|
||||||
return DataReturn('请选择绑定的分类', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 验证 seat_map 为合法 JSON
|
|
||||||
$seat_map = json_decode($data['seat_map'], true);
|
|
||||||
if (empty($seat_map) && $data['seat_map'] !== '[]' && $data['seat_map'] !== '{}') {
|
|
||||||
return DataReturn('座位地图JSON格式错误', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($id > 0) {
|
|
||||||
\think\facade\Db::name('vr_seat_templates')->where('id', $id)->update($data);
|
|
||||||
return DataReturn('更新成功', 0);
|
|
||||||
} else {
|
|
||||||
$data['add_time'] = time();
|
|
||||||
$data['upd_time'] = time();
|
|
||||||
\think\facade\Db::name('vr_seat_templates')->insert($data);
|
|
||||||
return DataReturn('添加成功', 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑时加载数据
|
|
||||||
$info = [];
|
|
||||||
if ($id > 0) {
|
|
||||||
$info = \think\facade\Db::name('vr_seat_templates')->find($id);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 加载分类列表(用于下拉选择)
|
|
||||||
$categories = \think\facade\Db::name('GoodsCategory')
|
|
||||||
->where('is_enable', 1)
|
|
||||||
->order('id', 'asc')
|
|
||||||
->select();
|
|
||||||
|
|
||||||
return MyView('../../../plugins/vr_ticket/view/admin/seat_template/save', [
|
|
||||||
'info' => $info,
|
|
||||||
'categories' => $categories,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 删除座位模板(软删除)
|
|
||||||
*/
|
|
||||||
public function SeatTemplateDelete()
|
|
||||||
{
|
|
||||||
if (!(request()->isAjax() && request()->isPost())) {
|
|
||||||
return DataReturn('非法请求', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = input('id', 0, 'intval');
|
|
||||||
if ($id <= 0) {
|
|
||||||
return DataReturn('参数错误', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$hardDelete = input('hard_delete', 0, 'intval');
|
|
||||||
$template = \think\facade\Db::name('vr_seat_templates')->where('id', $id)->find();
|
|
||||||
|
|
||||||
if (empty($template)) {
|
|
||||||
return DataReturn('记录不存在', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($hardDelete) {
|
|
||||||
// 检查是否有关联商品(ShopXO 使用 is_delete_time 做软删除判断)
|
|
||||||
$goods = \think\facade\Db::name('Goods')
|
|
||||||
->where('vr_goods_config', 'like', '%"template_id":' . $id . '%')
|
|
||||||
->where('is_delete_time', 0)
|
|
||||||
->find();
|
|
||||||
\think\facade\Db::name('vr_seat_templates')->where('id', $id)->delete();
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::log(
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::ACTION_DELETE_TEMPLATE,
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
|
|
||||||
$id,
|
|
||||||
['name' => $template['name'], 'has_goods' => !empty($goods)],
|
|
||||||
"模板: {$template['name']}"
|
|
||||||
);
|
|
||||||
return DataReturn('删除成功', 0, ['has_goods' => !empty($goods)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 软删除(禁用)
|
|
||||||
\think\facade\Db::name('vr_seat_templates')
|
|
||||||
->where('id', $id)
|
|
||||||
->update(['status' => 0, 'upd_time' => time()]);
|
|
||||||
|
|
||||||
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::log(
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::ACTION_DISABLE_TEMPLATE,
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
|
|
||||||
$id,
|
|
||||||
['before_status' => $template['status'] ?? 1],
|
|
||||||
$template ? "模板: {$template['name']}" : "ID:{$id}"
|
|
||||||
);
|
|
||||||
|
|
||||||
return DataReturn('已禁用', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function SeatTemplateEnable()
|
|
||||||
{
|
|
||||||
if (!(request()->isAjax() && request()->isPost())) {
|
|
||||||
return DataReturn('非法请求', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = input('id', 0, 'intval');
|
|
||||||
if ($id <= 0) {
|
|
||||||
return DataReturn('参数错误', -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
\think\facade\Db::name('vr_seat_templates')
|
|
||||||
->where('id', $id)
|
|
||||||
->update(['status' => 1, 'upd_time' => time()]);
|
|
||||||
|
|
||||||
\think\facade\Db::name('vr_seat_templates')
|
|
||||||
->where('id', $id)
|
|
||||||
->update(['status' => 1, 'upd_time' => time()]);
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::log(
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::ACTION_ENABLE_TEMPLATE,
|
|
||||||
\app\plugins\vr_ticket\service\AuditService::TARGET_TEMPLATE,
|
|
||||||
$id,
|
|
||||||
['after_status' => 1],
|
|
||||||
"模板ID:{$id}"
|
|
||||||
);
|
|
||||||
return DataReturn('已启用', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// 电子票(Ticket)
|
// 电子票(Ticket)
|
||||||
// 视图: admin/view/ticket/{action}.html
|
// 视图: admin/view/ticket/{action}.html
|
||||||
|
|
|
||||||
|
|
@ -1,107 +0,0 @@
|
||||||
{{:ModuleInclude('public/header')}}
|
|
||||||
|
|
||||||
<div class="right-content">
|
|
||||||
<div class="content-nav">
|
|
||||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'index')}}" class="am-btn am-btn-secondary am-btn-xs">
|
|
||||||
<i class="am-icon-angle-left"></i> 返回
|
|
||||||
</a>
|
|
||||||
<span>座位模板</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 搜索栏 -->
|
|
||||||
<div class="am-scrollable-horizontal">
|
|
||||||
<div class="am-margin-bottom-sm">
|
|
||||||
<form action="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList')}}" method="GET" class="am-form-inline">
|
|
||||||
<div class="am-input-group am-input-group-sm am-fl" style="width: 200px;">
|
|
||||||
<input type="text" name="name" value="{{$name|default=''}}" placeholder="搜索模板名称" class="am-radius" />
|
|
||||||
</div>
|
|
||||||
<div class="am-input-group am-input-group-sm am-fl am-margin-left-sm" style="width: 120px;">
|
|
||||||
<select name="status" class="am-radius">
|
|
||||||
<option value="">全部状态</option>
|
|
||||||
<option value="1" {{if isset($status) && $status==1}}selected{{/if}}>启用</option>
|
|
||||||
<option value="0" {{if isset($status) && $status==0}}selected{{/if}}>禁用</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<button type="submit" class="am-btn am-btn-default am-btn-xs am-radius am-fl am-margin-left-sm">
|
|
||||||
<i class="am-icon-search"></i> 搜索
|
|
||||||
</button>
|
|
||||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateSave')}}" class="am-btn am-btn-primary am-btn-xs am-radius am-fr">
|
|
||||||
<i class="am-icon-plus"></i> 添加模板
|
|
||||||
</a>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 表格 -->
|
|
||||||
<table class="am-table am-table-striped am-table-hover am-text-middle">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>ID</th>
|
|
||||||
<th>模板名称</th>
|
|
||||||
<th>座位数</th>
|
|
||||||
<th>状态</th>
|
|
||||||
<th>添加时间</th>
|
|
||||||
<th>操作</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{{if !empty($list)}}
|
|
||||||
{{foreach $list as $item}}
|
|
||||||
<tr>
|
|
||||||
<td>{{$item.id}}</td>
|
|
||||||
<td>{{$item.name}}</td>
|
|
||||||
<td>{{$item.seat_count|default=0}}</td>
|
|
||||||
<td>
|
|
||||||
{{if $item.status == 1}}
|
|
||||||
<span class="am-badge am-badge-success">启用</span>
|
|
||||||
{{else}}
|
|
||||||
<span class="am-badge am-badge-danger">禁用</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
<td>{{if !empty($item.created_at)}}{{date('Y-m-d H:i', $item.created_at)}}{{/if}}</td>
|
|
||||||
<td>
|
|
||||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateSave', ['id'=>$item.id])}}" class="am-btn am-btn-default am-btn-xs am-radius">
|
|
||||||
<i class="am-icon-edit"></i> 编辑
|
|
||||||
</a>
|
|
||||||
<button type="button" class="am-btn am-btn-danger am-btn-xs am-radius" onclick="toggleTemplate({{$item.id}}, {{$item.status}})">
|
|
||||||
{{if $item.status == 1}}<i class="am-icon-ban"></i> 禁用{{else}}<i class="am-icon-check"></i> 启用{{/if}}
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{/foreach}}
|
|
||||||
{{else}}
|
|
||||||
<tr>
|
|
||||||
<td colspan="6" class="am-text-center">暂无数据</td>
|
|
||||||
</tr>
|
|
||||||
{{/if}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- 分页 -->
|
|
||||||
{{if !empty($page)}}{{$page|raw}}{{/if}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
function toggleTemplate(id, currentStatus) {
|
|
||||||
var action = currentStatus == 1 ? '禁用' : '启用';
|
|
||||||
if (!confirm('确定' + action + '该模板?')) return;
|
|
||||||
var url = currentStatus == 1
|
|
||||||
? '{{:PluginsAdminUrl("vr_ticket", "admin", "SeatTemplateDelete")}}'
|
|
||||||
: '{{:PluginsAdminUrl("vr_ticket", "admin", "SeatTemplateEnable")}}';
|
|
||||||
$.ajax({
|
|
||||||
url: url,
|
|
||||||
type: 'POST',
|
|
||||||
data: {id: id},
|
|
||||||
dataType: 'json',
|
|
||||||
success: function(res) {
|
|
||||||
if (res.code >= 0) {
|
|
||||||
location.reload();
|
|
||||||
} else {
|
|
||||||
alert(res.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{{:ModuleInclude('public/footer')}}
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
{{:ModuleInclude('public/header')}}
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.venue-editor {
|
|
||||||
background: #fff;
|
|
||||||
padding: 30px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 2px 12px rgba(0,0,0,0.05);
|
|
||||||
}
|
|
||||||
.section-title {
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: bold;
|
|
||||||
margin: 30px 0 20px 0;
|
|
||||||
padding-left: 10px;
|
|
||||||
border-left: 4px solid #1677ff;
|
|
||||||
}
|
|
||||||
.seat-preview {
|
|
||||||
background: #fdfdfd;
|
|
||||||
border: 2px dashed #eee;
|
|
||||||
padding: 60px 20px 40px 20px;
|
|
||||||
min-height: 200px;
|
|
||||||
position: relative;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
.seat-row {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.seat-col {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
margin: 2px;
|
|
||||||
border-radius: 4px;
|
|
||||||
background: #eee;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 10px;
|
|
||||||
color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
}
|
|
||||||
.seat-col:hover {
|
|
||||||
transform: scale(1.2);
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
.seat-empty {
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
margin: 2px;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<div class="right-content">
|
|
||||||
<div class="content-nav">
|
|
||||||
<a href="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList')}}" class="am-btn am-btn-secondary am-btn-xs">
|
|
||||||
<i class="am-icon-angle-left"></i> 返回
|
|
||||||
</a>
|
|
||||||
<span>{{if !empty($info)}}编辑模板{{else}}添加座位模板{{/if}}</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="venue-editor">
|
|
||||||
<form class="am-form form-validation" action="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateSave')}}" method="POST" request-type="ajax-url" request-value="{{:PluginsAdminUrl('vr_ticket', 'admin', 'SeatTemplateList')}}">
|
|
||||||
{{if !empty($info)}}
|
|
||||||
<input type="hidden" name="id" value="{{$info.id}}" />
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<div class="am-form-group">
|
|
||||||
<label>模板名称 <span class="am-text-danger">*</span></label>
|
|
||||||
<input type="text" name="name" value="{{if !empty($info)}}{{$info.name}}{{/if}}" placeholder="请输入模板名称" data-validation-message="模板名称不能为空" required class="am-radius" />
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="am-form-group">
|
|
||||||
<label>关联分类 <span class="am-text-danger">*</span></label>
|
|
||||||
<select name="category_id" data-validation-message="请选择关联分类" required>
|
|
||||||
<option value="">请选择分类</option>
|
|
||||||
{{if !empty($categories)}}
|
|
||||||
{{foreach $categories as $c}}
|
|
||||||
<option value="{{$c.id}}" {{if !empty($info.category_id) && $info.category_id==$c.id}}selected{{/if}}>{{$c.name}}</option>
|
|
||||||
{{/foreach}}
|
|
||||||
{{/if}}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="am-form-group">
|
|
||||||
<label>状态</label>
|
|
||||||
<select name="status">
|
|
||||||
<option value="1" {{if !empty($info) && $info.status==1}}selected{{/if}}>启用</option>
|
|
||||||
<option value="0" {{if !empty($info) && $info.status==0}}selected{{/if}}>禁用</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="am-form-group">
|
|
||||||
<label>座位地图 JSON</label>
|
|
||||||
<textarea name="seat_map" rows="8" placeholder='示例: [{"row":"A","seats":[{"char":"1","price":380},{"char":"2","price":380}]}]' class="am-radius">{{if !empty($info)}}{{$info.seat_map}}{{/if}}</textarea>
|
|
||||||
<div class="am-alert am-alert-secondary am-margin-top-xs am-text-xs">
|
|
||||||
请填写符合规范的 JSON 格式座位数据。编辑模式下此字段将更新。
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="am-form-group am-margin-top-lg">
|
|
||||||
<button type="submit" class="am-btn am-btn-primary am-btn-block am-radius" data-am-loading="{spinner: 'circle-o-notch', loadingText: '保存中...'}">
|
|
||||||
{{if !empty($info)}}保存修改{{else}}添加模板{{/if}}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{:ModuleInclude('public/footer')}}
|
|
||||||
Loading…
Reference in New Issue