forked from verdnatura/salix-front
feat: refs #6825 VnComponent mix component and attrs Form to create new row
This commit is contained in:
parent
598d116848
commit
d00801b066
|
@ -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>
|
||||
|
|
|
@ -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 = () => {
|
||||
|
|
|
@ -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 };
|
||||
|
|
|
@ -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"
|
||||
>
|
||||
<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]"
|
||||
/>
|
||||
<!-- v-bind="$attrs.create" -->
|
||||
<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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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]"
|
||||
|
|
|
@ -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"
|
||||
<span class="full-width">
|
||||
<VnTableColumn
|
||||
:column="$props.column"
|
||||
:row="{}"
|
||||
default="input"
|
||||
v-model="model"
|
||||
v-bind="{ ...isBasicComponent?.attrs, ...columnFilter.attrs }"
|
||||
v-on="isBasicComponent?.event ?? columnFilter.event"
|
||||
dense
|
||||
:components="components"
|
||||
:component-prop="`columnFilter`"
|
||||
/>
|
||||
<span v-else class="full-width">
|
||||
<QInput
|
||||
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>
|
||||
</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 };
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -270,7 +270,8 @@ customer:
|
|||
extendedList:
|
||||
tableVisibleColumns:
|
||||
id: Identifier
|
||||
name: Name
|
||||
name: Comercial name
|
||||
socialName: Business name
|
||||
fi: Tax number
|
||||
salesPersonFk: Salesperson
|
||||
credit: Credit
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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' },
|
||||
},
|
||||
},
|
||||
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 }">
|
||||
|
|
Loading…
Reference in New Issue