# Iterator Bone
# Introducción
Éste componente no está encargado de renderizar, es de uso interno de la tabla. Guía de uso parte lógica
# Código fuente
<script>
const propsLocal = {
items: {
type: Array,
default: () => []
},
cache_items: {
type: Boolean,
default: true
},
filtros: {
type: Array,
default: () => []
},
filtros_fcn: {
type: String,
required: false
},
args_filtros_fcn: {
type: Array,
default: () => []
},
items_fcn: {
type: String,
required: false
},
args_items_fcn: {
type: Array,
default: () => []
},
rowsPerPage: {
type: Number,
default: 5
},
comportamiento: {
type: Object,
default: () => ({})
},
itemKey: {
type: String
},
id_resolver: {
type: String
},
type: {
type: String
},
columns: {
type: Number
},
columnsXs: {
type: Number,
default: 1
},
columnsSm: {
type: Number,
default: 2
},
columnsMd: {
type: Number,
default: 3
},
columnsLg: {
type: Number
},
columnsXl: {
type: Number
},
adaptPerPage: {
type: Boolean
},
hideDefaultFooter: {
type: Boolean,
default: false
},
hideDefaultHeader: {
type: Boolean,
default: true
},
colorHeader: {
type: String,
default: '#ffffff'
},
fullHeight: {
type: Boolean,
required: false
},
linked: {
type: Boolean,
default: false
}
}
import {
normalizeProps,
assignCamelToSnake
} from '../../helpers/propsGenerator'
import resolveMixin from '../../mixins/resolveIdResolver'
import {
fcn_executer,
iteratorComponentName,
navigator,
modal,
availableTableTypes,
externalDBEndpoint,
getCamposEndpoint
} from '../../constants'
import fcnResponseHandler from '../../mixins/fcnResponseHandler'
const mergedProps = normalizeProps(propsLocal)
import Vue, { ref, onMounted, h, watch, computed } from 'vue'
export default {
name: iteratorComponentName + 'Bone',
mixins: [fcnResponseHandler, resolveMixin],
props: mergedProps,
setup(props, { slots, emit }) {
const { handleError, handleSuccess, handleSuccessResponse } =
fcnResponseHandler.methods
const { resolve } = resolveMixin.methods
const iclAxios = Vue.prototype.$iclAxios
const iclstore = Vue.prototype.$iclstore
const iclRouter = Vue.prototype.$iclRouter
const detectResponseStructErrors = Vue.prototype.$detectResponseStructErrors
const logErrorComponent = Vue.prototype.$logErrorComponent
const propError = Vue.prototype.$propError
const itemsLocal = ref([])
const filtrosLocal = ref([])
const loading = ref(false)
const keys = ref([])
const dialogData = ref({ id_resolver_dlg: '', titulo: '', dialog_args: {} })
const comportamientoLocal = ref({})
const actionToConfirm = ref({})
const itemToConfirm = ref({})
const structureError = ref(false)
const computedIdResolver = computed(() => {
return props.idResolver
? props.idResolver
: props.id_resolver
})
// Watchers
watch(
() => props.filtros,
(newFiltros) => {
filtrosLocal.value = newFiltros
}
)
const getFiltros = async (fcn, args) => {
let argsAux = {
id_usuario: iclstore.state.id_usuario,
id_resolver: computedIdResolver.value
? computedIdResolver.value
: iclRouter.currentRoute.path,
...args
}
try {
const rsp = await iclAxios.post(getCamposEndpoint + fcn, [
JSON.stringify(argsAux)
])
return rsp
} catch (error) {
console.log(error)
logErrorComponent(
iteratorComponentName,
'Ocurrió un error en la consulta de los campos.',
iclRouter
)
return props.filtros
}
}
const getItems = async (fcn, filters, args) => {
loading.value = true
let params = {
filtros: filters,
args,
id_usuario: iclstore.state.id_usuario,
id_resolver: computedIdResolver.value
? computedIdResolver.value
: iclRouter.currentRoute.path
}
try {
let rsp = await iclAxios.post(externalDBEndpoint + fcn, [
JSON.stringify(params)
])
handleSuccess(rsp, () => {}, {
iclRouter,
iclstore,
emit,
handleSuccessResponse
})
if (Array.isArray(rsp)) {
detectResponseStructErrors(
rsp,
fcn,
[],
'IrDataCardBone',
'{ <propName>: Any }',
'https://icl.iridiumrobotics.com.ar/components/ir-data-table.html#uso'
)
} else {
structureError.value = propError(
propsLocal,
{ ...props, ...rsp },
iteratorComponentName
)
}
if (
rsp === null ||
rsp.length === 0 ||
(typeof rsp[0][fcn] !== 'undefined' && rsp[0][fcn] === null)
)
rsp = Object.assign([])
manageResponse(rsp)
setKeys()
} catch (error) {
handleError(error, iclstore)
console.log(error)
} finally {
loading.value = false
}
}
const manageResponse = (response) => {
if (Array.isArray(response)) {
itemsLocal.value = response
} else {
itemsLocal.value = response.items ? [...response.items] : []
if (response.value || response.items) {
emit('update-value', response.value)
}
}
}
const setKeys = () => {
let max = 0
let firstLongerRow = {}
itemsLocal.value.forEach((item) => {
let len = Object.keys(item).length
if (len > max) {
max = len
firstLongerRow = item
}
})
keys.value = Object.keys(firstLongerRow)
}
const checkIndexedFields = (id_resolver, items, id_columna) => {
let intervals = id_resolver.match(/#\d+#/g)
if (intervals !== null) {
intervals.forEach((interval) => {
let index = interval.slice(1, interval.length - 1)
id_resolver = id_resolver.replace(
interval,
typeof items[index] !== 'undefined'
? items[index][id_columna]
: 'undefined'
)
})
}
return id_resolver
}
const makeQuery = async (fcn, args) => {
let localArgs = [
JSON.stringify({
...args,
id_usuario: iclstore.state.id_usuario,
id_resolver: computedIdResolver.value
? computedIdResolver.value
: iclRouter.currentRoute.path
})
]
try {
const rsp = await iclAxios.post(externalDBEndpoint + fcn, localArgs)
handleSuccess(rsp, createdFunctions, {
iclRouter,
iclstore,
emit,
handleSuccessResponse
})
} catch (error) {
handleError(error, iclstore)
console.log(error)
}
}
const assignDialogData = (id_resolver, titulo, message, dialogAttrs) => {
dialogData.value = Object.assign({})
dialogData.value.id_resolver_dlg = id_resolver
dialogData.value.titulo = titulo
dialogData.value.message = message
dialogData.value = { ...dialogData.value, ...dialogAttrs }
}
const redirect = (path, query) => {
let pathAux = resolve(path, query)
if (pathAux.startsWith('https://') || pathAux.startsWith('http://')) {
window.open(pathAux, '_blank')
} else {
iclRouter.push({ path: pathAux })
}
}
const continueAction = (confirmation) => {
if (confirmation) {
delete actionToConfirm.value.confirmation
handleItemClick(itemToConfirm.value)
} else {
emit('close-dialog')
}
actionToConfirm.value = Object.assign({})
itemToConfirm.value = Object.assign({})
}
const isValidIdResolver = (item, comportamiento) => {
let { id_columna, id_resolver, type } = comportamiento
let idSelected = item[id_columna]
let id_resolver_aux
if (type !== fcn_executer) {
id_resolver_aux = resolve(
id_resolver,
Array.isArray(idSelected) ? idSelected : [idSelected]
)
if (!id_resolver_aux || id_resolver_aux === '')
console.error(
`No se ha enviado un id_resolver para el comportamiento ${type} de IrIterator`
)
return id_resolver_aux && id_resolver_aux !== ''
}
return true
}
const createdFunctions = async () => {
if (!(!!props.items_fcn && iclAxios)) {
handleMissingItemsFcn()
return
}
if (typeof props.filtros_fcn === 'undefined') {
filtrosLocal.value = props.filtros
} else {
let datosFiltros = iclstore.getters.getDatosSitios({
sitio: iclRouter.app._route.path,
query: props.filtros_fcn,
method: 'post',
params: props.args_filtros_fcn
})
if (datosFiltros !== null) {
// los busca en el store, si los encuentra los guarda en filtrosLocal hasta que se termine de hacer la consulta por los filtros
datosFiltros = Object.assign({}, JSON.parse(datosFiltros.response))
filtrosLocal.value = datosFiltros[0].props.campos
}
filtrosLocal.value = await getFiltros(
props.filtros_fcn,
props.args_filtros_fcn
)
}
const params = { filtros: filtrosLocal.value, args: props.args_items_fcn }
const datosSitio = iclstore.getters.getDatosSitios({
sitio: iclRouter.app._route.path,
query: props.items_fcn,
method: 'get',
params: [JSON.stringify(params)]
})
// los busca en el store, si los encuentra los guarda en itemsLocal hasta que se termine de hacer la consulta por los items
if (
datosSitio !== null &&
props.cache_items &&
datosSitio.response !== null
)
itemsLocal.value = [...datosSitio.response]
await getItems(props.items_fcn, filtrosLocal.value, props.args_items_fcn)
}
// Manejo de items_fcn faltante
const handleMissingItemsFcn = () => {
const isFiltrosValid =
typeof props.filtros_fcn !== 'undefined' || props.filtros.length > 0
const isItemsFcnValid =
typeof props.items_fcn !== 'undefined' ||
props.items_fcn !== null ||
props.items_fcn !== ''
if (isFiltrosValid && !isItemsFcnValid) {
logErrorComponent(
iteratorComponentName,
'Se quiso renderizar un formulario de filtros pero no se detalló alguna función en la propiedad "items_fcn".',
iclRouter
)
}
itemsLocal.value = props.items
setKeys()
}
const checkExistence = (arr, element, id = undefined) => {
// chequea que 'element' se encuentre dentro de 'arr'
return (
typeof arr.find((el) => {
return typeof id === 'undefined'
? el === element
: el[id] === element[id]
}) !== 'undefined'
)
}
const validateComportamiento = () => {
if (Object.keys(props.comportamiento).length !== 0) {
// valida que el objeto no esté vacío
let tipo = props.comportamiento.type
if (tipo === '') {
logErrorComponent(
iteratorComponentName,
'La propiedad "type" del comportamiento se envió vacía.',
iclRouter
)
return
} else if (typeof tipo === 'undefined') {
logErrorComponent(
iteratorComponentName,
'La propiedad "type" del comportamiento no se envió.',
iclRouter
)
return
}
let active = !(
typeof props.comportamiento.active === 'boolean' &&
!props.comportamiento.active
)
if (active && checkExistence(availableTableTypes, tipo))
comportamientoLocal.value = props.comportamiento
else if (!active)
logErrorComponent(
iteratorComponentName,
'La propiedad "type" del comportamiento no coincide con un tipo de comportamiento valido.',
iclRouter
)
}
}
const handleItemClick = (item) => {
let {
type,
fcn_click,
id_columna,
id_resolver,
args_fcn_click,
confirmation,
titulo,
dialogAttrs
} = actionToConfirm.value.type
? actionToConfirm.value
: comportamientoLocal.value
if (confirmation === undefined || confirmation === '') {
let idSelected = item[id_columna]
let argsLocal, id_resolver_aux
if (type === fcn_executer) {
argsLocal = { ...args_fcn_click }
if (idSelected !== undefined)
argsLocal = { ...argsLocal, selected_rows: [idSelected] }
makeQuery(fcn_click, argsLocal)
} else if (type === modal) {
id_resolver = checkIndexedFields(
id_resolver,
itemsLocal.value,
id_columna
)
if (idSelected !== undefined)
id_resolver_aux = resolve(
id_resolver,
Array.isArray(idSelected) ? idSelected : [idSelected]
)
// Si el id_resolver_aux es una url externa, se abre en nueva pestaña
if (
id_resolver_aux.startsWith('https://') ||
id_resolver_aux.startsWith('http://')
) {
window.open(id_resolver_aux, '_blank')
emit('close-dialog')
} else {
emit('open-dialog')
assignDialogData(id_resolver_aux, titulo, undefined, dialogAttrs)
}
} else if (type === navigator) {
id_resolver = checkIndexedFields(
id_resolver,
itemsLocal.value,
id_columna
)
redirect(
id_resolver,
Array.isArray(idSelected) ? idSelected : [idSelected]
)
}
} else {
actionToConfirm.value = Object.assign({}, comportamientoLocal.value)
itemToConfirm.value = Object.assign({}, item)
assignDialogData(undefined, undefined, confirmation)
emit('open-dialog')
}
}
onMounted(() => {
structureError.value = propError(propsLocal, props, iteratorComponentName)
if (structureError.value) {
iclstore.commit('updateSnack', {
text: 'Ha ocurrido un error al cargar el componente.',
active: true,
color: 'warning'
})
}
})
const itemsPerPage = ref(-1)
assignCamelToSnake(mergedProps, props)
itemsPerPage.value = props.rowsPerPage
validateComportamiento()
createdFunctions()
return () => {
if (structureError.value) return
return h(
'div', // Contenedor como nodo raíz
[
slots.default({
items: itemsLocal.value,
itemsPerPage: itemsPerPage.value,
keys: keys.value,
id_resolver_dlg: dialogData.value.id_resolver_dlg,
tituloDialog: dialogData.value.titulo,
dialogAttrs: dialogData.value,
message: dialogData.value.message,
filtros: filtrosLocal.value,
submitForm: {
click: () => {
getItems(
props.items_fcn,
filtrosLocal.value,
props.args_items_fcn
)
}
},
comportamiento: comportamientoLocal.value,
id_resolver: computedIdResolver.value,
handleItemClick: handleItemClick,
type: props.type,
fullWidth: props.fullWidth,
columns: {
xs: props.columnsXs,
sm: props.columnsSm,
md: props.columnsMd,
lg: props.columnsLg,
xl: props.columnsXl,
default: props.columns
},
isValidIdResolver: isValidIdResolver,
continueAction: continueAction,
adaptPerPage: props.adaptPerPage,
hideDefaultFooter: props.hideDefaultFooter,
hideDefaultHeader: props.hideDefaultHeader,
colorHeader: props.colorHeader,
refreshParent: createdFunctions,
fullHeight: props.fullHeight,
isLoading: loading.value,
linked: props.linked
})
]
)
}
}
}
</script>