Software">
Crud Laravel Vue 2.0
Crud Laravel Vue 2.0
Crud Laravel Vue 2.0
proyecto en Laravel:
composer create-project --prefer-dist laravel/laravel senasoft_beta "5.6."*
php artisan serve
Abrir la url :
localhost:8000
Y Verficiar el funcionamiento del aplicativo
Abrir el proyecto con Visual Studio Code
npm -v
Instalar Laravel mix para trabajar con package.json. Se instala en el direct
orio del proyecto de la siguiente manera:
npm install
npm run dev
instalar sweetalert
npm install sweetalert --save-dev
Editar el archivo boostrap
resources\assets\js\bootstrap.js
Agregar el código:
require("sweetalert");
window._ = require('lodash');
window.Popper = require('popper.js').default;
/**
* We'll load jQuery and the Bootstrap jQuery plugin which provides support
* for JavaScript based Bootstrap features such as modals and tabs. This
* code may be modified to fit the specific needs of your application.
*/
try {
window.$ = window.jQuery = require('jquery');
require('bootstrap');
require("sweetalert");
} catch (e) {}
/**
* We'll load the axios HTTP library which allows us to easily issue request
s
* to our Laravel back-end. This library automatically handles sending the
* CSRF token as a header based on the value of the "XSRF" token cookie.
*/
window.axios = require('axios');
window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
/**
* Next we will register the CSRF Token as a common header with Axios so tha
t
* all outgoing HTTP requests automatically have it attached. This is just
* a simple convenience so we don't have to attach every token manually.
*/
let token = document.head.querySelector('meta[name="csrf-token"]');
if (token) {
window.axios.defaults.headers.common['X-CSRF-TOKEN'] = token.content;
} else {
console.error('CSRF token not found: https://laravel.com/docs/csrf#csrf-
x-csrf-token');
}
/**
* Echo exposes an expressive API for subscribing to channels and listening
* for events that are broadcast by Laravel. Echo and event broadcasting
* allows your team to easily build robust real-time web applications.
*/
// import Echo from 'laravel-echo'
// window.Pusher = require('pusher-js');
// window.Echo = new Echo({
// broadcaster: 'pusher',
// key: process.env.MIX_PUSHER_APP_KEY,
// cluster: process.env.MIX_PUSHER_APP_CLUSTER,
// encrypted: true
// });
Ejecutar el comando
npm run dev
El anterior comando compila javascript y css
Debe aparecer luego de compilar:
Crear la base de datos en phpmyadmin:
sensasoft_beta
Cambiar la cadena de conexión de la base de datos
Ubicarse en el archivo .env y cambiar la cadena de conexión: Para el ejemplo
será:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=senasoft_beta
DB_USERNAME=root
DB_PASSWORD=
Luego limpiar la caché de laravel cuando no se apliquen cambios en el navega
dor
php artisan cache:clear
php artisan config:clear
php artisan config:cache
Migraciones:
Crear migraciones de la tabla Categoria:
php artisan make:migration create_categorias_table
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateCategoriasTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('categorias', function (Blueprint $table) {
$table->increments('id');
$table->string('nombre',50);
$table->string('descripcion',256)->nullable;
$table->boolean('estado')->default(1);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('categorias');
}
}
Migrar la tabla:
php artisan migrate
Para crear el modelo de categorias:
php artisan make:model Categoria
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Categoria extends Model
{
protected $fillable = ['nombre','descripcion','estado'];
Luego crear el controlador Categoria:
php artisan make:Controller CategoriaController
El controlador debe quedar con los siguientes métodos:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Categoria;
class CategoriaController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$buscar = $request->buscar;
$criterio = $request->criterio;
if ($buscar==''){
$categorias = Categoria::orderBy('id', 'desc')->paginate(3);
}
else{
$categorias = Categoria::where($criterio, 'like', '%'. $buscar .
'%')->orderBy('id', 'desc')->paginate(3);
}
return [
'pagination' => [
'total' => $categorias->total(),
'current_page' => $categorias->currentPage(),
'per_page' => $categorias->perPage(),
'last_page' => $categorias->lastPage(),
'from' => $categorias->firstItem(),
'to' => $categorias->lastItem(),
],
'categorias' => $categorias
];
}
public function listaCategoria(Request $request){
$categorias = Categoria::where('estado','=','1')
->select('id','nombre')->orderBy('nombre', 'asc')->get();
return ['categorias' => $categorias];
}
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$categoria = new Categoria();
$categoria->nombre = $request->nombre;
$categoria->descripcion = $request->descripcion;
$categoria->estado = '1';
$categoria->save();
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request)
{
$categoria = Categoria::findOrFail($request->id);
$categoria->nombre = $request->nombre;
$categoria->descripcion = $request->descripcion;
$categoria->estado = '1';
$categoria->save();
}
public function desactivar(Request $request)
{
if (!$request->ajax()) return redirect('/'); //Comentar cuando no s
e requieran peticiones ajax
$categoria = Categoria::findOrFail($request->id);
$categoria->estado = '0';
$categoria->save();
}
public function activar(Request $request)
{
$categoria = Categoria::findOrFail($request->id);
$categoria->estado = '1';
$categoria->save();
}
}
<?php
/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/
Route::get('/', function () {
return view('welcome');
});
Route::get('/categoria', 'CategoriaController@index');
Route::post('/categoria/registrar', 'CategoriaController@store');
Route::put('/categoria/actualizar', 'CategoriaController@update');
Route::put('/categoria/activar', 'CategoriaController@activar');
Route::put('/categoria/desactivar', 'CategoriaController@desactivar');
Route::get('/categoria/listaCategoria', 'CategoriaController@selectCategoria');
Testear en la url
localhost:8000/categoria
El archivo debe contener el siguiente código. El código permite realizar todo el CRUD a través de
VUE. Todo componente en vue se ubica dentro las etiquetas <template></template>
<template>
<main class="main">
<div class="container-fluid">
<!-- Listado -->
<div class="card">
<div class="card-header">
<i class="fa fa-align-justify"></i> Categorías
<button type="button" @click="abrirModal('categoria'
,'registrar')" class="btn btn-secondary">
<i class="icon-plus"></i> Nuevo
</button>
</div>
<div class="card-body">
<div class="form-group row">
<div class="col-md-6">
<div class="input-group">
<select class="form-control col-md-3" v-
model="criterio">
<option value="nombre">Nombre</option>
<option value="descripcion">Descripció
n</option>
</select>
<input type="text" v-model="buscar"
@keyup.enter="listarCategoria(1,buscar,criterio)" class="form-control" place
holder="Texto a buscar">
<button type="submit" @click="listarCate
goria(1,buscar,criterio)" class="btn btn-primary"><i class="fa fa-
search"></i> Buscar</button>
</div>
</div>
</div>
<table class="table table-bordered table-striped tab
le-sm" id="table">
<thead>
<tr>
<th>Nombre</th>
<th>Descripción</th>
<th>Estado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<tr v-for="categoria in arrayCategoria"
:key="categoria.id">
<td v-text="categoria.nombre"></td>
<td v-text="categoria.descripcion"></td>
<td>
<div v-if="categoria.estado">
<button class="badge badge-
success">Activo</button>
</div>
<div v-else>
<button class="badge badge-
success">Inactivo</button>
</div>
</td>
<td>
<button type="button" @click="abrirM
odal('categoria','actualizar',categoria)" class="btn btn-warning btn-sm">
<i class="icon-pencil">Editar</i>
</button>
<template v-if="categoria.estado">
<button type="button" class="btn
btn-danger" @click="desactivarCategoria(categoria.id)">
<i class="icon-
trash">Inactivar</i>
</button>
</template>
<template v-else>
<button type="button" class="btn
btn-info" @click="activarCategoria(categoria.id)">
<i class="icon-
check">Activar</i>
</button>
</template>
</td>
</tr>
</tbody>
</table>
<nav>
<ul class="pagination">
<li class="page-item" v-
if="pagination.current_page > 1">
<a class="page-link" href="#"
@click.prevent="cambiarPagina(pagination.current_page - 1,buscar,criterio)">
Ant</a>
</li>
<li class="page-item" v-for="page in pagesNu
mber" :key="page" :class="[page == isActived ? 'active' : '']">
<a class="page-link" href="#"
@click.prevent="cambiarPagina(page,buscar,criterio)" v-text="page"></a>
</li>
<li class="page-item" v-
if="pagination.current_page < pagination.last_page">
<a class="page-link" href="#"
@click.prevent="cambiarPagina(pagination.current_page + 1,buscar,criterio)">
Sig</a>
</li>
</ul>
</nav>
</div>
</div>
<!-- Fin Listado Tabla -->
</div>
<!--Inicio del modal agregar/actualizar-->
<div class="modal fade" tabindex="-1" :class="{'mostrar' : moda
l}" role="dialog" aria-labelledby="myModalLabel" style="display: none;" aria
-hidden="true">
<div class="modal-dialog modal-primary modal-lg" rol
e="document">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" v-
text="tituloModal"></h4>
<button type="button" class="close" @click="cerr
arModal()" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<form action="" method="post" enctype="multipart
/form-data" class="form-horizontal">
<div class="form-group row">
<label class="col-md-3 form-control-
label" for="text-input">Nombre</label>
<div class="col-md-9">
<input type="text" v-model="nombre"
class="form-control" placeholder="Nombre de categoría">
</div>
</div>
<div class="form-group row">
<label class="col-md-3 form-control-
label" for="email-input">Descripción</label>
<div class="col-md-9">
<input type="email" v-
model="descripcion" class="form-control" placeholder="Ingrese descripción">
</div>
</div>
<div v-show="errorCategoria" class="form-
group row div-error">
<div class="text-center text-error">
<div v-for="error in errorMostrarMsj
Categoria" :key="error" v-text="error">
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary"
@click="cerrarModal()">Cerrar</button>
<button type="button" v-if="tipoAccion==1" clas
s="btn btn-primary" @click="registrarCategoria()">Guardar</button>
<button type="button" v-if="tipoAccion==2" clas
s="btn btn-primary" @click="actualizarCategoria()">Actualizar</button>
</div>
</div>
<!-- /.modal-content -->
</div>
<!-- /.modal-dialog -->
</div>
<!--Fin del modal-->
</main>
</template>
<script>
export default {
data (){
return {
//Variables a usar de javascript
categoria_id: 0,
nombre : '',
descripcion : '',
arrayCategoria : [], //array para almacenar las categorias
modal : 0,
tituloModal : '',
tipoAccion : 0,
errorCategoria : 0,
errorMostrarMsjCategoria : [],
pagination : {
'total' : 0,
'current_page' : 0,
'per_page' : 0,
'last_page' : 0,
'from' : 0,
'to' : 0,
},
offset : 3,
criterio : 'nombre',
buscar : ''
}
},
computed:{
isActived: function(){
return this.pagination.current_page; //Retornar la página ac
tiva
},
//Calcular los elementos de la paginación
pagesNumber: function() {
if(!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if(from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if(to >= this.pagination.last_page){
to = this.pagination.last_page;
}
var pagesArray = [];
while(from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
}
},
methods : {
//Mètodos involucrados en el CRUD
listarCategoria (page,buscar,criterio){ //Listar las categorías.
let me=this;
var url= '/categoria?page=' + page + '&buscar='+ buscar + '&
criterio='+ criterio;
axios.get(url).then(function (response) {
var respuesta= response.data;
me.arrayCategoria = respuesta.categorias.data;
me.pagination= respuesta.pagination;
})
.catch(function (error) {
console.log(error);
});
},
cambiarPagina(page,buscar,criterio){ //Método para cambiar de pá
gina en la paginación
let me = this;
//Actualiza la página actual
me.pagination.current_page = page;
//Envia la petición para visualizar la data de esa página
me.listarCategoria(page,buscar,criterio);
},
registrarCategoria(){ //Método para Registrar la Categoría
if (this.validarCategoria()){
return;
}
let me = this;
axios.post('/categoria/registrar',{
'nombre': this.nombre,
'descripcion': this.descripcion
}).then(function (response) {
me.cerrarModal();
me.listarCategoria(1,'','nombre');
}).catch(function (error) {
console.log(error);
});
},
actualizarCategoria(){ //Método para actualizar la categoría
if (this.validarCategoria()){
return;
}
let me = this;
axios.put('/categoria/actualizar',{
'nombre': this.nombre,
'descripcion': this.descripcion,
'id': this.categoria_id
}).then(function (response) {
me.cerrarModal();
me.listarCategoria(1,'','nombre');
}).catch(function (error) {
console.log(error);
});
},
desactivarCategoria(id){ //Método para desactivar la categoría
let me = this;
axios.put('/categoria/desactivar',{
'id': id
}).then(function (response) {
me.listarCategoria(1,'','nombre');
swal(
'Desactivado!',
'El registro ha sido desactivado con éxito.',
'success'
)
console.log(response);
}).catch(function (error) {
console.log(error);
});
},
activarCategoria(id){ //Método para activar la categoría
let me = this;
axios.put('/categoria/activar',{
'id': id
}).then(function (response) {
console.log(response);
me.listarCategoria(1,'','nombre');
swal(
'Activado!',
'El registro ha sido activado con éxito.',
'success'
)
}).catch(function (error) {
console.log(error);
});
},
validarCategoria(){ //Método para Validar la Categoría
this.errorCategoria=0;
this.errorMostrarMsjCategoria =[];
if (!this.nombre) this.errorMostrarMsjCategoria.push("El nom
bre de la categoría no puede estar vacío.");
if (this.errorMostrarMsjCategoria.length) this.errorCategori
a = 1;
return this.errorCategoria;
},
cerrarModal(){ //Método para cerrar la modal
this.modal=0;
this.tituloModal='';
this.nombre='';
this.descripcion='';
},
abrirModal(modelo, accion, data = []){ //Método para abrir la mo
dal
switch(modelo){
case "categoria":
{
switch(accion){
case 'registrar':
{
this.modal = 1;
this.tituloModal = 'Registrar Categoría';
this.nombre= '';
this.descripcion = '';
this.tipoAccion = 1;
break;
}
case 'actualizar':
{
this.modal=1;
this.tituloModal='Actualizar categoría';
this.tipoAccion=2;
this.categoria_id=data['id'];
this.nombre = data['nombre'];
this.descripcion= data['descripcion'];
break;
}
}
}
}
}
},
mounted() { //Método que se cargaal acceder al componente.
this.listarCategoria(1,this.buscar,this.criterio);
}
}
</script>
<style>
.modal-content{
width: 100% !important;
position: absolute !important;
}
.mostrar{
display: list-item !important;
opacity: 1 !important;
position: absolute !important;
background-color: #3c29297a !important;
}
.text-error{
color: red !important;
font-weight: bold;
}
.div-error{
display: flex;
justify-content: center;
}
</style>
Luego de crear un componente vue, para incluir los cambios en la aplicación ejecutar el
comando npm run dev para compilar el componente.
/**
* First we will load all of this project's JavaScript dependencies which
* includes Vue and other libraries. It is a great starting point when
* building robust, powerful web applications using Vue and Laravel.
*/
require('./bootstrap');
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
//Se deben ubicar los componentes
//Vue.component('example-component', require('./components/ExampleComponent.
vue'));
Vue.component('categoria-component', requir
e('./components/CategoriaComponent.vue'));
const app = new Vue({
el: '#app', // Este es el id de la línea 12 ubicado en principal.blade.p
hp
data :{
menu :0 //Se indica la opción de menú 0
}
});
Ejecutar el comando npm run dev para incluir los cambios efectuados en app.js
<div class="sidebar">
<nav class="sidebar-nav">
<ul class="nav">
<li class="nav-item nav-dropdown">
<a class="nav-link nav-dropdown-toggle" href="#"><i clas
s="icon-bag"></i> Almacén</a>
<ul class="nav-dropdown-items">
<li @click="menu=1" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Categorías</a>
</li>
<li @click="menu=2" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Productos</a>
</li>
<li @click="menu=3" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Ventas</a>
</li>
</ul>
</li>
</ul>
</nav>
</div>
<template v-if="menu==1">
<!-- v-if es el if en vue -->
<!--redireccionar al componente categoria-component -->
<categoria-component></categoria-component>
</template>
<template v-if="menu==2">
<h1>Contenido del menú 2</h1>
</template>
<template v-if="menu==3">
<!--redireccionar al componente venta-component -->
<venta-component></venta-component>
</template>
@endsection
<!doctype html>
<html lang="{{ app()->getLocale() }}">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Prueba</title>
<link href="{{asset('css/app.css')}}" rel="stylesheet"> <!--Añadimos
el css-->
<meta name="csrf-token" content="{{ csrf_token() }}">
</head>
<body>
<div id="app" class="content"><!--La equita id debe ser app, com
o hemos visto en app.js-->
@include('menu')
@yield('contenido')
</div>
<script src="{{asset('js/app.js')}}"></script> <!--Añadimos el js, d
onde se encuentra el componente vuejs-->
</body>
</html>
Route::get('/', function () {
return view('welcome');
});
Por:
Route::get('/', function () {
return view('contenido');
});
Cliente
Producto
Crear la migracion de productos
php artisan make:migration create_productos_table
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductosTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up() //Crear la migración de productos
{
Schema::create('productos', function (Blueprint $table) {
$table->increments('id');
$table->integer('idcategoria')->unsigned();
$table->string('codigo', 30)->nullable();
$table->string('nombre', 60)->unique();
$table->string('descripcion', 256)->nullable();
$table->decimal('precio', 12, 2);
$table->integer('stock');
$table->boolean('estado')->default(1);
$table->timestamps();
$table->foreign('idcategoria')->references('id')-
>on('categorias');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('productos');
}
}
php artisan migrate
Crear el modelo de productos:
php artisan make:model Producto
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Producto extends Model
{
protected $fillable =[
'idcategoria','codigo','nombre', 'descripcion','precio','stock','est
ado'
];
public function categoria(){
return $this->belongsTo('App\Categoria');
}
}
Crear el controlador:
php artisan make:Controller ProductoController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Producto;
class ProductoController extends Controller
{
public function index(Request $request)
{
$buscar = $request->buscar;
$criterio = $request->criterio;
if ($buscar==''){
$productos = Producto::join('categorias','productos.idcategoria'
,'=','categorias.id')
-
>select('productos.id','productos.idcategoria','productos.codigo','productos
.nombre','productos.descripcion','categorias.nombre as nombre_categoria','pr
oductos.precio','productos.stock','productos.estado')
->orderBy('productos.id', 'desc')->paginate(3);
}
else{
$productos = Producto::join('categorias','productos.idcategoria'
,'=','categorias.id')
-
>select('productos.id','productos.idcategoria','productos.codigo','productos
.nombre','categorias.nombre as nombre_categoria','productos.descripcion','pr
oductos.precio','productos.stock','productos.estado')
->where('productos.'.$criterio, 'like', '%'. $buscar . '%')
->orderBy('productos.id', 'desc')->paginate(3);
}
return [
'pagination' => [
'total' => $productos->total(),
'current_page' => $productos->currentPage(),
'per_page' => $productos->perPage(),
'last_page' => $productos->lastPage(),
'from' => $productos->firstItem(),
'to' => $productos->lastItem(),
],
'productos' => $productos
];
}
public function store(Request $request)
{
$producto = new Producto();
$producto->idcategoria = $request->idcategoria;
$producto->codigo = $request->codigo;
$producto->nombre = $request->nombre;
$producto->descripcion = $request->descripcion;
$producto->precio = $request->precio;
$producto->stock = $request->stock;
$producto->estado = '1';
$producto->save();
}
public function update(Request $request)
{
$producto = Producto::findOrFail($request->id);
$producto->idcategoria = $request->idcategoria;
$producto->codigo = $request->codigo;
$producto->nombre = $request->nombre;
$producto->descripcion = $request->descripcion;
$producto->precio = $request->precio;
$producto->stock = $request->stock;
$producto->estado = '1';
$producto->save();
}
public function activar(Request $request)
{
$producto = Producto::findOrFail($request->id);
$producto->estado = '1';
$producto->save();
}
public function desactivar(Request $request)
{
$producto = Producto::findOrFail($request->id);
$producto->estado = '0';
$producto->save();
}
public function listarProductoVenta(Request $request)
{
$buscar = $request->buscar;
$criterio = $request->criterio;
if ($buscar==''){
$productos = Producto::join('categorias','productos.idcategoria'
,'=','categorias.id')
-
>select('productos.id','productos.idcategoria','productos.codigo','productos
.nombre','categorias.nombre as nombre_categoria','productos.precio','product
os.stock','productos.descripcion','productos.estado')
->where('productos.stock','>','0')
->orderBy('productos.id', 'desc')->paginate(10);
}
else{
$productos = Producto::join('categorias','productos.idcategoria'
,'=','categorias.id')
-
>select('productos.id','productos.idcategoria','productos.codigo','productos
.nombre','categorias.nombre as nombre_categoria','productos.precio','product
os.stock','productos.descripcion','productos.estado')
->where('productos.'.$criterio, 'like', '%'. $buscar . '%')
->where('productos.stock','>','0')
->orderBy('productos.id', 'desc')->paginate(10);
}
return ['productos' => $productos];
}
public function buscarProductoVenta(Request $request){
$filtro = $request->filtro;
$productos = Producto::where('codigo','=', $filtro)
->select('id','nombre','stock','precio')
->where('stock','>','0')
->orderBy('nombre', 'asc')
->take(1)->get();
return ['productos' => $productos];
}
Agregar las siguientes rutas a web.php
Route::get('/producto', 'ProductoController@index');
Route::post('/producto/registrar', 'ProductoController@store');
Route::put('/producto/actualizar', 'ProductoController@update');
Route::put('/producto/activar', 'ProductoController@activar');
Route::put('/producto/desactivar', 'ProductoController@desactivar');
Route::get('/producto/buscarProducto', 'ProductoController@buscarProducto');
Route::get('/producto/listarProducto', 'ProductoController@listarProducto');
Route::get('/producto/buscarProductoVenta', 'ProductoController@buscarProduc
toVenta');
Route::get('/producto/listarProductoVenta', 'ProductoController@listarProduc
toVenta');
////////////////////////////////////////FIN PRODUCTOS////////////////////////////////////////
////////////////////////////////////////INICIO CLIENTES////////////////////////////////////////
Crear la migracion de clientes
php artisan make:migration create_clientes_table
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateClientesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('clientes', function (Blueprint $table) {
$table->increments('id');
$table->string('nombre', 100)->unique();
$table->string('tipo_documento', 20)->nullable();
$table->string('numero_documento', 20)->nullable();
$table->string('direccion', 70)->nullable();
$table->string('telefono', 20)->nullable();
$table->string('email', 50)->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('clientes');
}
}
Realizar la migración de cliente
php artisan migrate
Crear el modelo de clientes:
php artisan make:model Cliente
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Cliente extends Model
{
protected $fillable = ['nombre','tipo_documento','numero_documento','dir
eccion','telefono','email'];
public function user()
{
return $this->hasOne('App\User');
}
}
Crear el controlador:
php artisan make:Controller ClienteController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Cliente;
class ClienteController extends Controller
{
public function index(Request $request)
{
$buscar = $request->buscar;
$criterio = $request->criterio;
if ($buscar==''){
$clientes = Cliente::orderBy('id', 'desc')->paginate(3);
}
else{
$clientes = Cliente::where($criterio, 'like', '%'. $buscar . '%'
)->orderBy('id', 'desc')->paginate(3);
}
return [
'pagination' => [
'total' => $clientes->total(),
'current_page' => $clientes->currentPage(),
'per_page' => $clientes->perPage(),
'last_page' => $clientes->lastPage(),
'from' => $clientes->firstItem(),
'to' => $clientes->lastItem(),
],
'clientes' => $clientes
];
}
public function listaCliente(Request $request){
$filtro = $request->filtro;
$clientes = Cliente::where('nombre', 'like', '%'. $filtro . '%')
->orWhere('numero_documento', 'like', '%'. $filtro . '%')
->select('id','nombre','numero_documento')
->orderBy('nombre', 'asc')->get();
return ['clientes' => $clientes];
}
public function store(Request $request)
{
$cliente = new Cliente();
$cliente->nombre = $request->nombre;
$cliente->tipo_documento = $request->tipo_documento;
$cliente->numero_documento = $request->numero_documento;
$cliente->direccion = $request->direccion;
$cliente->telefono = $request->telefono;
$cliente->email = $request->email;
$cliente->save();
}
public function update(Request $request)
{
$cliente = Cliente::findOrFail($request->id);
$cliente->nombre = $request->nombre;
$cliente->tipo_documento = $request->tipo_documento;
$cliente->numero_documento = $request->numero_documento;
$cliente->direccion = $request->direccion;
$cliente->telefono = $request->telefono;
$cliente->email = $request->email;
$cliente->save();
}
}
Agregar las siguientes rutas a web.php
Route::get('/cliente', 'ClienteController@index');
Route::post('/cliente/registrar', 'ClienteController@store');
Route::put('/cliente/actualizar', 'ClienteController@update');
Route::get('/cliente/listaCliente', 'ClienteController@listaCliente');
////////////////////////////////////////INICIO VENTA-DETALLE////////////////////////////
CREAR LA MIGRACIÓN DE LA TABLA ventas
php artisan make:migration create_ventas_table
CREAR LA MIGRACIÓN DE LA TABLA detalle_ventas
php artisan make:migration create_detalle_ventas_table
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateVentasTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('ventas', function (Blueprint $table) {
$table->increments('id');
$table->integer('idcliente')->unsigned();
$table->foreign('idcliente')->references('id')->on('clientes');
$table->integer('idusuario')->unsigned();
$table->foreign('idusuario')->references('id')->on('users');
$table->dateTime('fecha_hora');
$table->decimal('total', 12, 2);
$table->string('estado', 20);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('ventas');
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateDetalleVentasTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('detalle_ventas', function (Blueprint $table) {
$table->increments('id');
$table->integer('idventa')->unsigned();
$table->foreign('idventa')->references('id')->on('ventas')-
>onDelete('cascade');
$table->integer('idproducto')->unsigned();
$table->foreign('idproducto')->references('id')-
>on('productos');
$table->integer('cantidad');
$table->decimal('precio', 12, 2);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('detalle_ventas');
}
}
php artisan migrate
Para crear el modelo de ventas:
php artisan make:model Venta
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Venta extends Model
{
protected $fillable =[
'idcliente',
'idusuario',
'fecha_hora',
'total',
'estado'
];
}
Luego crear el controlador:
php artisan make:Controller VentaController
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;
use App\Venta;
use App\DetalleVenta;
class VentaController extends Controller
{
public function index(Request $request)
{
$buscar = $request->buscar;
$criterio = $request->criterio;
if ($buscar==''){
$ventas = Venta::join('clientes','ventas.idcliente','=','cliente
s.id')
->join('users','ventas.idusuario','=','users.id')
->select('ventas.id','ventas.fecha_hora','ventas.total',
'ventas.estado','clientes.nombre','users.name')
->orderBy('ventas.id', 'desc')->paginate(3);
}
else{
$ventas = Venta::join('clientes','ventas.idcliente','=','cliente
s.id')
->join('users','ventas.idusuario','=','users.id')
->select('ventas.id','ventas.fecha_hora','ventas.total',
'ventas.estado','clientes.nombre','users.name')
->where('ventas.'.$criterio, 'like', '%'. $buscar . '%')
->orderBy('ventas.id', 'desc')->paginate(3);
}
return [
'pagination' => [
'total' => $ventas->total(),
'current_page' => $ventas->currentPage(),
'per_page' => $ventas->perPage(),
'last_page' => $ventas->lastPage(),
'from' => $ventas->firstItem(),
'to' => $ventas->lastItem(),
],
'ventas' => $ventas
];
}
public function desactivar(Request $request)
{
$venta = Venta::findOrFail($request->id);
$venta->estado = 'Anulado';
$venta->save();
}
public function obtenerEncabezado(Request $request){
$id = $request->id;
$venta = Venta::join('clientes','ventas.idcliente','=','clientes.id'
)
->join('users','ventas.idusuario','=','users.id')
->select('ventas.id','ventas.fecha_hora','ventas.total',
'ventas.estado','clientes.nombre','users.name')
->where('ventas.id','=',$id)
->orderBy('ventas.id', 'desc')->take(1)->get();
return ['venta' => $venta];
}
public function obtenerDetalles(Request $request){
$id = $request->id;
$detalles = DetalleVenta::join('productos','detalle_ventas.idproduct
o','=','productos.id')
->select('detalle_ventas.cantidad','detalle_ventas.precio',
'productos.nombre as producto')
->where('detalle_ventas.idventa','=',$id)
->orderBy('detalle_ventas.id', 'desc')->get();
return ['detalles' => $detalles];
}
public function store(Request $request)
{
$venta = new Venta();
$venta->idcliente = $request->idcliente;
//$venta->idusuario = \Auth::user()->id;
$venta->idusuario = 1; //Si no hay login colocar un id de los usuari
os registrados
$venta->fecha_hora = Carbon::now('America/Lima');
$venta->total = $request->idcliente;
$venta->estado = 'Registrado';
$venta->save();
$detalles = $request->data;//Array de detalles
//Recorro los elementos del array
foreach($detalles as $ep=>$det)
{
$detalle = new DetalleVenta();
$detalle->idventa = $venta->id;
$detalle->idproducto = $det['idproducto'];
$detalle->cantidad = $det['cantidad'];
$detalle->precio = $det['precio'];
$detalle->save();
}
return response()->json($venta->id);
}
}
Para crear el modelo de ventas:
php artisan make:model DetalleVenta
El modelo detalle ventas debe quedar como:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class DetalleVenta extends Model
{
protected $table = 'detalle_ventas';
protected $fillable = [
'idventa',
'idproducto',
'cantidad',
'precio'
];
public $timestamps = false;
}
El archivo deberá contener el siguiente código con las operaciones del crud a través de VUE.
<template>
<div class="card-body">
<div class="card-header">
<i class="fa fa-align-justify"></i> Ventas
<button type="button" @click="mostrarDetalle()" class="btn btn-
secondary">
<i class="icon-plus"></i> Nueva Venta
</button>
</div>
<!-- Listado-->
<template v-if="listado==1">
<div class="table-responsive">
<table class="table table-bordered table-striped table-sm">
<thead>
<tr>
<th>Usuario</th>
<th>Cliente</th>
<th>Fecha Hora</th>
<th>Total</th>
<th>Estado</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<tr v-for="venta in arrayVenta" :key="venta.id">
<td v-text="venta.name"></td>
<td v-text="venta.nombre"></td>
<td v-text="venta.fecha_hora"></td>
<td v-text="venta.total"></td>
<td v-text="venta.estado"></td>
<td>
<button type="button" @click="verVenta(venta.id)" cl
ass="btn btn-success btn-sm">
Detallar Venta
</button>
<template v-if="venta.estado=='Registrado'">
<button type="button" class="btn btn-danger btn-
sm" @click="desactivarVenta(venta.id)">
Desactivar
</button>
</template>
</td>
</tr>
</tbody>
</table>
<nav>
<ul class="pagination">
<li class="page-item" v-if="pagination.current_page > 1">
<a class="page-link" href="#"
@click.prevent="cambiarPagina(pagination.current_page - 1,buscar,criterio)">
Ant</a>
</li>
<li class="page-item" v-for="page in pagesNumber"
:key="page" :class="[page == isActived ? 'active' : '']">
<a class="page-link" href="#"
@click.prevent="cambiarPagina(page,buscar,criterio)" v-text="page"></a>
</li>
<li class="page-item" v-if="pagination.current_page < pagina
tion.last_page">
<a class="page-link" href="#"
@click.prevent="cambiarPagina(pagination.current_page + 1,buscar,criterio)">
Sig</a>
</li>
</ul>
</nav>
</div>
</template>
<!-- Ver ingreso -->
<template v-if="listado==2">
<div class="card-body">
<div class="form-group row border">
<div class="col-md-9">
<div class="form-group">
<label for="">Cliente</label>
<p v-text="nombreCliente"></p>
</div>
</div>
<div class="col-md-3">
<label for="">Id</label>
<p v-text="idVenta"></p>
</div>
</div>
<div class="form-group row border">
<div class="table-responsive col-md-12">
<table class="table table-bordered table-
striped table-sm">
<thead>
<tr>
<th>Prducto</th>
<th>Precio</th>
<th>Cantidad</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody v-if="arrayDetalle.length">
<tr v-for="detalle in arrayDetalle"
:key="detalle.id">
<td v-text="detalle.producto">
</td>
<td v-text="detalle.precio">
</td>
<td v-text="detalle.cantidad">
</td>
<td>
{{detalle.precio*detalle.can
tidad}}
</td>
</tr>
<!--<tr style="background-color: #CE
ECF5;">
<td colspan="4" align="right"><s
trong>Total Parcial:</strong></td>
<td>$ {{totalParcial=(total-
totalImpuesto).toFixed(2)}}</td>
</tr>
<tr style="background-color: #CEECF5
;">
<td colspan="4" align="right"><s
trong>Total Impuesto:</strong></td>
<td>$ {{totalImpuesto=((total*im
puesto)).toFixed(2)}}</td>
</tr>
<tr style="background-color: #CEECF5
;">
<td colspan="4" align="right"><s
trong>Total Neto:</strong></td>
<td>$ {{total}}</td>
</tr>
-->
</tbody>
<tbody v-else>
<tr>
<td colspan="5">
NO hay artículos agregados
</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<button type="button" @click="ocultarDetalle
()" class="btn btn-secondary">Cerrar</button>
</div>
</div>
</div>
</template>
<!-- fin ver ingreso -->
<template v-else-if="listado==0">
<div class="card-body">
<div class="form-group row border">
<div class="col-md-9">
<div class="form-group">
<label for="">Cliente(*)</label>
<select class="form-control" v-model="idcliente">
<option value="0" disabled>Seleccione</option>
<option v-for="cliente in arrayCliente"
:key="cliente.id" :value="cliente.id" v-text="cliente.nombre"></option>
</select>
</div>
</div>
<div class="col-md-12">
<div v-show="errorVenta" class="form-group row div-error">
<div class="text-center text-error">
<div v-for="error in errorMostrarMsjVenta"
:key="error" v-text="error">
</div>
</div>
</div>
</div>
</div>
</div>
<div class="form-group row border">
<div class="col-md-4">
<div class="form-group">
<label>Producto <span style="color:red;" v-
show="idproducto==0">(*Seleccione)</span></label>
<div class="form-inline">
<input type="text" class="form-control" v-model="codigo"
@keyup.enter="buscarArticulo()" placeholder="Ingrese producto">
<input type="text" readonly class="form-control" v-
model="producto">
</div>
</div>
</div>
<div class="col-md-4">
<div class="form-group">
<label>Cantidad <span style="color:red;" v-
show="cantidad==0">(*Ingrese)</span></label>
<input type="number" value="0" class="form-control" v-
model="cantidad">
</div>
</div>
<div class="col-md-2">
<div class="form-group">
<button @click="agregarDetalle()" class="btn btn-success for
m-control btnagregar">Agregar</button>
</div>
</div>
<div class="form-group row">
<div class="col-md-12">
<button type="button" class="btn btn-primary"
@click="registrarVenta()">Registrar Venta</button>
</div>
</div>
<div class="form-group row border">
<div class="table-responsive col-md-12">
<table class="table table-bordered table-striped table-sm">
<thead>
<tr>
<th>Opciones</th>
<th>Producto</th>
<th>Precio</th>
<th>Cantidad</th>
<th>Subtotal</th>
</tr>
</thead>
<tbody v-if="arrayDetalle.length">
<tr v-for="(detalle,index) in arrayDetalle"
:key="detalle.id">
<td>
<button @click="eliminarDetalle(index)" type
="button" class="btn btn-danger btn-sm">
<i class="icon-close"></i>
</button>
</td>
<td v-text="detalle.producto">
</td>
<td>
<input v-model="detalle.precio" typ
e="number" class="form-control">
</td>
<td>
<span style="color:red;" v-
show="detalle.cantidad>detalle.stock">Stock: {{detalle.stock}}</span>
<input v-model="detalle.cantidad" typ
e="number" class="form-control">
</td>
<td>
{{detalle.precio*detalle.cantidad}}
</td>
</tr>
<!--
<tr style="background-color: #CEECF5;">
<td colspan="5" align="right"><strong>Total Parc
ial:</strong></td>
<td>$ {{totalParcial=(total-
totalImpuesto).toFixed(2)}}</td>
</tr>
<tr style="background-color: #CEECF5;">
<td colspan="5" align="right"><strong>Total Impu
esto:</strong></td>
<td>$ {{totalImpuesto=((total*impuesto)/
(1+impuesto)).toFixed(2)}}</td>
</tr>
<tr style="background-color: #CEECF5;">
<td colspan="5" align="right"><strong>Total Neto
:</strong></td>
<td>$ {{total=calcularTotal}}</td>
</tr>
-->
</tbody>
<tbody v-else>
<tr>
<td colspan="6">
NO hay artículos agregados
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
</div>
</template>
<script>
export default {
data (){
return {
arrayVenta : [],
criterio : 'id',
buscar : '',
listado:1,
nombreCliente:'',
idVenta: 0,
idcliente:0,
codigo:0,
arrayCliente: [],
arrayProducto: [],
arrayDetalle : [],
idproducto: 0,
producto: '',
precio: 0,
cantidad: 0,
stock: 0,
total:0,
errorVenta:0,
errorMostrarMsjVenta : [],
pagination : {
'total' : 0,
'current_page' : 0,
'per_page' : 0,
'last_page' : 0,
'from' : 0,
'to' : 0,
},
offset : 3
}
},
computed:{
isActived: function(){
return this.pagination.current_page;
},
//Calcula los elementos de la paginación
pagesNumber: function() {
if(!this.pagination.to) {
return [];
}
var from = this.pagination.current_page - this.offset;
if(from < 1) {
from = 1;
}
var to = from + (this.offset * 2);
if(to >= this.pagination.last_page){
to = this.pagination.last_page;
}
var pagesArray = [];
while(from <= to) {
pagesArray.push(from);
from++;
}
return pagesArray;
},
calcularTotal: function(){
var resultado=0.0;
for(var i=0;i<this.arrayDetalle.length;i++){
resultado=resultado+
(this.arrayDetalle[i].precio*this.arrayDetalle[i].cantidad-
this.arrayDetalle[i].descuento)
}
return resultado;
}
},
methods : {
listarVenta (page,buscar,criterio){
let me=this;
var url= '/venta?page=' + page + '&buscar='+ buscar + '&crit
erio='+ criterio;
axios.get(url).then(function (response) {
var respuesta= response.data;
me.arrayVenta = respuesta.ventas.data;
me.pagination= respuesta.pagination;
})
.catch(function (error) {
console.log(error);
});
},
cambiarPagina(page,buscar,criterio){
let me = this;
//Actualizar la página actual
me.pagination.current_page = page;
//Enviar la petición para visualizar la data de esa página
me.listarVenta(page,buscar,criterio);
},
selectCliente(){
let me=this;
var url= '/cliente/listaCliente';
axios.get(url).then(function (response) {
//console.log(response);
var respuesta= response.data;
me.arrayCliente = respuesta.clientes;
})
.catch(function (error) {
console.log(error);
});
},
buscarArticulo(){
let me=this;
var url= '/producto/buscarProductoVenta?filtro=' + m
e.codigo;
axios.get(url).then(function (response) {
var respuesta= response.data;
me.arrayProducto = respuesta.productos;
if (me.arrayProducto.length>0){
me.producto=me.arrayProducto[0]['nombre'];
me.idproducto=me.arrayProducto[0]['id'];
me.precio=me.arrayProducto[0]['precio'];
me.stock=me.arrayProducto[0]['stock'];
}
else{
me.producto='No existe producto';
me.idproducto=0;
}
})
.catch(function (error) {
console.log(error);
});
},
agregarDetalle(){
let me=this;
if(me.idproducto==0 || me.cantidad==0 || me.precio==0){
}
else{
if(me.encuentra(me.idproducto)){
swal({
type: 'error',
title: 'Error...',
text: 'Ese artículo ya se encuentra agregado!',
})
}
else{
if(me.cantidad>me.stock){
swal({
type: 'error',
title: 'Error...',
text: 'NO hay stock disponible!',
})
}
else{
me.arrayDetalle.push({
idproducto: me.idproducto,
producto: me.producto,
cantidad: me.cantidad,
precio: me.precio,
stock: me.stock
});
me.codigo="";
me.idproducto=0;
me.producto="";
me.cantidad=0;
me.precio=0;
me.stock=0
}
}
}
},
encuentra(id){
var sw=0;
for(var i=0;i<this.arrayDetalle.length;i++){
if(this.arrayDetalle[i].idarticulo==id){
sw=true;
}
}
return sw;
},
registrarVenta(){
if (this.validarVenta()){
return;
}
let me = this;
axios.post('/venta/registrar',{
'idcliente': this.idcliente,
'idproducto': this.idproducto,
'total' : this.total,
'data': this.arrayDetalle
}).then(function (response) {
me.listado=1;
me.listarVenta(1,'','id');
me.idcliente=0;
me.total=0.0;
me.idproducto=0;
me.prodcto='';
me.cantidad=0;
me.precio=0;
me.stock=0;
me.codigo='';
me.arrayDetalle=[];
console.log(response);
}).catch(function (error) {
console.log(error);
});
},
verVenta(id){
let me=this;
me.listado=2;
//Obtener los datos del ingreso
var arrayVentaT=[];
var url= '/venta/obtenerEncabezado?id=' + id;
axios.get(url).then(function (response) {
var respuesta= response.data;
arrayVentaT = respuesta.venta;
me.idVenta = arrayVentaT[0]['id'];
me.nombreCliente = arrayVentaT[0]['nombre'];
me.total=arrayVentaT[0]['total'];
})
.catch(function (error) {
console.log(error);
});
//Obtener los datos de los detalles
var urld= '/venta/obtenerDetalles?id=' + id;
axios.get(urld).then(function (response) {
console.log(response);
var respuesta= response.data;
me.arrayDetalle = respuesta.detalles;
})
.catch(function (error) {
console.log(error);
});
},
ocultarDetalle(){
this.listado=1;
},
mostrarDetalle(){
let me=this;
me.listado=0;
me.idproducto=0;
me.producto='';
me.cantidad=0;
me.precio=0;
me.impuesto=0.20;
me.total=0.0;
me.arrayDetalle=[];
},
ocultarDetalle(){
this.listado=1;
},
validarVenta(){
let me=this;
me.errorVenta=0;
me.errorMostrarMsjVenta =[];
var art;
me.arrayDetalle.map(function(x){
if (x.cantidad>x.stock){
art=x.articulo + " con stock insuficiente";
me.errorMostrarMsjVenta.push(art);
}
});
if (me.idcliente==0) me.errorMostrarMsjVenta.push("Seleccion
e un Cliente");
if (me.arrayDetalle.length<=0) me.errorMostrarMsjVenta.push
("Ingrese detalles");
if (me.errorMostrarMsjVenta.length) me.errorVenta = 1;
return me.errorVenta;
},
},
mounted() {
this.listarVenta(1,this.buscar,this.criterio);
this.selectCliente();
}
}
</script>
Route::get('/venta', 'VentaController@index');
Route::post('/venta/registrar', 'VentaController@store');
Route::put('/venta/desactivar', 'VentaController@desactivar');
Route::get('/venta/obtenerEncabezado', 'VentaController@obtenerEncabezado');
Route::get('/venta/obtenerDetalles', 'VentaController@obtenerDetalles');
Vue.component('venta-component', requir
e('./components/VentaComponent.vue'));
require('./bootstrap');
window.Vue = require('vue');
/**
* Next, we will create a fresh Vue application instance and attach it to
* the page. Then, you may begin adding components to this application
* or customize the JavaScript scaffolding to fit your unique needs.
*/
//Vue.component('example-component', require('./components/ExampleComponent.
vue'));
Vue.component('categoria-component', requir
e('./components/CategoriaComponent.vue'));
Vue.component('venta-component', requir
e('./components/VentaComponent.vue'));
const app = new Vue({
el: '#app',
data :{
menu :0
}
});
Ejecutar el comando:
Cada vez que se efectúe un cambio en los componentes o en el javascript ejecutar el comando
npm run dev
Al finalizar la compilación debe aparecer un mensaje en el terminal de comandos como el
siguiente:
Luego en las vistas verificar en menu.blade.php si ventas está incluido en las opciones de menú:
<li @click="menu=3" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Ventas</a>
</li>
<div class="sidebar">
<nav class="sidebar-nav">
<ul class="nav">
<li class="nav-item nav-dropdown">
<a class="nav-link nav-dropdown-toggle" href="#"><i clas
s="icon-bag"></i> Almacén</a>
<ul class="nav-dropdown-items">
<li @click="menu=1" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Categorías</a>
</li>
<li @click="menu=2" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Productos</a>
</li>
<li @click="menu=3" class="nav-item">
<a class="nav-link" href="#"><i class="icon-
bag"></i> Ventas</a>
</li>
</ul>
</li>
</ul>
</nav>
</div>
@extends('principal')
@section('contenido')
<template v-if="menu==1">
<!-- v-if es el if en vue -->
<!--redireccionar al componente categoria-component -->
<categoria-component></categoria-component>
</template>
<template v-if="menu==2">
<h1>Contenido del menú 2</h1>
</template>
<template v-if="menu==3">
<!--redireccionar al componente venta-component -->
<venta-component></venta-component>
</template>
@endsection