#6825 - VnTable V1 #396

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

View File

@ -2,17 +2,12 @@
<span v-for="toComponent of componentArray" :key="toComponent.field">
<component
v-if="toComponent?.component"
:is="
(components && components[toComponent.component]) ?? toComponent.component
"
v-bind="
typeof toComponent.attrs == 'function'
? toComponent.attrs(value)
: toComponent.attrs
"
@click="toComponent.event && toComponent.event(value)"
:is="mix(toComponent).component"
v-bind="mix(toComponent).attrs"
v-on="mix(toComponent).event"
v-model="model"
/>
<!-- @click="toComponent.event && toComponent.event(value)" -->
</span>
</template>
<script setup>
@ -40,4 +35,24 @@ const componentArray = computed(() => {
if (typeof $props.prop === 'object') return [$props.prop];
return $props.prop;
});
function mix(toComponent) {
const { component, attrs, event } = toComponent;
const customComponent = $props.components[component];
return {
component: customComponent?.component ?? component,
attrs: {
...toValueAttrs(attrs),
...toValueAttrs(customComponent?.attrs),
...toComponent,
...toValueAttrs(customComponent?.forceAttrs),
},
event: event ?? customComponent?.event,
};
}
function toValueAttrs(attrs) {
if (!attrs) return;
return typeof attrs == 'function' ? attrs($props.value) : attrs;
}
</script>

View File

