Items view
This commit is contained in:
parent
47c61a7b35
commit
e97aa4b2a4
|
@ -0,0 +1,119 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
|
||||||
|
import { fetch } from 'src/composables/serviceUtils';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
searchFn: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
url: {
|
||||||
|
type: String,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
searchField: {
|
||||||
|
type: String,
|
||||||
|
default: 'search'
|
||||||
|
},
|
||||||
|
exprBuilder: {
|
||||||
|
type: Function,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(['onSearch', 'onSearchError']);
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const searchTerm = ref('');
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
try {
|
||||||
|
router.replace({
|
||||||
|
query: searchTerm.value ? { search: searchTerm.value } : {}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!searchTerm.value) {
|
||||||
|
emit('onSearchError');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (props.url) {
|
||||||
|
const params = {
|
||||||
|
filter: props.filter,
|
||||||
|
[props.searchField]: searchTerm.value
|
||||||
|
};
|
||||||
|
|
||||||
|
const { data } = await fetch({
|
||||||
|
url: props.url,
|
||||||
|
params,
|
||||||
|
exprBuilder: props.exprBuilder
|
||||||
|
});
|
||||||
|
emit('onSearch', data);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error searching:', error);
|
||||||
|
emit('onSearchError');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (route.query.search) {
|
||||||
|
searchTerm.value = route.query.search;
|
||||||
|
search();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnInput
|
||||||
|
v-model="searchTerm"
|
||||||
|
@keyup.enter="search()"
|
||||||
|
:placeholder="props.placeholder || t('search')"
|
||||||
|
bg-color="white"
|
||||||
|
is-outlined
|
||||||
|
:clearable="false"
|
||||||
|
class="searchbar"
|
||||||
|
data-cy="searchBar"
|
||||||
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<QIcon name="search" class="cursor-pointer" @click="search()" />
|
||||||
|
</template>
|
||||||
|
</VnInput>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import 'src/css/responsive';
|
||||||
|
|
||||||
|
.searchbar {
|
||||||
|
@include mobile {
|
||||||
|
max-width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n lang="yaml">
|
||||||
|
en-US:
|
||||||
|
search: Search
|
||||||
|
es-ES:
|
||||||
|
search: Buscar
|
||||||
|
ca-ES:
|
||||||
|
search: Cercar
|
||||||
|
fr-FR:
|
||||||
|
search: Rechercher
|
||||||
|
pt-PT:
|
||||||
|
search: Pesquisar
|
||||||
|
</i18n>
|
|
@ -0,0 +1,116 @@
|
||||||
|
import { api } from '@/boot/axios';
|
||||||
|
|
||||||
|
function buildFilter(params, builderFunc) {
|
||||||
|
let and = [];
|
||||||
|
|
||||||
|
for (let param in params) {
|
||||||
|
let value = params[param];
|
||||||
|
if (value == null) continue;
|
||||||
|
let expr = builderFunc(param, value);
|
||||||
|
if (expr) and.push(expr);
|
||||||
|
}
|
||||||
|
return simplifyOperation(and, 'and');
|
||||||
|
}
|
||||||
|
|
||||||
|
const simplifyOperation = (operation, operator) => {
|
||||||
|
switch (operation.length) {
|
||||||
|
case 0:
|
||||||
|
return undefined;
|
||||||
|
case 1:
|
||||||
|
return operation[0];
|
||||||
|
default:
|
||||||
|
return { [operator]: operation };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function fetch({
|
||||||
|
url,
|
||||||
|
append = false,
|
||||||
|
params,
|
||||||
|
exprBuilder,
|
||||||
|
mapKey,
|
||||||
|
existingData = [],
|
||||||
|
existingMap = new Map(),
|
||||||
|
oneRecord = false
|
||||||
|
}) {
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
let exprFilter;
|
||||||
|
if (exprBuilder) {
|
||||||
|
exprFilter = buildFilter(params, (param, value) => {
|
||||||
|
if (param === 'filter') return;
|
||||||
|
const res = exprBuilder(param, value);
|
||||||
|
if (res) delete params[param];
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.filter?.where || exprFilter) {
|
||||||
|
params.filter.where = { ...params.filter.where, ...exprFilter };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params?.filter?.order?.length) {
|
||||||
|
delete params?.filter?.order;
|
||||||
|
}
|
||||||
|
|
||||||
|
params.filter = JSON.stringify(params.filter);
|
||||||
|
|
||||||
|
const response = await api.get(url, { params });
|
||||||
|
const processedData = processData(response.data, {
|
||||||
|
mapKey,
|
||||||
|
map: !!mapKey,
|
||||||
|
append,
|
||||||
|
oneRecord,
|
||||||
|
existingData,
|
||||||
|
existingMap
|
||||||
|
});
|
||||||
|
|
||||||
|
return { response, data: processedData.data, map: processedData.map };
|
||||||
|
}
|
||||||
|
|
||||||
|
function processData(data, options) {
|
||||||
|
const {
|
||||||
|
mapKey,
|
||||||
|
map = true,
|
||||||
|
append = true,
|
||||||
|
oneRecord = false,
|
||||||
|
existingData = [],
|
||||||
|
existingMap = new Map()
|
||||||
|
} = options;
|
||||||
|
let resultData = [...existingData];
|
||||||
|
let resultMap = new Map(existingMap);
|
||||||
|
|
||||||
|
if (oneRecord) {
|
||||||
|
return Array.isArray(data) ? data[0] : data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!append) {
|
||||||
|
resultData = [];
|
||||||
|
resultMap = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(data)) {
|
||||||
|
resultData = data;
|
||||||
|
} else if (!map && append) {
|
||||||
|
resultData.push(...data);
|
||||||
|
} else {
|
||||||
|
for (const row of data) {
|
||||||
|
const key = row[mapKey];
|
||||||
|
const val = { ...row, key };
|
||||||
|
if (key && resultMap.has(key)) {
|
||||||
|
const { position } = resultMap.get(key);
|
||||||
|
val.position = position;
|
||||||
|
resultMap.set(key, val);
|
||||||
|
resultData[position] = val;
|
||||||
|
} else {
|
||||||
|
val.position = resultMap.size;
|
||||||
|
resultMap.set(key, val);
|
||||||
|
resultData.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { data: resultData, map: resultMap };
|
||||||
|
}
|
||||||
|
|
||||||
|
export { buildFilter, fetch, processData };
|
|
@ -3,28 +3,47 @@ import { ref } from 'vue';
|
||||||
|
|
||||||
import CardList from 'src/components/ui/CardList.vue';
|
import CardList from 'src/components/ui/CardList.vue';
|
||||||
import VnImg from 'src/components/ui/VnImg.vue';
|
import VnImg from 'src/components/ui/VnImg.vue';
|
||||||
import VnSearchBar from 'src/components/ui/VnSearchBar.vue';
|
|
||||||
import VnList from 'src/components/ui/VnList.vue';
|
import VnList from 'src/components/ui/VnList.vue';
|
||||||
|
import VnSearchBar from 'src/components/ui/NewVnSearchBar.vue';
|
||||||
|
|
||||||
import { useAppStore } from 'stores/app';
|
import { useAppStore } from 'stores/app';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
const appStore = useAppStore();
|
const appStore = useAppStore();
|
||||||
const { isHeaderMounted } = storeToRefs(appStore);
|
const { isHeaderMounted } = storeToRefs(appStore);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const items = ref([]);
|
const items = ref([]);
|
||||||
|
const itemFilterProps = {
|
||||||
const query = `SELECT i.id, i.longName, i.size, i.category,
|
fields: ['id', 'name', 'longName', 'description', 'isActive'], // Incluye explícitamente 'longName'
|
||||||
i.value5, i.value6, i.value7,
|
include: [
|
||||||
i.image, im.updated
|
{
|
||||||
FROM vn.item i
|
relation: 'itemType',
|
||||||
LEFT JOIN image im
|
scope: {
|
||||||
ON im.collectionFk = 'catalog'
|
fields: ['id', 'name']
|
||||||
AND im.name = i.image
|
}
|
||||||
WHERE (i.longName LIKE CONCAT('%', #search, '%')
|
},
|
||||||
OR i.id = #search) AND i.isActive
|
{
|
||||||
ORDER BY i.longName LIMIT 50`;
|
relation: 'intrastat',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'description']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'origin',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'production',
|
||||||
|
scope: {
|
||||||
|
fields: ['id', 'name']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
isActive: true,
|
||||||
|
order: 'name ASC'
|
||||||
|
};
|
||||||
|
|
||||||
const onSearch = data => (items.value = data || []);
|
const onSearch = data => (items.value = data || []);
|
||||||
</script>
|
</script>
|
||||||
|
@ -32,9 +51,10 @@ const onSearch = data => (items.value = data || []);
|
||||||
<template>
|
<template>
|
||||||
<Teleport v-if="isHeaderMounted" to="#actions">
|
<Teleport v-if="isHeaderMounted" to="#actions">
|
||||||
<VnSearchBar
|
<VnSearchBar
|
||||||
:sql-query="query"
|
url="Items/filter"
|
||||||
@on-search="onSearch"
|
@on-search="onSearch"
|
||||||
@on-search-error="items = []"
|
@on-search-error="items = []"
|
||||||
|
:filter="itemFilterProps"
|
||||||
/>
|
/>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
<QPage class="vn-w-xs">
|
<QPage class="vn-w-xs">
|
||||||
|
@ -68,7 +88,7 @@ const onSearch = data => (items.value = data || []);
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<span class="text-bold q-mb-sm">
|
<span class="text-bold q-mb-sm">
|
||||||
{{ item.longName }}
|
{{ item.name }}
|
||||||
</span>
|
</span>
|
||||||
<span>
|
<span>
|
||||||
{{ item.value5 }} {{ item.value6 }}
|
{{ item.value5 }} {{ item.value6 }}
|
||||||
|
|
Loading…
Reference in New Issue