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')
|
@yield('custom-foot')
|
||||||
<input type="hidden" id="api-display-url" value="{{route('v1.visitor.display')}}">
|
<input type="hidden" id="api-display-url" value="{{route('v1.visitor.display')}}">
|
||||||
@if(auth()->check() && auth()->user()->hasRole('developer') && !request()->has('ediable'))
|
{{--@if(auth()->check() && auth()->user()->hasRole('developer') && !request()->has('ediable'))--}}
|
||||||
<a id="do-edit" href="?ediable">
|
{{--<a id="do-edit" href="?ediable">--}}
|
||||||
<i class="ri-settings-2-line"></i>
|
{{-- <i class="ri-settings-2-line"></i>--}}
|
||||||
</a>
|
{{--</a>--}}
|
||||||
@endif
|
{{--@endif--}}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
Loading…
Reference in New Issue