inicio del proyecto

This commit is contained in:
Andres2908 2022-05-24 22:38:17 -05:00
parent 420f0e591b
commit bb27bd3f12
30 changed files with 15948 additions and 0 deletions

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
node_modules
*.log*
.nuxt
.nitro
.cache
.output
.env
dist

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"semi": false,
"singleQuote": true
}

5
app.vue Normal file
View File

@ -0,0 +1,5 @@
<template>
<div>
<NuxtWelcome />
</div>
</template>

BIN
assets/404.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/logo_acatlan.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
assets/logo_unam.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

85
assets/scss/main.scss Normal file
View File

@ -0,0 +1,85 @@
@import '~bulma/sass/utilities/_all';
// Set your colors
$primary: #8c67ef;
$primary-light: findLightColor($primary);
$primary-dark: findDarkColor($primary);
$primary-invert: findColorInvert($primary);
$twitter: #4099ff;
$twitter-invert: findColorInvert($twitter);
// Lists and maps
$custom-colors: null !default;
$custom-shades: null !default;
// Setup $colors to use as bulma classes (e.g. 'is-twitter')
$colors: mergeColorMaps(
(
'white': (
$white,
$black,
),
'black': (
$black,
$white,
),
'light': (
$light,
$light-invert,
),
'dark': (
$dark,
$dark-invert,
),
'primary': (
$primary,
$primary-invert,
$primary-light,
$primary-dark,
),
'link': (
$link,
$link-invert,
$link-light,
$link-dark,
),
'info': (
$info,
$info-invert,
$info-light,
$info-dark,
),
'success': (
$success,
$success-invert,
$success-light,
$success-dark,
),
'warning': (
$warning,
$warning-invert,
$warning-light,
$warning-dark,
),
'danger': (
$danger,
$danger-invert,
$danger-light,
$danger-dark,
),
),
$custom-colors
);
// Links
$link: $primary;
$link-invert: $primary-invert;
$link-focus-border: $primary;
$tablet: 767px;
// Import bulma styles
@import '~bulma';
// Import buefy styles
@import '~buefy/src/scss/buefy';

130
components/Login.vue Normal file
View File