@ -2,7 +2,12 @@
import { computed, ref } from 'vue';
import { useI18n } from 'vue-i18n';
const emit = defineEmits(['update:modelValue', 'update:options', 'keyup.enter']);
const emit = defineEmits([
'update:modelValue',
'update:options',
'keyup.enter',
'remove',
]);
const $props = defineProps({
modelValue: {
@ -43,6 +48,7 @@ const onEnterPress = () => {
const handleValue = (val = null) => {
value.value = val;
emit('remove');
};
const focus = () => {

View File

@ -22,6 +22,10 @@ const $props = defineProps({
type: String,
default: 'id',
},
optionFilter: {
type: String,
default: null,
},
url: {
type: String,
default: '',
@ -59,7 +63,7 @@ const $props = defineProps({
const { t } = useI18n();
const requiredFieldRule = (val) => val ?? t('globals.fieldRequired');
const { optionLabel, optionValue, options, modelValue } = toRefs($props);
const { optionLabel, optionValue, optionFilter, options, modelValue } = toRefs($props);
const myOptions = ref([]);
const myOptionsOriginal = ref([]);
const vnSelectRef = ref();
@ -109,7 +113,7 @@ async function fetchFilter(val) {
const { fields, sortBy, limit } = $props;
let key = optionLabel.value;
if (new RegExp(/\d/g).test(val)) key = optionValue.value;
if (new RegExp(/\d/g).test(val)) key = optionFilter.value ?? optionValue.value;
const where = { ...{ [key]: { like: `%${val}%` } }, ...$props.where };
const fetchOptions = { where, order: sortBy, limit };

View File

@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import { useStateStore } from 'stores/useStateStore';
import FormModelPopup from 'components/FormModelPopup.vue';
import VnPaginate from 'components/ui/VnPaginate.vue';
import VnFilterPanel from 'components/ui/VnFilterPanel.vue';
import VnLv from 'components/ui/VnLv.vue';
@ -189,7 +190,7 @@ const rowClickFunction = computed(() => {
:column="col"
:show-title="true"
:data-key="$attrs['data-key']"
v-model="params[col.field]"
v-model="params[col.columnFilter?.field ?? col.field]"
/>
</QTh>
</template>
@ -197,7 +198,12 @@ const rowClickFunction = computed(() => {
<template #body-cell="{ col, row }">
<!-- Columns -->
<QTd auto-width :class="`text-${col.align ?? 'left'}`">
<VnTableColumn :column="col" :row="row" />
<VnTableColumn
:column="col"
:row="row"
:is-editable="false"
v-model="row[col.field]"
/>
</QTd>
</template>
<template #item="{ row, colsMap }">
@ -308,12 +314,24 @@ const rowClickFunction = computed(() => {
</QTooltip> -->
</QPageSticky>
<QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
<VnTableCreate
:columns="cardTemplate.create"
<FormModelPopup
v-bind="{ ...$attrs, ...create }"
:model="$attrs['data-key'] + 'Create'"
:create="create"
/>
<!-- v-bind="$attrs.create" -->
>
<template #form-inputs="{ data }">
<div class="grid-create">
<VnTableColumn
v-for="column of cardTemplate.create"
:key="column.field"
:column="column"
:row="{}"
default="input"
v-model="data[column.field]"
/>
<slot name="more-create-dialog" :data="data" />
</div>
</template>
</FormModelPopup>
</QDialog>
</template>
<style lang="scss">
@ -381,6 +399,14 @@ const rowClickFunction = computed(() => {
margin: 0 auto;
}
.grid-create {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, max-content));
max-width: 100%;
grid-gap: 20px;
margin: 0 auto;
}
.flex-one {
display: flex;
flex-flow: row wrap;

View File

@ -4,8 +4,8 @@
:prop="col.before"
:components="components"
:value="value"
v-model="model"
/>
{{ col?.component }}
<VnComponent
v-if="col.component"
:prop="col"
@ -13,13 +13,13 @@
:value="value"
v-model="model"
/>
<!-- ?? [defaultComponent] -->
<span v-else>{{ dashIfEmpty(row[col.field]) }}</span>
<VnComponent
v-if="col.after"
:prop="col.after"
:components="components"
:value="value"
v-model="model"
/>
</template>
<script setup>
@ -45,29 +45,85 @@ const $props = defineProps({
},
default: {
type: [Object, String],
required: false,
default: null,
},
componentProp: {
type: String,
default: null,
},
isEditable: {
type: Boolean,
default: true,
},
components: {
type: Object,
default: null,
},
});
const components = {
input: markRaw(VnInput),
number: markRaw(VnInput),
date: markRaw(VnInputDate),
checkbox: markRaw(QCheckbox),
select: markRaw(VnSelect),
icon: markRaw(QIcon),
const defaultComponents = {
input: {
component: markRaw(VnInput),
attrs: {
disable: !$props.isEditable,
},
},
number: {
component: markRaw(VnInput),
attrs: {
disable: !$props.isEditable,
},
},
date: {
component: markRaw(VnInputDate),
attrs: {
disable: !$props.isEditable,
},
},
checkbox: {
component: markRaw(QCheckbox),
attrs: (prop) => {
return {
disable: !$props.isEditable,
// 'true-value': 1,
// 'false-value': 0,
// 'model-value': Boolean(prop),
};
},
forceAttrs: {
label: null,
},
},
select: {
component: markRaw(VnSelect),
attrs: {
disable: !$props.isEditable,
},
},
icon: {
component: markRaw(QIcon),
},
};
const value = computed(() =>
$props.column.format ? $props.column.format($props.row) : $props.row
);
const value = computed(() => {
return $props.column.format ? $props.column.format($props.row) : $props.row;
});
const col = computed(() => {
const newColumn = { ...$props.column };
console.log('newColumn: ', newColumn);
console.log('newColumn.componen: ', newColumn.component);
let newColumn = { ...$props.column };
const specific = newColumn[$props.componentProp];
if (specific) {
newColumn = {
...newColumn,
...specific,
...specific.attrs,
...specific.forceAttrs,
};
}
if ($props.default && !newColumn.component) newColumn.component = $props.default;
return newColumn;
});
const components = computed(() => $props.components ?? defaultComponents);
</script>

View File

@ -19,12 +19,7 @@ const $props = defineProps({
<VnTableColumn
v-for="column of $props.columns"
:key="column.field"
:column="{
...(column ?? column.create),
...{
attrs: column,
},
}"
:column="column"
:row="{}"
default="input"
v-model="data[column.field]"

View File

@ -11,24 +11,15 @@
class="row no-wrap"
>
<hr class="q-ma-none divisor-line" v-if="showTitle" />
<component
v-if="columnFilter?.component"
:is="isBasicComponent?.component ?? columnFilter.component"
v-model="model"
v-bind="{ ...isBasicComponent?.attrs, ...columnFilter.attrs }"
v-on="isBasicComponent?.event ?? columnFilter.event"
dense
/>
<span v-else class="full-width">
<QInput
<span class="full-width">
<VnTableColumn
:column="$props.column"
:row="{}"
default="input"
v-model="model"
dense
class="q-px-sm q-pb-xs q-pt-none"
:label="showTitle ? '' : column.label"
@keyup.enter="addFilter"
>
<template #append> </template>
</QInput>
:components="components"
:component-prop="`columnFilter`"
/>
</span>
</div>
<div v-else-if="showTitle" style="height: 45px"><!-- fixme! --></div>
@ -42,6 +33,7 @@ import { useArrayData } from 'composables/useArrayData';
import VnSelect from 'components/common/VnSelect.vue';
import VnInput from 'components/common/VnInput.vue';
import VnInputDate from 'components/common/VnInputDate.vue';
import VnTableColumn from 'components/common/VnTableColumn.vue';
const $props = defineProps({
column: {
@ -60,16 +52,23 @@ const $props = defineProps({
const model = defineModel();
const arrayData = useArrayData($props.dataKey);
const columnFilter = computed(() => $props.column?.columnFilter);
const isBasicComponent = computed(
() => components[$props.column?.columnFilter?.component]
);
const updateEvent = { 'update:modelValue': addFilter };
const enterEvent = { 'keyup.enter': () => addFilter(model.value) };
const enterEvent = {
'keyup.enter': () => addFilter(model.value),
remove: () => addFilter(null),
};
const components = {
input: {
component: markRaw(VnInput),
event: enterEvent,
attrs: {
class: 'q-px-sm q-pb-xs q-pt-none',
dense: true,
},
forceAttrs: {
label: $props.showTitle ? '' : $props.column.label,
},
},
number: {
component: markRaw(VnInput),
@ -82,12 +81,18 @@ const components = {
checkbox: {
component: markRaw(QCheckbox),
event: updateEvent,
forceAttrs: {
label: $props.showTitle ? '' : $props.column.label,
},
},
select: {
component: markRaw(VnSelect),
event: updateEvent,
attrs: {
class: 'full-width q-px-sm',
class: 'q-px-md q-pb-xs q-pt-none',
dense: true,
},
forceAttrs: {
label: $props.showTitle ? '' : $props.column.label,
},
},
@ -109,7 +114,6 @@ const components = {
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

@ -58,6 +58,7 @@ onMounted(() => {
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));
params.value = { ...params.value, ...userParams.value };
}
emit('init', { params: userParams.value });
});
@ -147,7 +148,6 @@ async function clearFilters() {
}
const tagsList = computed(() => {
console.log('userParams.value: ', userParams.value);
return Object.entries(userParams.value)
.filter(([key, value]) => value && !(props.hiddenTags || []).includes(key))
.map(([key, value]) => ({
@ -166,8 +166,6 @@ const customTags = computed(() =>
async function remove(key) {
userParams.value[key] = null;
params.value[key] = undefined;
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

@ -270,7 +270,8 @@ customer:
extendedList:
tableVisibleColumns:
id: Identifier
name: Name
name: Comercial name
socialName: Business name
fi: Tax number
salesPersonFk: Salesperson
credit: Credit

View File

@ -268,7 +268,8 @@ customer:
extendedList:
tableVisibleColumns:
id: Identificador
name: Nombre
name: Nombre Comercial
socialName: Razón social
fi: NIF / CIF
salesPersonFk: Comercial
credit: Crédito

View File

@ -3,6 +3,7 @@ import { ref, computed, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRouter } from 'vue-router';
import VnTable from 'components/common/VnTable.vue';
import VnLocation from 'src/components/common/VnLocation.vue';
import CustomerSummary from '../Card/CustomerSummary.vue';
import { useSummaryDialog } from 'src/composables/useSummaryDialog';
@ -30,6 +31,7 @@ const selectedCustomerId = ref(0);
const selectedSalesPersonId = ref(0);
const allColumnNames = ref([]);
const visibleColumns = ref([]);
const postcodesOptions = ref([]);
const tableColumnComponents = {
customerStatus: {
@ -278,8 +280,8 @@ const columns = computed(() => [
condition: () => true,
},
columnFilter: {
field: 'search',
component: 'select',
field: 'search',
attrs: {
url: 'Clients',
fields: ['id', 'name'],
@ -294,33 +296,32 @@ const columns = computed(() => [
isTitle: true,
create: true,
},
{
align: 'left',
field: 'socialName',
label: t('customer.extendedList.tableVisibleColumns.socialName'),
isTitle: true,
create: true,
},
{
align: 'left',
field: 'fi',
label: t('customer.extendedList.tableVisibleColumns.fi'),
name: 'fi',
create: true,
},
{
align: 'left',
field: 'salesPersonFk',
label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'),
name: 'salesPersonFk',
columnFilter: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
},
},
create: {
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
},
component: 'select',
attrs: {
url: 'Workers/activeWithInheritedRole',
fields: ['id', 'name'],
where: { role: 'salesPerson' },
},
create: true,
},
{
align: 'left',
@ -359,6 +360,7 @@ const columns = computed(() => [
field: 'street',
label: t('customer.extendedList.tableVisibleColumns.street'),
name: 'street',
create: true,
},
{
align: 'left',
@ -400,9 +402,16 @@ const columns = computed(() => [
},
{
align: 'left',
field: 'businessType',
field: 'businessTypeFk',
label: t('customer.extendedList.tableVisibleColumns.businessTypeFk'),
name: 'businessTypeFk',
create: true,
component: 'select',
attrs: {
url: 'BusinessTypes',
optionLabel: 'description',
optionValue: 'code',
},
},
{
align: 'left',
@ -450,6 +459,8 @@ const columns = computed(() => [
field: 'isEqualizated',
label: t('customer.extendedList.tableVisibleColumns.isEqualizated'),
name: 'isEqualizated',
component: 'checkbox',
created: true,
},
{
align: 'left',
@ -473,6 +484,7 @@ const columns = computed(() => [
field: 'hasToInvoiceByAddress',
label: t('customer.extendedList.tableVisibleColumns.hasToInvoiceByAddress'),
name: 'hasToInvoiceByAddress',
component: 'checkbox',
},
{
align: 'left',
@ -530,6 +542,14 @@ const navigateToTravelId = (id) => router.push({ path: `/customer/${id}` });
const selectCustomerId = (id) => (selectedCustomerId.value = id);
const selectSalesPersonId = (id) => (selectedSalesPersonId.value = id);
function handleLocation(data, location) {
const { town, code, provinceFk, countryFk } = location ?? {};
data.postcode = code;
data.city = town;
data.provinceFk = provinceFk;
data.countryFk = countryFk;
}
</script>
<template>
@ -569,10 +589,29 @@ const selectSalesPersonId = (id) => (selectedSalesPersonId.value = id);
}"
order="id DESC"
:columns="columns"
default-mode="card"
redirect="customer"
default-mode="table"
auto-load
>
<template #more-create-dialog="{ data }">
<QInput :label="t('Email')" clearable type="email" v-model="data.email">
<template #append>
<QIcon name="info" class="cursor-info">
<QTooltip max-width="400px">{{
t('customer.basicData.youCanSaveMultipleEmails')
}}</QTooltip>
</QIcon>
</template>
</QInput>
<QInput v-model="data.userName" :label="t('Web user')" />
<VnLocation
:roles-allowed-to-create="['deliveryAssistant']"
:options="postcodesOptions"
v-model="data.location"
@update:model-value="(location) => handleLocation(data, location)"
>
</VnLocation>
</template>
<!-- redirect="customer" -->
<!--
default-mode="table"
<template #body-cell="{ col, value }">