0
0
Fork 0

feat: refs #6825 VnComponent mix component and attrs Form to create new row

This commit is contained in:
Alex Moreno 2024-05-17 13:00:57 +02:00
parent 598d116848
commit d00801b066
11 changed files with 233 additions and 88 deletions

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);
}

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 }">