@ -0,0 +1,130 @@
<template>
<div class="full-h is-flex is-justify-content-center is-align-items-center">
<form class="box">
<div class="has-text-centered">
<h2 class="is-size-1">PC Puma</h2>
</div>
<b-field label="Usuario" :type="error">
<b-input v-model="usuario" @keyup.enter.native="login()" />
</b-field>
<b-field label="Contraseña" :type="error">
<b-input
type="password"
password-reveal
v-model="password"
@keyup.enter.native="login()"
/>
</b-field>
<!-- <b-field label="Modulo" :type="error">
<b-select v-model="idModulo" expanded>
<option value="" disabled>Módulo</option>
<option v-for="(m, i) in modulos" :key="i" :value="m.idModulo">
{{ m.modulo }}
</option>
</b-select>
</b-field> -->
<div class="has-text-centered">
<b-button
@click="login()"
type="is-success"
:disabled="error || !usuario || !password || !idModulo"
>
Iniciar Sesión
</b-button>
</div>
</form>
</div>
</template>
<script>
import axios from 'axios'
export default {
props: {
imprimirError: { type: Function, required: true },
updateIsLoading: { type: Function, required: true },
},
data() {
return {
error: '',
idModulo: '',
password: '',
usuario: '',
modulos: [],
}
},
methods: {
login() {
if (this.usuario && this.password && !this.error && this.idModulo) {
const data = {
operador: this.usuario,
password: this.password,
}
this.updateIsLoading(true)
axios
.post(`${process.env.api}/operador/login`, data)
.then((res) => {
const info = res.data
localStorage.setItem('token', info.token)
localStorage.setItem('idModulo', this.idModulo)
localStorage.setItem('idOperador', info.Operador.idOperador)
localStorage.setItem('operador', info.Operador.operador)
localStorage.setItem(
'idTipoUsuario',
info.Operador.TipoUsuario.idTipoUsuario
)
localStorage.setItem(
'tipoUsuario',
info.Operador.TipoUsuario.tipoUsuario
)
this.updateIsLoading(false)
this.$router.push('/prestamo_devolucion')
})
.catch((err) => {
this.error = 'is-danger'
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
}
},
obtenerCatalogoModulo() {
axios
.get(`${process.env.api}/modulo`)
.then((res) => {
this.modulos = res.data
this.idModulo = '1'
})
.catch((err) => {
this.imprimirError(err.response.data)
})
},
},
watch: {
password() {
if (this.error) this.error = ''
},
usuario() {
if (this.error) this.error = ''
},
},
created() {
this.obtenerCatalogoModulo()
},
}
</script>
<style scoped>
.full-h {
height: 75vh;
}
form {
width: 30rem;
}
</style>

View File

@ -0,0 +1,32 @@
<template>
<footer class="footer">
<div class="container has-text-centered is-size-7 has-text-white">
<p>Hecho en México, todos los derechos reservados {{ year() }}.</p>
<p>
Esta página puede ser reproducida con fines no lucrativos, siempre y
cuando no se mutile, se cite la fuente completa y su dirección
electrónica. De otra forma, requiere permiso previo por escrito de la
institución.
</p>
</div>
</footer>
</template>
<script>
import moment from 'moment'
export default {
methods: {
year() {
return moment().year()
},
},
}
</script>
<style scoped>
footer {
background-color: #1d3d6f;
}
</style>

View File

@ -0,0 +1,41 @@
<template>
<header>
<div class="container py-3">
<div
class="
columns
is-mobile is-gapless is-vcentered is-justify-content-space-between
"
>
<a
href="https://www.unam.mx/"
class="column is-5-mobile is-3-tablet is-one-fifth-widescreen ml-3"
target="_blank"
>
<b-image :src="require('@/assets/logo_unam.png')" alt="logo_unam" />
</a>
<a
href="https://www.acatlan.unam.mx/"
class="column is-5-mobile is-3-tablet is-one-fifth-widescreen mr-3"
target="_blank"
>
<b-image
:src="require('@/assets/logo_acatlan.png')"
alt="logo_acatlan"
/>
</a>
</div>
</div>
</header>
</template>
<script>
export default {}
</script>
<style scoped>
header {
background-color: #1d3d6f;
}
</style>

166
components/layouts/Menu.vue Normal file
View File

@ -0,0 +1,166 @@
<template>
<aside>
<b-sidebar type="is-white" v-model="open" fullheight overlay>
<div class="m-5">
<b-image :src="require('@/assets/logo.png')" alt="logo_pc_puma" />
<b-menu>
<b-menu-list label="Menu">
<!-- <b-menu-list>
<b-menu-item
label="Inicio"
:icon="icono('/inicio')"
:disabled="activo('/inicio')"
@click="opcionMenu('/inicio')"
/>
</b-menu-list> -->
<b-menu-list>
<b-menu-item
label="Préstamo/Devolución"
:icon="icono('/prestamo_devolucion')"
:disabled="activo('/prestamo_devolucion')"
@click="opcionMenu('/prestamo_devolucion')"
/>
</b-menu-list>
<b-menu-list>
<b-menu-item
label="Historial de Préstamos"
:icon="icono('/historial_prestamos')"
:disabled="activo('/historial_prestamos')"
@click="opcionMenu('/historial_prestamos')"
/>
</b-menu-list>
<b-menu-item icon="cellphone-link">
<template #label="props">
Equipos
<b-icon
class="is-pulled-right"
:icon="props.expanded ? 'menu-down' : 'menu-up'"
/>
</template>
<b-menu-item
label="Buscar equipo"
:icon="icono('/equipos/buscar_equipo')"
:disabled="activo('/equipos/buscar_equipo')"
@click="opcionMenu('/equipos/buscar_equipo')"
/>
<b-menu-item
label="Todos los equipos"
:icon="icono('/equipos/todos_equipos')"
:disabled="activo('/equipos/todos_equipos')"
@click="opcionMenu('/equipos/todos_equipos')"
/>
</b-menu-item>
<b-menu-item
label="Regreso Inmediato"
:icon="icono('/regreso_inmediato')"
:disabled="activo('/regreso_inmediato')"
@click="opcionMenu('/regreso_inmediato')"
v-if="idTipoUsuario === 1"
/>
<b-menu-item icon="account-plus" v-if="idTipoUsuario === 1">
<template #label="props">
Administrator
<b-icon
class="is-pulled-right"
:icon="props.expanded ? 'menu-down' : 'menu-up'"
/>
</template>
<b-menu-item
label="Usuarios"
:icon="icono('/admin/usuarios')"
:disabled="activo('/admin/usuarios')"
@click="opcionMenu('/admin/usuarios')"
/>
<b-menu-item
label="Operadores"
:icon="icono('/admin/operadores')"
:disabled="activo('/admin/operadores')"
@click="opcionMenu('/admin/operadores')"
/>
<b-menu-item
label="Carga Masiva Equipos"
:icon="icono('/admin/carga_masiva_equipos')"
:disabled="activo('/admin/carga_masiva_equipos')"
@click="opcionMenu('/admin/carga_masiva_equipos')"
/>
<b-menu-item
label="Carga Masiva Usuarios"
:icon="icono('/admin/carga_masiva_usuarios')"
:disabled="activo('/admin/carga_masiva_usuarios')"
@click="opcionMenu('/admin/carga_masiva_usuarios')"
/>
<b-menu-item
label="Reportes"
:icon="icono('/admin/reportes')"
:disabled="activo('/admin/reportes')"
@click="opcionMenu('/admin/reportes')"
/>
</b-menu-item>
</b-menu-list>
<b-menu-list label="Acciones">
<b-menu-item label="Cerrar Sesión" @click="cerrarSesion()" />
</b-menu-list>
</b-menu>
</div>
</b-sidebar>
<dir class="pl-0 py-5">
<b-button type="is-info" @click="open = true">
<b-icon icon="menu" />
</b-button>
</dir>
</aside>
</template>
<script>
export default {
data() {
return {
open: false,
idTipoUsuario: Number(localStorage.getItem('idTipoUsuario')),
}
},
methods: {
cerrarSesion() {
localStorage.clear()
this.$router.push('/')
},
opcionMenu(path) {
this.open = false
this.$router.push(path)
},
activo(ruta) {
return ruta === window.location.pathname
},
icono(ruta) {
return this.activo(ruta) ? 'square' : 'crop-square'
},
},
created() {
if (
!localStorage.getItem('token') ||
!localStorage.getItem('idOperador') ||
!localStorage.getItem('idTipoUsuario') ||
!localStorage.getItem('operador') ||
!localStorage.getItem('tipoUsuario') ||
!localStorage.getItem('idModulo')
)
this.cerrarSesion()
},
}
</script>
<style></style>

View File

@ -0,0 +1,25 @@
<template>
<section class="columns is-justify-content-space-between pb-5">
<h2 class="column is-narrow is-size-2 is-size-3-mobile">{{ title }}</h2>
<div
class="column is-narrow is-flex is-align-items-center"
v-if="operador.operador"
>
<b-icon icon="account-circle" size="is-medium" />
<p class="is-size-4 pl-1">{{ operador.operador }}</p>
</div>
</section>
</template>
<script>
export default {
props: {
operador: { type: Object, required: false },
title: { type: String, required: true },
},
}
</script>
<style></style>

View File

@ -0,0 +1,185 @@
<template>
<section>
<b-tabs type="is-toggle" :destroy-on-hide="true" v-model="tab" expanded>
<b-tab-item label="Manual" icon="lead-pencil" value="0">
<div class="columns">
<ManualIdPrestamo :prestamoIdPrestamo="prestamoIdPrestamo" />
<ManualNumeroInventario
:operador="operador"
:imprimirWarning="imprimirWarning"
:imprimirError="imprimirError"
:imprimirMensaje="imprimirMensaje"
:updateIsLoading="updateIsLoading"
/>
</div>
</b-tab-item>
<b-tab-item label="Scanner" icon="qrcode-scan" value="1">
<Scanner :prestamoIdPrestamo="prestamoIdPrestamo" />
</b-tab-item>
</b-tabs>
</section>
</template>
<script>
import axios from 'axios'
import ManualIdPrestamo from '@/components/operador/ManualIdPrestamo'
import ManualNumeroInventario from '@/components/operador/ManualNumeroInventario'
import ModalMultaIdPrestamo from './ModalMultaIdPrestamo'
import Scanner from '@/components/operador/Scanner'
export default {
components: { ManualIdPrestamo, ManualNumeroInventario, Scanner },
props: {
operador: { type: Object, required: true },
imprimirMensaje: { type: Function, required: true },
imprimirWarning: { type: Function, required: true },
imprimirError: { type: Function, required: true },
updateIsLoading: { type: Function, required: true },
},
data() {
return { idPrestamo: '', updateVariable: () => {}, tab: '0' }
},
methods: {
revisionMulta() {
const modalProps = {
operador: this.operador,
idPrestamo: this.idPrestamo,
regresar: this.regresar,
regresoInmediato: false,
imprimirError: this.imprimirError,
imprimirMensaje: this.imprimirMensaje,
updateIsLoading: this.updateIsLoading,
}
this.$buefy.modal.open({
props: modalProps,
parent: this,
component: ModalMultaIdPrestamo,
hasModalCard: true,
customClass: 'custom-class custom-class-2',
trapFocus: true,
})
},
revisionMultaRegresoInmediato() {
const modalProps = {
operador: this.operador,
idPrestamo: this.idPrestamo,
regresar: this.regresar,
regresoInmediato: true,
imprimirError: this.imprimirError,
imprimirMensaje: this.imprimirMensaje,
updateIsLoading: this.updateIsLoading,
}
this.$buefy.modal.open({
props: modalProps,
parent: this,
component: ModalMultaIdPrestamo,
hasModalCard: true,
customClass: 'custom-class custom-class-2',
trapFocus: true,
})
},
entregar() {
const data = {
idOperadorEntrega: this.operador.idOperador,
idPrestamo: this.idPrestamo,
}
this.updateIsLoading(true)
axios
.put(`${process.env.api}/prestamo/entregar`, data, this.operador.token)
.then((res) => {
this.updateVariable()
if (this.tab === '1') this.tab = '0'
this.imprimirMensaje(
`
Entregar al usuario el equipo:<br>
Carrito: ${res.data.Carrito.carrito}<br>
Equipo: ${res.data.equipo}<br>
Tipo: ${res.data.Carrito.TipoCarrito.tipoCarrito}<br>
Número de Inventario: ${res.data.numeroInventario}<br>
Número de Serie: ${res.data.numeroSerie}<br>
`
)
this.updateIsLoading(false)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
regresar() {
const data = {
idOperadorRegreso: this.operador.idOperador,
idPrestamo: this.idPrestamo,
}
this.updateIsLoading(true)
axios
.put(`${process.env.api}/prestamo/regresar`, data, this.operador.token)
.then((res) => {
this.updateVariable()
if (this.tab === '1') this.tab = '0'
this.updateIsLoading(false)
this.imprimirMensaje(res.data.message)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
prestamoIdPrestamo(idPrestamo, updateVariable = () => {}) {
this.updateIsLoading(true)
this.idPrestamo = idPrestamo
this.updateVariable = updateVariable
axios
.get(
`${process.env.api}/prestamo?idPrestamo=${this.idPrestamo}`,
this.operador.token
)
.then((res) => {
let data = res.data
if (data.activo) {
if (data.Equipo.Status.idStatus === 1)
this.imprimirWarning(
'Esta a punto de entregar el equipo de cómputo al usuario y dar inicio a un préstamo. ¿Esta segur@ de querer realizar esta acción?',
this.entregar
)
else if (data.Equipo.Status.idStatus === 2)
this.imprimirWarning(
`
Esta apunto de recibir el equipo:<br>
Equipo: ${data.Equipo.equipo}<br>
Carrito: ${data.Equipo.Carrito.carrito}<br>
Tipo: ${data.Equipo.Carrito.TipoCarrito.tipoCarrito}<br>
Número de Inventario: ${data.Equipo.numeroInventario}<br>
Número de Serie: ${data.Equipo.numeroSerie}<br>
y dar por terminado el préstamo. ¿Esta segur@ de querer realizar esta acción?
`,
this.revisionMulta
)
} else if (data.regresoInmediato && !data.OperadorRegreso)
this.imprimirWarning(
'Esta a punto de confirmar el regreso de un equipo de cómputo. ¿Esta segur@ de querer realizar esta acción?',
this.revisionMultaRegresoInmediato
)
else
this.imprimirError({
message: 'Este préstamo ya no se encuentra activo.',
})
this.updateIsLoading(false)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,45 @@
<template>
<div class="column">
<b-field label="Número de Prestamo">
<b-input
type="number"
@keyup.enter.native="llamarPrestamoIdPrestamo()"
v-model="idPrestamo"
/>
</b-field>
<b-field class="has-text-centered">
<b-button
type="is-info"
@click="prestamoIdPrestamo(idPrestamo, idPrestamoReset)"
:disabled="!idPrestamo"
>
Enviar
</b-button>
</b-field>
</div>
</template>
<script>
export default {
props: {
prestamoIdPrestamo: { type: Function, required: true },
},
data() {
return {
idPrestamo: '',
}
},
methods: {
idPrestamoReset() {
this.idPrestamo = ''
},
llamarPrestamoIdPrestamo() {
if (this.idPrestamo)
this.prestamoIdPrestamo(this.idPrestamo, this.idPrestamoReset)
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,126 @@
<template>
<div class="column">
<b-field label="Número de Inventario">
<b-input
type="text"
v-model="numeroInventario"
@keyup.enter.native="prestamoNumeroInventario()"
/>
</b-field>
<b-field class="has-text-centered">
<b-button
type="is-info"
@click="prestamoNumeroInventario()"
:disabled="!numeroInventario"
>
Enviar
</b-button>
</b-field>
</div>
</template>
<script>
import axios from 'axios'
import ModalMultaNumeroInventario from './ModalMultaNumeroInventario'
export default {
props: {
operador: { type: Object, required: true },
imprimirWarning: { type: Function, required: true },
imprimirError: { type: Function, required: true },
imprimirMensaje: { type: Function, required: true },
updateIsLoading: { type: Function, required: true },
},
data() {
return {
numeroInventario: '',
}
},
methods: {
revisionMultaNumeroInventario() {
const modalProps = {
operador: this.operador,
numeroInventario: this.numeroInventario,
regresarNumeroInventario: this.regresarNumeroInventario,
updateIsLoading: this.updateIsLoading,
imprimirError: this.imprimirError,
imprimirMensaje: this.imprimirMensaje,
}
this.$buefy.modal.open({
props: modalProps,
parent: this,
component: ModalMultaNumeroInventario,
hasModalCard: true,
customClass: 'custom-class custom-class-2',
trapFocus: true,
})
},
regresarNumeroInventario() {
const data = {
idOperadorRegreso: this.operador.idOperador,
numeroInventario: this.numeroInventario,
}
this.updateIsLoading(true)
axios
.put(
`${process.env.api}/prestamo/regresar_numero_inventario`,
data,
this.operador.token
)
.then((res) => {
this.numeroInventario = ''
this.imprimirMensaje(res.data.message)
this.updateIsLoading(false)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
prestamoNumeroInventario() {
if (this.numeroInventario) {
this.updateIsLoading(true)
axios
.get(
`${process.env.api}/prestamo/prestamo_numero_inventario?numeroInventario=${this.numeroInventario}`,
this.operador.token
)
.then((res) => {
let data = res.data
this.updateIsLoading(false)
if (data.activo) {
if (data.Equipo.Status.idStatus === 1)
this.imprimirError({
message: 'Aún no se entrega el equipo de cómputo al usuario.',
})
else if (data.Equipo.Status.idStatus === 2)
this.imprimirWarning(
'Esta a punto de recibir el equipo de cómputo y dar por terminado un préstamo. ¿Esta segur@ de querer realizar esta acción?',
this.revisionMultaNumeroInventario
)
}
// this.imprimirWarning(
// 'Esta a punto de confirmar el regreso de un equipo de cómputo. ¿Esta segur@ de querer realizar esta acción?',
// this.revisionMultaNumeroInventario
// )
else
this.imprimirError({
message:
'Por el momento la opción de confirmar el regreso de un equipo por número de inventario no se encuentra disponible. Favor de usar el número de préstamo.',
})
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
}
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,195 @@
<template>
<form>
<div class="modal-card">
<header class="modal-card-head">
<h5 class="modal-card-title">Multas</h5>
</header>
<section class="modal-card-body">
<div>
<div>
<div class="is-flex is-justify-content-space-between py-3">
<p>¿Desea multar al usuario?</p>
</div>
<b-field>
<b-radio
type="is-danger"
size="is-medium"
:native-value="true"
v-model="multaUsuario"
>
Si
</b-radio>
</b-field>
<b-field>
<b-radio
type="is-info"
size="is-medium"
:native-value="false"
v-model="multaUsuario"
>
No
</b-radio>
</b-field>
</div>
<div v-show="multaUsuario">
<div class="is-flex is-justify-content-space-between py-3">
<p>Infraccion cometida por el usuario</p>
</div>
<b-field>
<b-select
placeholder="Seleccionar"
v-model="idInfraccion"
expanded
>
<option disabled value="">Selecciona una opción</option>
<option
v-for="(item, index) in infracciones"
:key="index"
:value="item.idInfraccion"
>
{{ item.infraccion }}
</option>
</b-select>
</b-field>
</div>
<div v-show="multaUsuario">
<div class="is-flex is-justify-content-space-between py-3">
<p>Motivos por los que desea multar al usuario</p>
</div>
<b-field>
<b-input
type="textarea"
expanded
maxlength="500"
v-model="descripcion"
/>
</b-field>
</div>
</div>
</section>
<footer
class="modal-card-foot"
style="display: flex; justify-content: space-between"
>
<b-button label="Cancelar" type="is-danger" @click="$emit('close')" />
<b-button
label="Enviar"
type="is-success"
@click="
decisionOperador()
$emit('close')
"
:disabled="multaUsuario && !(idInfraccion && descripcion)"
/>
</footer>
</div>
</form>
</template>
<script>
import axios from 'axios'
export default {
props: {
regresoInmediato: { type: Boolean, required: true },
operador: { type: Object, required: true },
idPrestamo: { type: String, required: true },
regresar: { type: Function, required: true },
imprimirError: { type: Function, required: true },
imprimirMensaje: { type: Function, required: true },
updateIsLoading: { type: Function, required: true },
},
data() {
return {
multaUsuario: false,
idInfraccion: '',
descripcion: '',
infracciones: [],
}
},
methods: {
decisionOperador() {
if (this.multaUsuario) {
if (this.regresoInmediato) this.multa()
else this.multaPrestamo()
} else this.regresar()
},
multa() {
const data = {
idOperadorMulta: this.operador.idOperador,
idPrestamo: this.idPrestamo,
idInfraccion: this.idInfraccion,
descripcion: this.descripcion,
}
this.updateIsLoading(true)
axios
.post(`${process.env.api}/multa/multar`, data, this.operador.token)
.then((res) => {
this.updateIsLoading(false)
this.imprimirMensaje(res.data.message, this.regresar)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
multaPrestamo() {
const data = {
idOperadorMulta: this.operador.idOperador,
idPrestamo: this.idPrestamo,
idInfraccion: this.idInfraccion,
descripcion: this.descripcion,
}
this.updateIsLoading(true)
axios
.post(
`${process.env.api}/multa/multar_id_prestamo`,
data,
this.operador.token
)
.then((res) => {
this.updateIsLoading(false)
this.imprimirMensaje(res.data.message, this.regresar)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
obtenerCatalogoInfracciones() {
axios
.get(`${process.env.api}/infraccion`, this.operador.token)
.then((res) => {
this.infracciones = res.data
})
.catch((err) => {
this.imprimirError(err.response.data)
})
},
},
watch: {
multaUsuario(value) {
if (!value) {
this.idInfraccion = ''
this.descripcion = ''
}
},
},
created() {
this.obtenerCatalogoInfracciones()
},
}
</script>
<style></style>

View File

@ -0,0 +1,172 @@
<template>
<form>
<div class="modal-card">
<header class="modal-card-head">
<h5 class="modal-card-title">Multas</h5>
</header>
<div class="modal-card-body">
<div>
<div>
<div class="is-flex is-justify-content-space-between py-3">
<p>¿Desea multar al usuario?</p>
</div>
<b-field>
<b-radio
type="is-danger"
size="is-medium"
:native-value="true"
v-model="multaUsuario"
>
Si
</b-radio>
</b-field>
<b-field>
<b-radio
type="is-info"
size="is-medium"
:native-value="false"
v-model="multaUsuario"
>
No
</b-radio>
</b-field>
</div>
<div v-show="multaUsuario">
<div class="is-flex is-justify-content-space-between py-3">
<p>Infraccion cometida por el usuario</p>
</div>
<b-field>
<b-select
placeholder="Seleccionar"
v-model="idInfraccion"
expanded
>
<option disabled value="">Selecciona una opción</option>
<option
v-for="(item, index) in infracciones"
:key="index"
:value="item.idInfraccion"
>
{{ item.infraccion }}
</option>
</b-select>
</b-field>
</div>
<div v-show="multaUsuario">
<div class="is-flex is-justify-content-space-between py-3">
<p>Motivos por los que desea multar al usuario</p>
</div>
<b-field>
<b-input
type="textarea"
expanded
maxlength="500"
v-model="descripcion"
/>
</b-field>
</div>
</div>
</div>
<footer
class="modal-card-foot"
style="display: flex; justify-content: space-between"
>
<b-button label="Cancelar" type="is-danger" @click="$emit('close')" />
<b-button
label="Enviar"
type="is-success"
@click="
decisionOperador()
$emit('close')
"
:disabled="multaUsuario && !(idInfraccion && descripcion)"
/>
</footer>
</div>
</form>
</template>
<script>
import axios from 'axios'
export default {
props: {
operador: { type: Object, required: true },
numeroInventario: { type: String, required: true },
regresarNumeroInventario: { type: Function, require: true },
imprimirError: { type: Function, required: true },
imprimirMensaje: { type: Function, required: true },
updateIsLoading: { type: Function, required: true },
},
data() {
return {
multaUsuario: false,
idInfraccion: '',
descripcion: '',
infracciones: [],
}
},
methods: {
decisionOperador() {
if (this.multaUsuario) this.multaNumeroInventario()
else this.regresarNumeroInventario()
},
multaNumeroInventario() {
const data = {
idOperadorMulta: this.operador.idOperador,
numeroInventario: this.numeroInventario,
idInfraccion: this.idInfraccion,
descripcion: this.descripcion,
}
this.updateIsLoading(true)
axios
.post(
`${process.env.api}/multa/multar_numero_inventario`,
data,
this.operador.token
)
.then((res) => {
this.updateIsLoading(false)
this.imprimirMensaje(res.data.message, this.regresarNumeroInventario)
})
.catch((err) => {
this.updateIsLoading(false)
this.imprimirError(err.response.data)
})
},
obtenerCatalogoInfracciones() {
axios
.get(`${process.env.api}/infraccion`, this.operador.token)
.then((res) => {
this.infracciones = res.data
})
.catch((err) => {
this.imprimirError(err.response.data)
})
},
},
watch: {
multaUsuario(value) {
if (!value) {
this.idInfraccion = ''
this.descripcion = ''
}
},
},
created() {
this.obtenerCatalogoInfracciones()
},
}
</script>
<style></style>

View File

@ -0,0 +1,40 @@
<template>
<QrcodeStream @decode="onDecode"> </QrcodeStream>
</template>
<script>
import { QrcodeStream } from 'vue-qrcode-reader'
export default {
components: { QrcodeStream },
props: {
prestamoIdPrestamo: { type: Function, required: true },
},
data() {
return { idPrestamo: '' }
},
methods: {
resetear() {
if (this.idPrestamo) this.idPrestamo = ''
},
onDecode(decodedString) {
let decodedData = {}
try {
decodedData = JSON.parse(decodedString)
} catch (err) {
this.resetear()
}
if (decodedData.idPrestamo) {
if (typeof decodedData.idPrestamo === 'number')
this.idPrestamo = decodedData.idPrestamo.toString()
else this.idPrestamo = decodedData.idPrestamo
}
if (this.idPrestamo)
this.prestamoIdPrestamo(this.idPrestamo, this.resetear)
},
},
}
</script>
<style></style>

View File

@ -0,0 +1,267 @@
<template>
<section>
<h4 class="title">Préstamos Activos: {{ total }}</h4>
<div class="columns is-multiline mb-0">
<b-field class="column is-4-tablet is-3-desktop" label="Número de Cuenta">
<b-input
type="text"
placeholder="Número de Cuenta"
icon="account"
maxlength="9"
@keyup.enter.native="obtenerPrestamos()"
v-model="search.numeroCuenta"
rounded
/>
</b-field>
<b-field
class="column is-4-tablet is-3-desktop"
label="Número de Inventario"
>
<b-input
type="text"
placeholder="Número de Inventario"
icon="laptop"
@keyup.enter.native="obtenerPrestamos()"
v-model="search.numeroInventario"
rounded
/>
</b-field>
<b-field class="column is-4-tablet is-3-desktop" label="Módulo">
<b-select icon="storefront-outline" v-model="idModulo" expanded rounded>
<option value="" disabled>Módulo</option>
<option
v-for="(m, i) in modulos"
:key="i"
:value="m.idModulo"
v-show="m.idModulo === idModulo || operador.idTipoUsuario === 1"
>
{{ m.modulo }}
</option>
</b-select>
</b-field>
<b-field class="column is-4-tablet is-3-desktop" label="Tipo de Equipo">
<b-select
icon="book-variant"
v-model="search.idTipoCarrito"
expanded
rounded
>
<option value="">Tipo</option>
<option
v-for="(t, i) in tipoCarritos"
:key="i"
:value="t.idTipoCarrito"
>
{{ t.tipoCarrito }}
</option>
</b-select>
</b-field>
<b-field
class="column is-4-tablet is-3-desktop"
label="Número de Préstamo"
>
<b-input
type="text"
placeholder="Número de Préstamo"
icon="numeric"
@keyup.enter.native="obtenerPrestamos()"
v-model="search.idPrestamo"
rounded
/>
</b-field>
<b-field class="column is-4-tablet is-3-desktop" label="Carrito">
<b-input
type="text"
placeholder="Carrito"
icon="cart"
@keyup.enter.native="obtenerPrestamos()"
v-model="search.carrito"
rounded
/>
</b-field>
<b-field class="column is-4-tablet is-3-desktop" label="Equipo">
<b-input
type="text"
placeholder="Equipo"
icon="laptop"
@keyup.enter.native="obtenerPrestamos()"
v-model="search.equipo"
rounded
/>
</b-field>
</div>
<div class="is-flex is-justify-content-center mb-5">
<b-button type="is-success" @click="obtenerPrestamos()">
Buscar
</b-button>
</div>
<TablaPrestamos
:operador="operador"
:isLoadingTable="isLoading"
:data="data"
:page="page"
:onPageChange="onPageChange"
:total="total"
:columnaNumeroCuenta="true"
:columnaNombre="true"
:columnaNumeroInventario="true"
:columnaTipo="true"
:columnaCarrito="true"
:columnaHoraFin="true"
:columnaEnUso="true"
:columnaBotonCancelar="true"
:columnaIdPrestamo="true"
:columnaEquipo="true"
:columnaregresoInmediato="true"
:columnaHoraRegreso="true"
:columnaLiberar="true"
:filaRetraso="true"
:imprimirError="imprimirError"
:imprimirWarning="imprimirWarning"
:imprimirMensaje="imprimirMensaje"
:obtenerPrestamos="obtenerPrestamos"
:updateIsLoadingPage="updateIsLoading"
/>
</section>
</template>
<script>
import axios from 'axios'
import TablaPrestamos from '@/components/operador/TablaPrestamos'
export default {
components: { TablaPrestamos },
data() {
return {
modulos: [],
tipoCarritos: [],
data: [],
page: 1,
total: 0,
search: {
idTipoCarrito: '',
},
lastSearch: {},
idModulo: null,
isLoading: false,
}
},
props: {
operador: { type: Object, required: true },
imprimirError: { type: Function, required: true },
imprimirWarning: { type: Function, required: true },
imprimirMensaje: { type: Function, required: true },
updateIsLoading: { type: Function, required: true },
},
methods: {
onPageChange(page) {
this.page = page
this.obtenerPrestamos()
},
obtenerPrestamos() {
let data = ''
this.isLoading = true
if (
this.search.numeroCuenta != this.lastSearch.numeroCuenta ||
this.search.numeroInventario != this.lastSearch.numeroInventario ||
this.search.idTipoCarrito != this.lastSearch.idTipoCarrito ||
this.search.idPrestamo != this.lastSearch.idPrestamo ||
this.search.carrito != this.lastSearch.carrito ||
this.search.equipo != this.lastSearch.equipo ||
this.idModulo != this.lastSearch.idModulo
) {
this.page = 1
this.lastSearch.numeroCuenta = this.search.numeroCuenta
this.lastSearch.numeroInventario = this.search.numeroInventario
this.lastSearch.idTipoCarrito = this.search.idTipoCarrito
this.lastSearch.idPrestamo = this.search.idPrestamo
this.lastSearch.carrito = this.carrito
this.lastSearch.equipo = this.equipo
this.lastSearch.idModulo = this.idModulo
}
if (this.search.idTipoCarrito)
data += `&idTipoCarrito=${this.search.idTipoCarrito}`
if (this.search.numeroInventario)
data += `&numeroInventario=${this.search.numeroInventario}`
if (this.search.numeroCuenta)
data = `&usuario=${this.search.numeroCuenta}`
if (this.search.idPrestamo) data = `&idPrestamo=${this.search.idPrestamo}`
if (this.search.carrito) data = `&carrito=${this.search.carrito}`
if (this.search.equipo) data = `&equipo=${this.search.equipo}`
axios
.get(
`${process.env.api}/prestamo/activos?pagina=${this.page}&idModulo=${this.idModulo}${data}`,
this.operador.token
)
.then((res) => {
this.data = res.data.rows
this.total = res.data.count
this.isLoading = false
})
.catch((err) => {
this.isLoading = false
this.imprimirError(err.response.data)
})
},
obtenerCatalogoModulo() {
axios
.get(`${process.env.api}/modulo`, this.operador.token)
.then((res) => {
this.modulos = res.data
this.obtenerCatalogoTipoCarrito()
})
.catch((err) => {
this.imprimirError(err.response.data)
})
},
obtenerCatalogoTipoCarrito() {
axios
.get(`${process.env.api}/tipo_carrito`, this.operador.token)
.then((res) => {
this.tipoCarritos = res.data
})
.catch((err) => {
this.imprimirError(err.response.data)
})
},
},
watch: {
idModulo() {
localStorage.setItem('idModulo', this.idModulo)
this.obtenerPrestamos()
},
},
created() {
if (this.operador.idModulo) {
this.idModulo = this.operador.idModulo
this.obtenerCatalogoModulo()
}
},
mounted() {
this.socket = this.$nuxtSocket({
name: 'main',
path: process.env.path,
})
this.socket.on('reconnect', (data) => {
this.obtenerPrestamos()
})
this.socket.on('actualizar', (data) => {
this.obtenerPrestamos()
})
},
}
</script>
<style></style>

29
layouts/default.vue Normal file
View File

@ -0,0 +1,29 @@
<template>
<div>
<Header />
<div class="container px-4">
<Menu />
<nuxt />
</div>
<Footer />
</div>
</template>
<script>
import Header from '@/components/layouts/Header'
import Footer from '@/components/layouts/Footer'
import Menu from '@/components/layouts/Menu'
export default {
components: {
Header,
Footer,
Menu,
},
}
</script>
<style scoped></style>

48
layouts/error.vue Normal file
View File

@ -0,0 +1,48 @@
<template>
<section class="hero is-light is-bold is-large">
<div class="container hero-body">
<transition name="slide-fade" appear>
<div class="columns is-vcentered">
<div class="column animate__animated animate__fadeIn animate__slow">
<b-image :src="require('@/assets/404.webp')" alt="404_image" />
</div>
<div class="column">
<h1 class="title">ERROR 404 LA PÁGINA NO HA SIDO ENCONTRADA</h1>
<b-button class="is-dark" outlined @click="regresar()">
Ir a la página principal
</b-button>
</div>
</div>
</transition>
</div>
</section>
</template>
<script>
export default {
methods: {
regresar() {
this.$router.push('/')
},
},
layout: 'login',
}
</script>
<style scoped>
.slide-fade-enter-active {
transition: all 2s ease;
}
.slide-fade-leave-active {
transition: all 0.8s cubic-bezier(1, 0.5, 0.8, 1);
}
.slide-fade-enter,
.slide-fade-leave-to {
transform: translateX(10px);
opacity: 0;
}
</style>

23
layouts/login.vue Normal file
View File

@ -0,0 +1,23 @@
<template>
<div>
<Header />
<nuxt />
<Footer />
</div>
</template>
<script>
import Header from '@/components/layouts/Header'
import Footer from '@/components/layouts/Footer'
export default {
components: {
Header,
Footer,
},
}
</script>
<style scoped></style>

6
nuxt.config.ts Normal file
View File

@ -0,0 +1,6 @@
import { defineNuxtConfig } from 'nuxt'
// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({
})

14013
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

16
package.json Normal file
View File

@ -0,0 +1,16 @@
{
"private": true,
"scripts": {
"build": "nuxt build",
"dev": "nuxt dev",
"generate": "nuxt generate",
"preview": "nuxt preview"
},
"devDependencies": {
"nuxt": "3.0.0-rc.3"
},
"dependencies": {
"axios": "^0.27.2",
"vue-qrcode-reader": "^3.1.0"
}
}

58
pages/index.vue Normal file
View File

@ -0,0 +1,58 @@
<template>
<section class="container">
<Login :imprimirError="imprimirError" :updateIsLoading="updateIsLoading" />
<b-loading :is-full-page="true" v-model="isLoading" :can-cancel="false" />
</section>
</template>
<script>
import Login from '@/components/Login'
export default {
components: { Login },
data() {
return {
isLoading: false,
}
},
methods: {
updateIsLoading(booleanValue) {
this.isLoading = booleanValue
},
imprimirError(err = {}, title = '¡Hubo un error!', onConfirm = () => {}) {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-danger',
title,
message: err.message,
confirmText: 'Entendido',
hasIcon: true,
iconPack: 'mdi',
icon: 'alert-octagon',
onConfirm: () => onConfirm(),
})
if (err.err && err.err === 'token error') {
localStorage.clear()
this.$router.push('/')
}
},
},
created() {
if (
localStorage.getItem('token') &&
localStorage.getItem('idOperador') &&
localStorage.getItem('idTipoUsuario') &&
localStorage.getItem('tipoUsuario') &&
localStorage.getItem('operador') &&
localStorage.getItem('idModulo')
)
this.$router.push('/prestamo_devolucion')
else localStorage.clear()
},
layout: 'login',
}
</script>
<style scoped></style>

101
pages/inicio/index.vue Normal file
View File

@ -0,0 +1,101 @@
<template>
<div>
<Title title="PC Puma" :operador="operador" />
<b-loading :is-full-page="true" v-model="isLoading" :can-cancel="false" />
</div>
</template>
<script>
import Title from '@/components/layouts/Title'
export default {
components: {
Title,
},
data() {
return {
operador: {},
isLoading: false,
}
},
methods: {
updateIsLoading(valorBooleano) {
this.isLoading = valorBooleano
},
imprimirError(err = {}, title = '¡Hubo un error!', onConfirm = () => {}) {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-danger',
title,
message: err.message,
confirmText: 'Entendido',
hasIcon: true,
iconPack: 'mdi',
icon: 'alert-octagon',
onConfirm,
})
if (err.err && err.err === 'token error') {
localStorage.clear()
this.$router.push('/')
}
},
imprimirMensaje(message, onConfirm = () => {}, title = '¡Felicidades!') {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-success',
title,
message,
confirmText: 'Ok',
hasIcon: true,
iconPack: 'mdi',
icon: 'check-circle',
onConfirm,
})
},
imprimirWarning(
message,
onConfirm = () => {},
title = '¡Espera un minuto!',
onCancel = () => {}
) {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-warning',
title,
message,
confirmText: 'Confirmar',
canCancel: true,
cancelText: 'Cancelar',
hasIcon: true,
iconPack: 'mdi',
icon: 'help-circle',
onConfirm,
onCancel,
})
},
getLocalhostInfo() {
this.operador.idOperador = localStorage.getItem('idOperador')
this.operador.operador = localStorage.getItem('operador')
this.operador.idModulo = Number(localStorage.getItem('idModulo'))
this.operador.tipoUsuario = localStorage.getItem('tipoUsuario')
this.operador.idTipoUsuario = Number(
localStorage.getItem('idTipoUsuario')
)
this.operador.token = {
headers: {
token: localStorage.getItem('token'),
},
}
},
},
created() {
this.getLocalhostInfo()
},
}
</script>
<style></style>

View File

@ -0,0 +1,124 @@
<template>
<div>
<Title title="Préstamo/Devolución" :operador="operador" />
<EntregaRegreso
:operador="operador"
:imprimirMensaje="imprimirMensaje"
:imprimirWarning="imprimirWarning"
:imprimirError="imprimirError"
:updateIsLoading="updateIsLoading"
/>
<hr />
<TablaPrestamosActivos
:operador="operador"
:imprimirError="imprimirError"
:imprimirWarning="imprimirWarning"
:imprimirMensaje="imprimirMensaje"
:updateIsLoading="updateIsLoading"
/>
<b-loading :is-full-page="true" v-model="isLoading" :can-cancel="false" />
</div>
</template>
<script>
import EntregaRegreso from '@/components/operador/EntregaRegreso'
import TablaPrestamosActivos from '@/components/operador/TablaPrestamosActivos'
import Title from '@/components/layouts/Title'
export default {
components: {
EntregaRegreso,
TablaPrestamosActivos,
Title,
},
data() {
return {
operador: {},
actualizarTabla: false,
isLoading: false,
}
},
methods: {
updateIsLoading(valorBooleano) {
this.isLoading = valorBooleano
},
imprimirError(err = {}, title = '¡Hubo un error!', onConfirm = () => {}) {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-danger',
title,
message: err.message,
confirmText: 'Entendido',
hasIcon: true,
iconPack: 'mdi',
icon: 'alert-octagon',
onConfirm,
})
if (err.err && err.err === 'token error') {
localStorage.clear()
this.$router.push('/')
}
},
imprimirMensaje(message, onConfirm = () => {}, title = '¡Felicidades!') {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-success',
title,
message,
confirmText: 'Ok',
hasIcon: true,
iconPack: 'mdi',
icon: 'check-circle',
onConfirm,
})
},
imprimirWarning(
message,
onConfirm = () => {},
title = '¡Espera un minuto!',
onCancel = () => {}
) {
this.$buefy.dialog.alert({
ariaRole: 'alertdialog',
ariaModal: true,
type: 'is-warning',
title,
message,
confirmText: 'Confirmar',
canCancel: true,
cancelText: 'Cancelar',
hasIcon: true,
iconPack: 'mdi',
icon: 'help-circle',
onConfirm,
onCancel,
})
},
getLocalhostInfo() {
this.operador.idOperador = localStorage.getItem('idOperador')
this.operador.operador = localStorage.getItem('operador')
this.operador.idModulo = Number(localStorage.getItem('idModulo'))
this.operador.tipoUsuario = localStorage.getItem('tipoUsuario')
this.operador.idTipoUsuario = Number(
localStorage.getItem('idTipoUsuario')
)
this.operador.token = {
headers: {
token: localStorage.getItem('token'),
},
}
},
},
created() {
this.getLocalhostInfo()
},
}
</script>
<style></style>

4
tsconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
// https://v3.nuxtjs.org/concepts/typescript
"extends": "./.nuxt/tsconfig.json"
}