mirror of https://github.com/4xmen/xshop.git
added menu controller [WIP: sort]
added menu item input vue component added v-model(s) & name support to MorphSelectorpull/44/head
parent
5661e73e11
commit
f1dec8db9f
@ -0,0 +1,147 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Controllers\XController;
|
||||
use App\Http\Requests\MenuSaveRequest;
|
||||
use App\Models\Access;
|
||||
use App\Models\Item;
|
||||
use App\Models\Menu;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Helper;
|
||||
use function App\Helpers\hasCreateRoute;
|
||||
|
||||
class MenuController extends XController
|
||||
{
|
||||
|
||||
// protected $_MODEL_ = Menu::class;
|
||||
// protected $SAVE_REQUEST = MenuSaveRequest::class;
|
||||
|
||||
protected $cols = ['name'];
|
||||
protected $extra_cols = ['id'];
|
||||
|
||||
protected $searchable = ['name'];
|
||||
|
||||
protected $listView = 'admin.menus.menu-list';
|
||||
protected $formView = 'admin.menus.menu-form';
|
||||
|
||||
|
||||
protected $buttons = [
|
||||
'edit' =>
|
||||
['title' => "Edit", 'class' => 'btn-outline-primary', 'icon' => 'ri-edit-2-line'],
|
||||
// 'show' =>
|
||||
// ['title' => "Detail", 'class' => 'btn-outline-light', 'icon' => 'ri-eye-line'],
|
||||
'destroy' =>
|
||||
['title' => "Remove", 'class' => 'btn-outline-danger delete-confirm', 'icon' => 'ri-close-line'],
|
||||
];
|
||||
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct(Menu::class, MenuSaveRequest::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $menu Menu
|
||||
* @param $request MenuSaveRequest
|
||||
* @return Menu
|
||||
*/
|
||||
public function save($menu, $request)
|
||||
{
|
||||
|
||||
$menu->name = $request->input('name');
|
||||
if ($menu->user_id == null){
|
||||
$menu->user_id = auth()->user()->id;
|
||||
}
|
||||
$menu->save();
|
||||
|
||||
$items = json_decode($request->input('items','[]'));
|
||||
foreach ($items as $item) {
|
||||
if ($item->id == null){
|
||||
$i = new Item();
|
||||
}else{
|
||||
$i = Item::whereId($item->id)->first();
|
||||
}
|
||||
$i->user_id = auth()->user()->id;
|
||||
$i->menu_id = $menu->id;
|
||||
$i->meta = $item->meta??null;
|
||||
$i->sort = $item->sort;
|
||||
$i->parent = $item->parent;
|
||||
$i->kind = $item->kind;
|
||||
$i->title = $item->title;
|
||||
$i->menuable_id = $item->menuable_id??null;
|
||||
$i->menuable_type = $item->menuable_type??null;
|
||||
$i->save();
|
||||
}
|
||||
|
||||
Item::whereIn('id',json_decode($request->input('removed','[]')))->delete();
|
||||
|
||||
return $menu;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
//
|
||||
return view($this->formView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Menu $item)
|
||||
{
|
||||
//
|
||||
return view($this->formView, compact('item'));
|
||||
}
|
||||
|
||||
public function bulk(Request $request)
|
||||
{
|
||||
|
||||
// dd($request->all());
|
||||
$data = explode('.', $request->input('action'));
|
||||
$action = $data[0];
|
||||
$ids = $request->input('id');
|
||||
switch ($action) {
|
||||
case 'delete':
|
||||
$msg = __(':COUNT items deleted successfully', ['COUNT' => count($ids)]);
|
||||
$this->_MODEL_::destroy($ids);
|
||||
break;
|
||||
/**restore*/
|
||||
case 'restore':
|
||||
$msg = __(':COUNT items restored successfully', ['COUNT' => count($ids)]);
|
||||
foreach ($ids as $id) {
|
||||
$this->_MODEL_::withTrashed()->find($id)->restore();
|
||||
}
|
||||
break;
|
||||
/*restore**/
|
||||
default:
|
||||
$msg = __('Unknown bulk action : :ACTION', ["ACTION" => $action]);
|
||||
}
|
||||
|
||||
return $this->do_bulk($msg, $action, $ids);
|
||||
}
|
||||
|
||||
public function destroy(Menu $item)
|
||||
{
|
||||
return parent::delete($item);
|
||||
}
|
||||
|
||||
|
||||
public function update(Request $request, Menu $item)
|
||||
{
|
||||
return $this->bringUp($request, $item);
|
||||
}
|
||||
|
||||
/**restore*/
|
||||
public function restore($item)
|
||||
{
|
||||
return parent::restoreing(Menu::withTrashed()->where('id', $item)->first());
|
||||
}
|
||||
/*restore**/
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class MenuSaveRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return auth()->check();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => ['required','string','max:255','min:2'],
|
||||
];
|
||||
}
|
||||
}
|
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="menu-item">
|
||||
|
||||
<div v-for="(item,i) in currentItems" :key="i">
|
||||
<div class="card">
|
||||
|
||||
<div class="card-header">
|
||||
Menu item {{ i + 1 }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row p-0">
|
||||
<div class="col-md-11">
|
||||
<div>
|
||||
<!-- WIP translate-->
|
||||
<label :for="`kind-${i}`">
|
||||
Kind
|
||||
</label>
|
||||
<select v-model="item.kind" class="form-control" :id="`kind-${i}`">
|
||||
<option :value="null"> Kind</option>
|
||||
<option :value="kind" v-for="kind in kinds"> {{ kind }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="item.kind == 'module'">
|
||||
<label :for="`title2-${i}`">
|
||||
Title
|
||||
</label>
|
||||
|
||||
<input type="text" v-model="item.title" placeholder="Title" class="form-control"
|
||||
:id="`title2-${i}`">
|
||||
<morph-selector
|
||||
xname-type=""
|
||||
xname-id=""
|
||||
:morph-search-link="this.morphSearchLink"
|
||||
:morphs="morphs"
|
||||
:xlang="this.xlang"
|
||||
v-model:model-id="item.menuable_id"
|
||||
v-model:model-type="item.menuable_type"
|
||||
>
|
||||
</morph-selector>
|
||||
</div>
|
||||
<div v-else-if="item.kind == 'direct'">
|
||||
<div class="row pt-2">
|
||||
<div class="col-md p-0 pe-2">
|
||||
<label :for="`title1-${i}`">
|
||||
Title
|
||||
</label>
|
||||
<input type="text" v-model="item.title" placeholder="Title" class="form-control"
|
||||
:id="`title1-${i}`">
|
||||
</div>
|
||||
<div class="col-md p-0">
|
||||
<label :for="`meta-${i}`">
|
||||
Link
|
||||
</label>
|
||||
<input type="text" v-model="item.meta" placeholder="Link" class="form-control"
|
||||
:id="`meta-${i}`">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="py-2">
|
||||
<div class="alert bg-danger">
|
||||
Please select kind
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md d-flex justify-content-center align-items-center">
|
||||
<button type="button" class="btn btn-primary" @click="remItem(i)">
|
||||
<i class="ri-close-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary my-3" @click="addItem">
|
||||
<i class="ri-add-line"></i>
|
||||
</button>
|
||||
<input type="hidden" name="items" :value="JSON.stringify(this.currentItems)">
|
||||
<input type="hidden" name="removed" :value="JSON.stringify(this.removed)">
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
||||
import MorphSelector from "./MorphSelector.vue";
|
||||
|
||||
export default {
|
||||
name: "menu-item",
|
||||
components: {MorphSelector},
|
||||
data: () => {
|
||||
return {
|
||||
currentItems: [],
|
||||
removed: [],
|
||||
kinds: [
|
||||
'direct',
|
||||
'module'
|
||||
],
|
||||
}
|
||||
},
|
||||
props: {
|
||||
morphs: {
|
||||
default: [],
|
||||
},
|
||||
xlang: {
|
||||
default: null,
|
||||
},
|
||||
morphSearchLink: {
|
||||
default: null,
|
||||
},
|
||||
items: {
|
||||
default: []
|
||||
},
|
||||
menuId: {
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
let tmp = [];
|
||||
for (const i in this.items) {
|
||||
|
||||
if (typeof (this.items[i].title) != 'undefined') {
|
||||
tmp[i] = {...this.items[i]};
|
||||
tmp[i].title = this.items[i].title[this.xlang];
|
||||
}
|
||||
}
|
||||
|
||||
this.currentItems = tmp;
|
||||
},
|
||||
computed: {},
|
||||
methods: {
|
||||
addItem() {
|
||||
this.currentItems.push({
|
||||
id: null,
|
||||
title: null,
|
||||
menuable_id: null,
|
||||
menuable_type: null,
|
||||
kind: 'direct',
|
||||
sort: this.currentItems.length,
|
||||
parent: null,
|
||||
menu_id: this.menuId,
|
||||
});
|
||||
},
|
||||
remItem(i) {
|
||||
if (this.currentItems[i].id != null) {
|
||||
this.removed.push(this.currentItems[i].id);
|
||||
}
|
||||
this.currentItems.splice(i, 1)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.menu-item {
|
||||
padding-right: 1rem;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
.menu-item .card{
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.menu-item .card-body{
|
||||
background: #ffffff11;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,80 @@
|
||||
@extends('admin.templates.panel-form-template')
|
||||
@section('title')
|
||||
@if(isset($item))
|
||||
{{__("Edit menu")}} [{{$item->name}}]
|
||||
@else
|
||||
{{__("Add new menu")}}
|
||||
@endif -
|
||||
@endsection
|
||||
@section('form')
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-3">
|
||||
|
||||
@include('components.err')
|
||||
<div class="item-list mb-3">
|
||||
<h3 class="p-3">
|
||||
<i class="ri-message-3-line"></i>
|
||||
{{__("Tips")}}
|
||||
</h3>
|
||||
<ul>
|
||||
@if(isset($item))
|
||||
<li>
|
||||
{{__("You can add item after create menu")}}
|
||||
</li>
|
||||
@else
|
||||
<li>
|
||||
{{__("Added items view depends on theme part")}}
|
||||
</li>
|
||||
@endif
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-lg-9 ps-xl-1 ps-xxl-1">
|
||||
<div class="general-form ">
|
||||
|
||||
<h1>
|
||||
@if(isset($item))
|
||||
{{__("Edit menu")}} [{{$item->name}}]
|
||||
@else
|
||||
{{__("Add new menu")}}
|
||||
@endif
|
||||
|
||||
</h1>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 mt-3">
|
||||
<div class="form-group">
|
||||
<label for="name">
|
||||
{{__('Name')}}
|
||||
</label>
|
||||
<input name="name" type="text"
|
||||
id="name"
|
||||
class="form-control @error('name') is-invalid @enderror"
|
||||
placeholder="{{__('Name')}}"
|
||||
value="{{old('name',$item->name??null)}}"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<label> </label>
|
||||
<input name="" type="submit" class="btn btn-primary mt-2" value="{{__('Save')}}"/>
|
||||
</div>
|
||||
</div>
|
||||
@if(isset($item))
|
||||
|
||||
<h4 class="px-4">
|
||||
{{__("Menu items")}}
|
||||
</h4>
|
||||
<menu-item-input
|
||||
:morphs='@json(\App\Models\Menu::$mrohps)'
|
||||
morph-search-link="{{route('v1.morph.search')}}"
|
||||
xlang="{{config('app.locale')}}"
|
||||
:items='@json($item->items)'
|
||||
menu-id="{{$item->id}}"
|
||||
></menu-item-input>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
@ -0,0 +1,15 @@
|
||||
@extends('admin.templates.panel-list-template')
|
||||
|
||||
@section('list-title')
|
||||
<i class="ri-user-3-line"></i>
|
||||
{{__("Menus list")}}
|
||||
@endsection
|
||||
@section('title')
|
||||
{{__("Menus list")}} -
|
||||
@endsection
|
||||
@section('filter')
|
||||
{{-- Other filters --}}
|
||||
@endsection
|
||||
@section('bulk')
|
||||
{{-- <option value="-"> - </option> --}}
|
||||
@endsection
|
@ -1,10 +1,10 @@
|
||||
@yield('custom-foot')
|
||||
<input type="hidden" id="api-display-url" value="{{route('v1.visitor.display')}}">
|
||||
@if(auth()->check() && auth()->user()->hasRole('developer') && !request()->has('ediable'))
|
||||
<a id="do-edit" href="?ediable">
|
||||
<i class="ri-settings-2-line"></i>
|
||||
</a>
|
||||
@endif
|
||||
{{--@if(auth()->check() && auth()->user()->hasRole('developer') && !request()->has('ediable'))--}}
|
||||
{{--<a id="do-edit" href="?ediable">--}}
|
||||
{{-- <i class="ri-settings-2-line"></i>--}}
|
||||
{{--</a>--}}
|
||||
{{--@endif--}}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue