0
0
Fork 0

fea(VnTable)t: refs #6825 create VnComponent, add rowClick and redirect

This commit is contained in:
Alex Moreno 2024-05-13 14:16:01 +02:00
parent 4cb5b8c326
commit c223bbe0ea
7 changed files with 252 additions and 188 deletions

View File

@ -0,0 +1,36 @@
<template>
<span v-for="toComponent of componentArray" :key="toComponent.field">
<component
v-if="toComponent?.component"
:is="
(components && components[toComponent.component]) ?? toComponent.component
"
v-bind="toComponent.props && toComponent.props(value)"
@click="toComponent.event && toComponent.event(value)"
/>
</span>
</template>
<script setup>
import { computed } from 'vue';
const $props = defineProps({
prop: {
type: Object,
required: true,
},
components: {
type: Object,
default: () => {},
required: false,
},
value: {
type: Object,
default: () => {},
required: false,
},
});
const componentArray = computed(() => {
if (typeof $props.prop === 'object') return [$props.prop];
return $props.prop;
});
</script>

View File

@ -108,17 +108,12 @@ async function fetchFilter(val) {
const { fields, sortBy, limit } = $props; const { fields, sortBy, limit } = $props;
let key = optionLabel.value; let key = optionLabel.value;
console.log('key: ', key);
if (new RegExp(/\d/g).test(val)) key = optionValue.value; if (new RegExp(/\d/g).test(val)) key = optionValue.value;
console.log('key: ', key);
const where = { [key]: { like: `%${val}%` } }; const where = { [key]: { like: `%${val}%` } };
const fetchOptions = { where, order: sortBy, limit }; const fetchOptions = { where, order: sortBy, limit };
console.log('where: ', where);
console.log('fields: ', fields);
if (fields) fetchOptions.fields = fields; if (fields) fetchOptions.fields = fields;
console.log('fetchOptions: ', fetchOptions);
return dataRef.value.fetch(fetchOptions); return dataRef.value.fetch(fetchOptions);
} }

View File

