#6825 - VnTable V1 #396

Merged
alexm merged 73 commits from 6825-vnTable into dev 2024-06-20 13:08:55 +00:00
7 changed files with 252 additions and 188 deletions
Showing only changes of commit c223bbe0ea - Show all commits

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;
let key = optionLabel.value;
console.log('key: ', key);
if (new RegExp(/\d/g).test(val)) key = optionValue.value;
console.log('key: ', key);
const where = { [key]: { like: `%${val}%` } };
const fetchOptions = { where, order: sortBy, limit };
console.log('where: ', where);
console.log('fields: ', fields);
if (fields) fetchOptions.fields = fields;
console.log('fetchOptions: ', fetchOptions);
return dataRef.value.fetch(fetchOptions);
}

View File

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

View File

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

View File

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

View File

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

Si la operacion ternaria es para el valor de la traducción, porque no ponerla dentro de la funcion t?

Otra cosa es, hacen falta las traducciones? Porque en globals tenemos yes y no

Si la operacion ternaria es para el valor de la traducción, porque no ponerla dentro de la funcion t? Otra cosa es, hacen falta las traducciones? Porque en globals tenemos yes y no

View File

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