@ -1,15 +1,13 @@
<script setup> <script setup>
import { ref, onMounted, computed } from 'vue'; import { ref, onMounted, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import VnPaginate from 'components/ui/VnPaginate.vue'; import VnPaginate from 'components/ui/VnPaginate.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue'; import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnLv from 'components/ui/VnLv.vue'; import VnLv from 'components/ui/VnLv.vue';
import VnBreadcrumbs from 'components/common/VnBreadcrumbs.vue';
import VnTableColumn from 'components/common/VnTableColumn.vue'; import VnTableColumn from 'components/common/VnTableColumn.vue';
import VnTableFilter from 'components/common/VnTableFilter.vue'; import VnTableFilter from 'components/common/VnTableFilter.vue';
@ -24,7 +22,7 @@ const $props = defineProps({
}, },
defaultMode: { defaultMode: {
type: String, type: String,
default: 'list', // 'table', 'list', 'card' default: 'card', // 'table', 'card'
}, },
reponsive: { reponsive: {
type: Boolean, type: Boolean,
@ -38,49 +36,71 @@ const $props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
rowClick: {
type: Function,
default: null,
},
redirect: {
type: String,
default: null,
},
}); });
const { t } = useI18n(); const { t } = useI18n();
const $q = useQuasar();
const stateStore = useStateStore(); const stateStore = useStateStore();
const router = useRouter();
const mode = ref('list'); const mode = ref('card');
const selected = ref([]); const selected = ref([]);
const params = ref({}); const params = ref({});
const VnPaginateRef = ref(); const VnPaginateRef = ref();
const tableModes = computed(() => { const tableModes = [
const modes = [
{ {
icon: 'view_column', icon: 'view_column',
title: t('table view'), title: t('table view'),
value: 'table', value: 'table',
}, },
{ {
icon: 'lists', icon: 'grid_view',
title: t('list view'),
value: 'list',
},
];
if (!$q.screen.lt.md)
modes.push({
icon: 'apps',
title: t('grid view'), title: t('grid view'),
value: 'card', value: 'card',
disable: () => console.log('called'), disable: () => console.log('called'),
}); },
];
return modes;
});
onMounted(() => { onMounted(() => {
mode.value = $props.defaultMode; mode.value = $props.defaultMode;
stateStore.rightDrawer = true; stateStore.rightDrawer = true;
}); });
function columnsCard(cols) { const cardTemplate = computed(() => {
return Object.values(cols).filter( let chips = [];
(col) => col.cardVisible && !col.isId && col.field != 'tableActions' let visible = [];
); let title;
let actions;
for (const col of $props.columns) {
if (col.field == 'tableActions') {
actions = col;
continue;
} }
if (col.chip) {
chips.push(col);
continue;
}
if (col.isTitle) {
title = col;
continue;
}
if (col.cardVisible) visible.push(col);
}
return { chips, title, visible, actions };
});
const rowClickFunction = computed(() => {
if ($props.rowClick) return $props.rowClick;
if ($props.redirect)
return ({ id }) => router.push({ path: `/${$props.redirect}/${id}` });
return null;
});
</script> </script>
<template> <template>
<QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
@ -90,6 +110,7 @@ function columnsCard(cols) {
:data-key="$attrs['data-key']" :data-key="$attrs['data-key']"
:search-button="true" :search-button="true"
v-model:params="params" v-model:params="params"
@remove="(key) => console.log('HA LELGADO', key)"
> >
<template #body> <template #body>
<VnTableFilter <VnTableFilter
@ -116,23 +137,22 @@ function columnsCard(cols) {
class="vnTable" class="vnTable"
:columns="columns" :columns="columns"
:rows="rows" :rows="rows"
row-key="$index" row-key="id"
selection="multiple" selection="multiple"
v-model:selected="selected" v-model:selected="selected"
:grid="mode != 'table'" :grid="mode != 'table'"
table-header-class="bg-grey-8" table-header-class="bg-grey-8"
:card-container-class=" card-container-class="grid-three"
mode == 'list' || $q.screen.lt.md ? 'grid-one' : 'grid-three'
"
flat flat
:style="mode == 'table' && 'max-height: 92vh'" :style="mode == 'table' && 'max-height: 92vh'"
virtual-scroll virtual-scroll
@virtual-scroll=" @virtual-scroll="
(event) => event.index > rows.length - 2 && VnPaginateRef.paginate() (event) => event.index > rows.length - 2 && VnPaginateRef.paginate()
" "
@row-click="(_, row) => rowClickFunction(row)"
> >
<template #top-left> <template #top-left>
<VnBreadcrumbs /> <slot name="top-left"></slot>
</template> </template>
<template #top-right> <template #top-right>
<QBtn <QBtn
@ -175,47 +195,75 @@ function columnsCard(cols) {
</QTd> </QTd>
</template> </template>
<template #item="{ row, colsMap }"> <template #item="{ row, colsMap }">
<!-- <TransitionGroup name="grid" tag="div" class="grid"> --> <component
<QCard bordered flat class="row no-wrap justify-between"> :is="$props.redirect ? 'router-link' : 'span'"
<QCardSection vertical class="no-margin no-padding full-width"> :to="`/${$props.redirect}/` + row.id"
<!-- Chips -->
<QCardSection horizontal>
<span v-for="col of colsMap" :key="col.field">
<QChip
:class="
col.color
? col.color(row)
: col.isId == 1
? 'bg-primary-light'
: 'bg-chip-secondary'
"
square
v-if="col.isId"
:title="t(col.label)"
class="no-margin"
> >
{{ row[col.field] }} <QCard
bordered
flat
class="row no-wrap justify-between cursor-pointer"
@row-click="
(_, row) => {
$props.rowClick && $props.rowClick(row);
}
"
>
<QCardSection
vertical
class="no-margin no-padding full-width"
>
<!-- Chips -->
<QCardSection
v-if="cardTemplate.chips.length"
class="no-margin q-px-xs q-py-none"
>
<span
v-for="col of cardTemplate.chips"
:key="col.field"
>
<QChip
v-if="col.chip.condition(row[col.field], row)"
:title="t(col.label)"
:class="
col.chip.color
? col.chip.color(row)
: !col.chip.icon &&
'bg-chip-secondary'
"
dense
square
>
<span v-if="!col.chip.icon">{{
row[col.field]
}}</span>
<QIcon
v-else
:name="col.chip.icon"
color="primary-light"
/>
</QChip> </QChip>
</span> </span>
<slot name="afterChip" :row="row"></slot>
</QCardSection>
<!-- Title -->
<QCardSection
v-if="cardTemplate.title"
class="q-pl-sm q-py-none text-primary-light text-bold text-h6"
>
{{ row[cardTemplate.title.field] }}
</QCardSection> </QCardSection>
<!-- Fields --> <!-- Fields -->
<QCardSection <QCardSection class="q-pl-sm q-pr-lg q-py-xs flex-one">
class="q-pa-sm q-pr-lg"
:class="
$q.screen.lt.md || mode == 'card'
? 'flex-one'
: 'flex-three'
"
>
<div <div
v-for="col of columnsCard(colsMap)" v-for="col of cardTemplate.visible"
:key="col.field" :key="col.field"
class="fields" class="fields"
> >
<VnLv <VnLv
v-if=" v-if="
col.cardVisible && col.cardVisible &&
!col.isId && !col.isChip &&
col.field != 'tableActions' col.field != 'tableActions'
" "
:label="`${col.label}:`" :label="`${col.label}:`"
@ -237,15 +285,14 @@ function columnsCard(cols) {
:key="index" :key="index"
:title="btn.title" :title="btn.title"
:icon="btn.icon" :icon="btn.icon"
:outline="!btn.isPrimary" class="q-pa-xs"
size="sm" flat
class="q-pa-sm" :class="{ 'text-primary-light': btn.isPrimary }"
:class="{ 'bg-primary-light': btn.isPrimary }" @click.prevent="btn.action(row)"
@click.stop="btn.action(row)"
/> />
</QCardSection> </QCardSection>
</QCard> </QCard>
<!-- </TransitionGroup> --> </component>
</template> </template>
</QTable> </QTable>
</template> </template>
@ -262,7 +309,7 @@ function columnsCard(cols) {
.bg-chip-secondary { .bg-chip-secondary {
background-color: var(--vn-page-color); background-color: var(--vn-page-color);
color: var(--vn-text-colo); color: var(--vn-text-color);
} }
.text-primary-light { .text-primary-light {
color: var(--primary-light); color: var(--primary-light);
@ -290,7 +337,7 @@ function columnsCard(cols) {
/* Works on Firefox */ /* Works on Firefox */
* { * {
scrollbar-width: thin; scrollbar-width: thin;
scrollbar-color: var(--primary-light) transparent; scrollbar-color: grey transparent;
} }
/* Works on Chrome, Edge, and Safari */ /* Works on Chrome, Edge, and Safari */
@ -305,52 +352,17 @@ function columnsCard(cols) {
*::-webkit-scrollbar-thumb { *::-webkit-scrollbar-thumb {
background-color: transparent; background-color: transparent;
border-radius: 20px; border-radius: 20px;
border: 3px solid var(--primary-light); border: 3px solid grey;
}
.grid-one {
display: grid;
grid-template-columns: repeat(1, 1fr);
grid-gap: 10px;
} }
.grid-three { .grid-three {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1px)); grid-template-columns: repeat(auto-fit, minmax(350px, max-content));
max-width: 100%; max-width: 100%;
grid-gap: 12px; grid-gap: 20px;
margin: 0 auto; margin: 0 auto;
} }
.flex-three {
display: flex;
justify-content: space-between;
align-items: center;
div.fields {
width: 50%;
.vn-label-value {
display: flex;
justify-content: flex-start;
gap: 2%;
width: 100%;
.label {
width: 25%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
flex-grow: 0;
flex-shrink: 0;
}
.value {
width: 65%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
}
}
.flex-one { .flex-one {
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;

View File

@ -1,11 +1,23 @@
<template> <template>
<component <VnComponent
v-if="column.before"
:prop="column.before"
:components="components"
:value="row"
/>
<VnComponent
v-if="column.component" v-if="column.component"
:is="components[column.component] ?? column.component" :prop="column"
v-bind="column.props && column.props(row)" :components="components"
@click="column.event && column.event(row)" :value="row"
/> />
<span v-else>{{ dashIfEmpty(row[column.field]) }}</span> <span v-else>{{ dashIfEmpty(row[column.field]) }}</span>
<VnComponent
v-if="column.after"
:prop="column.after"
:components="components"
:value="row"
/>
</template> </template>
<script setup> <script setup>
import { markRaw } from 'vue'; import { markRaw } from 'vue';
@ -13,9 +25,10 @@ import { QIcon, QCheckbox } from 'quasar';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
/* basic input */ /* basic input */
import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'src/components/common/VnInput.vue'; import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnInputDate from 'components/common/VnInputDate.vue';
import VnComponent from 'components/common/VnComponent.vue';
const components = { const components = {
input: markRaw(VnInput), input: markRaw(VnInput),

View File

@ -1,4 +1,5 @@
<template> <template>
{{ model }}
<div <div
v-if="showTitle && column" v-if="showTitle && column"
class="q-pt-sm q-px-sm ellipsis" class="q-pt-sm q-px-sm ellipsis"
@ -69,7 +70,7 @@ const isBasicComponent = computed(
); );
const updateEvent = { 'update:modelValue': addFilter }; const updateEvent = { 'update:modelValue': addFilter };
const enterEvent = { 'keyup.enter': addFilter }; const enterEvent = { 'keyup.enter': () => addFilter(model.value) };
const components = { const components = {
input: { input: {
component: markRaw(VnInput), component: markRaw(VnInput),
@ -111,11 +112,10 @@ const components = {
// await arrayData.addFilter(filter); // await arrayData.addFilter(filter);
// } // }
async function addFilter(event) { async function addFilter(value) {
let value = model.value == '' ? null : model.value; if (value && typeof value === 'object') value = model.value;
if (event && typeof event !== 'object') { console.log('VALUE: ', value);
value = event; value = value == '' ? null : value;
}
let field = columnFilter.value?.field ?? $props.column.field; let field = columnFilter.value?.field ?? $props.column.field;
const toFilter = { [field]: value }; const toFilter = { [field]: value };

View File

@ -8,6 +8,7 @@ import toDate from 'filters/toDate';
import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue'; import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
const { t } = useI18n(); const { t } = useI18n();
const params = defineModel('params');
const props = defineProps({ const props = defineProps({
dataKey: { dataKey: {
type: String, type: String,
@ -18,11 +19,6 @@ const props = defineProps({
required: false, required: false,
default: false, default: false,
}, },
params: {
type: Object,
required: false,
default: null,
},
showAll: { showAll: {
type: Boolean, type: Boolean,
default: true, default: true,
@ -58,12 +54,10 @@ const store = arrayData.store;
const userParams = ref({}); const userParams = ref({});
onMounted(() => { onMounted(() => {
if (props.params) userParams.value = JSON.parse(JSON.stringify(props.params)); if (params.value) userParams.value = JSON.parse(JSON.stringify(params.value));
if (Object.keys(store.userParams).length > 0) { if (Object.keys(store.userParams).length > 0) {
userParams.value = JSON.parse(JSON.stringify(store.userParams)); userParams.value = JSON.parse(JSON.stringify(store.userParams));
} }
console.log('userParams.value: ', userParams.value);
emit('init', { params: userParams.value }); emit('init', { params: userParams.value });
}); });
@ -168,6 +162,9 @@ const customTags = computed(() =>
async function remove(key) { async function remove(key) {
userParams.value[key] = null; userParams.value[key] = null;
delete params.value[key];
console.log('key: ', key);
console.log('params: ', params.value);
await arrayData.applyFilter({ params: userParams.value }); await arrayData.applyFilter({ params: userParams.value });
emit('remove', key); emit('remove', key);
} }

View File

@ -10,6 +10,8 @@ import { QBtn, QIcon } from 'quasar';
import CustomerExtendedListActions from './CustomerExtendedListActions.vue'; import CustomerExtendedListActions from './CustomerExtendedListActions.vue';
import CustomerExtendedListFilter from './CustomerExtendedListFilter.vue'; import CustomerExtendedListFilter from './CustomerExtendedListFilter.vue';
import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { toDate } from 'src/filters'; import { toDate } from 'src/filters';
@ -272,7 +274,9 @@ const columns = computed(() => [
field: 'id', field: 'id',
label: t('customer.extendedList.tableVisibleColumns.id'), label: t('customer.extendedList.tableVisibleColumns.id'),
name: 'id', name: 'id',
isId: 1, chip: {
condition: () => true,
},
columnFilter: { columnFilter: {
field: 'search', field: 'search',
component: 'select', component: 'select',
@ -287,7 +291,7 @@ const columns = computed(() => [
field: 'name', field: 'name',
label: t('customer.extendedList.tableVisibleColumns.name'), label: t('customer.extendedList.tableVisibleColumns.name'),
name: 'name', name: 'name',
isId: 2, isTitle: true,
}, },
{ {
align: 'left', align: 'left',
@ -319,6 +323,12 @@ const columns = computed(() => [
label: t('customer.extendedList.tableVisibleColumns.phone'), label: t('customer.extendedList.tableVisibleColumns.phone'),
name: 'phone', name: 'phone',
cardVisible: true, cardVisible: true,
after: {
component: VnLinkPhone,
props: (prop) => ({
'phone-number': prop.phone,
}),
},
}, },
{ {
align: 'left', align: 'left',
@ -400,77 +410,76 @@ const columns = computed(() => [
field: 'isActive', field: 'isActive',
label: t('customer.extendedList.tableVisibleColumns.isActive'), label: t('customer.extendedList.tableVisibleColumns.isActive'),
name: 'isActive', name: 'isActive',
format: () => ' ', chip: {
color: null,
condition: (value) => !value,
icon: 'vn:disabled',
},
}, },
{ {
align: 'left', align: 'left',
field: 'isVies', field: 'isVies',
label: t('customer.extendedList.tableVisibleColumns.isVies'), label: t('customer.extendedList.tableVisibleColumns.isVies'),
name: 'isVies', name: 'isVies',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'isTaxDataChecked', field: 'isTaxDataChecked',
label: t('customer.extendedList.tableVisibleColumns.isTaxDataChecked'), label: t('customer.extendedList.tableVisibleColumns.isTaxDataChecked'),
name: 'isTaxDataChecked', name: 'isTaxDataChecked',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'isEqualizated', field: 'isEqualizated',
label: t('customer.extendedList.tableVisibleColumns.isEqualizated'), label: t('customer.extendedList.tableVisibleColumns.isEqualizated'),
name: 'isEqualizated', name: 'isEqualizated',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'isFreezed', field: 'isFreezed',
label: t('customer.extendedList.tableVisibleColumns.isFreezed'), label: t('customer.extendedList.tableVisibleColumns.isFreezed'),
name: 'isFreezed', name: 'isFreezed',
format: () => ' ', chip: {
color: null,
condition: (v, { isActive, isFreezed }) => isActive && isFreezed == true,
icon: 'vn:frozen',
},
}, },
{ {
align: 'left', align: 'left',
field: 'hasToInvoice', field: 'hasToInvoice',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoice'), label: t('customer.extendedList.tableVisibleColumns.hasToInvoice'),
name: 'hasToInvoice', name: 'hasToInvoice',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'hasToInvoiceByAddress', field: 'hasToInvoiceByAddress',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoiceByAddress'), label: t('customer.extendedList.tableVisibleColumns.hasToInvoiceByAddress'),
name: 'hasToInvoiceByAddress', name: 'hasToInvoiceByAddress',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'isToBeMailed', field: 'isToBeMailed',
label: t('customer.extendedList.tableVisibleColumns.isToBeMailed'), label: t('customer.extendedList.tableVisibleColumns.isToBeMailed'),
name: 'isToBeMailed', name: 'isToBeMailed',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'hasLcr', field: 'hasLcr',
label: t('customer.extendedList.tableVisibleColumns.hasLcr'), label: t('customer.extendedList.tableVisibleColumns.hasLcr'),
name: 'hasLcr', name: 'hasLcr',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'hasCoreVnl', field: 'hasCoreVnl',
label: t('customer.extendedList.tableVisibleColumns.hasCoreVnl'), label: t('customer.extendedList.tableVisibleColumns.hasCoreVnl'),
name: 'hasCoreVnl', name: 'hasCoreVnl',
format: () => ' ',
}, },
{ {
align: 'left', align: 'left',
field: 'hasSepaVnl', field: 'hasSepaVnl',
label: t('customer.extendedList.tableVisibleColumns.hasSepaVnl'), label: t('customer.extendedList.tableVisibleColumns.hasSepaVnl'),
name: 'hasSepaVnl', name: 'hasSepaVnl',
format: () => ' ',
}, },
{ {
align: 'right', align: 'right',
@ -534,7 +543,9 @@ const selectSalesPersonId = (id) => (selectedSalesPersonId.value = id);
url="Clients/extendedListFilter" url="Clients/extendedListFilter"
order="id DESC" order="id DESC"
:columns="columns" :columns="columns"
default-mode="card"
auto-load auto-load
redirect="customer"
> >
<!-- <!--
default-mode="table" default-mode="table"