Merge pull request 'feat: refs #6897 add time formatting and improve column alignment handling in VnTable' (!1435) from 6897-fixMinorIssueWithSelectAndStyle into test
gitea/salix-front/pipeline/pr-dev This commit looks good Details
gitea/salix-front/pipeline/head This commit looks good Details

Reviewed-on: #1435
Reviewed-by: Alex Moreno <alexm@verdnatura.es>
This commit is contained in:
Pablo Natek 2025-02-20 07:44:18 +00:00
commit bdd5843d14
5 changed files with 58 additions and 18 deletions

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import { markRaw, computed } from 'vue'; import { markRaw, computed } from 'vue';
import { QIcon, QCheckbox, QToggle } from 'quasar'; import { QIcon, QToggle } from 'quasar';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty } from 'src/filters';
import VnSelect from 'components/common/VnSelect.vue'; import VnSelect from 'components/common/VnSelect.vue';

View File

@ -14,10 +14,10 @@ import {
import { useArrayData } from 'src/composables/useArrayData'; import { useArrayData } from 'src/composables/useArrayData';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useRoute, useRouter } from 'vue-router'; import { useRoute, useRouter } from 'vue-router';
import { useQuasar } from 'quasar'; import { useQuasar, date } from 'quasar';
import { useStateStore } from 'stores/useStateStore'; import { useStateStore } from 'stores/useStateStore';
import { useFilterParams } from 'src/composables/useFilterParams'; import { useFilterParams } from 'src/composables/useFilterParams';
import { dashIfEmpty } from 'src/filters'; import { dashIfEmpty, toDate } from 'src/filters';
import CrudModel from 'src/components/CrudModel.vue'; import CrudModel from 'src/components/CrudModel.vue';
import FormModelPopup from 'components/FormModelPopup.vue'; import FormModelPopup from 'components/FormModelPopup.vue';
@ -345,7 +345,7 @@ const clickHandler = async (event) => {
if (isDateElement || isTimeElement || isQselectDropDown) return; if (isDateElement || isTimeElement || isQselectDropDown) return;
if (clickedElement === null) { if (clickedElement === null) {
destroyInput(editingRow.value, editingField.value); await destroyInput(editingRow.value, editingField.value);
return; return;
} }
const rowIndex = clickedElement.getAttribute('data-row-index'); const rowIndex = clickedElement.getAttribute('data-row-index');
@ -355,7 +355,7 @@ const clickHandler = async (event) => {
if (editingRow.value !== null && editingField.value !== null) { if (editingRow.value !== null && editingField.value !== null) {
if (editingRow.value == rowIndex && editingField.value == colField) return; if (editingRow.value == rowIndex && editingField.value == colField) return;
destroyInput(editingRow.value, editingField.value); await destroyInput(editingRow.value, editingField.value);
} }
if (isEditableColumn(column)) { if (isEditableColumn(column)) {
@ -365,7 +365,7 @@ const clickHandler = async (event) => {
async function handleTabKey(event, rowIndex, colField) { async function handleTabKey(event, rowIndex, colField) {
if (editingRow.value == rowIndex && editingField.value == colField) if (editingRow.value == rowIndex && editingField.value == colField)
destroyInput(editingRow.value, editingField.value); await destroyInput(editingRow.value, editingField.value);
const direction = event.shiftKey ? -1 : 1; const direction = event.shiftKey ? -1 : 1;
const { nextRowIndex, nextColumnName } = await handleTabNavigation( const { nextRowIndex, nextColumnName } = await handleTabNavigation(
@ -425,7 +425,8 @@ async function renderInput(rowId, field, clickedElement) {
await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row); await column?.cellEvent?.['update:modelValue']?.(value, oldValue, row);
}, },
keyup: async (event) => { keyup: async (event) => {
if (event.key === 'Enter') handleBlur(rowId, field, clickedElement); if (event.key === 'Enter')
await destroyInput(rowIndex, field, clickedElement);
}, },
keydown: async (event) => { keydown: async (event) => {
switch (event.key) { switch (event.key) {
@ -434,7 +435,7 @@ async function renderInput(rowId, field, clickedElement) {
event.stopPropagation(); event.stopPropagation();
break; break;
case 'Escape': case 'Escape':
destroyInput(rowId, field, clickedElement); await destroyInput(rowId, field, clickedElement);
break; break;
default: default:
break; break;
@ -456,12 +457,13 @@ async function renderInput(rowId, field, clickedElement) {
node.el?.querySelector('span > div > div').focus(); node.el?.querySelector('span > div > div').focus();
} }
function destroyInput(rowIndex, field, clickedElement) { async function destroyInput(rowIndex, field, clickedElement) {
if (!clickedElement) if (!clickedElement)
clickedElement = document.querySelector( clickedElement = document.querySelector(
`[data-row-index="${rowIndex}"][data-col-field="${field}"]`, `[data-row-index="${rowIndex}"][data-col-field="${field}"]`,
); );
if (clickedElement) { if (clickedElement) {
await nextTick();
render(null, clickedElement); render(null, clickedElement);
Array.from(clickedElement.childNodes).forEach((child) => { Array.from(clickedElement.childNodes).forEach((child) => {
child.style.visibility = 'visible'; child.style.visibility = 'visible';
@ -473,10 +475,6 @@ function destroyInput(rowIndex, field, clickedElement) {
editingField.value = null; editingField.value = null;
} }
function handleBlur(rowIndex, field, clickedElement) {
destroyInput(rowIndex, field, clickedElement);
}
async function handleTabNavigation(rowIndex, colName, direction) { async function handleTabNavigation(rowIndex, colName, direction) {
const columns = $props.columns; const columns = $props.columns;
const totalColumns = columns.length; const totalColumns = columns.length;
@ -527,11 +525,36 @@ function formatColumnValue(col, row, dashIfEmpty) {
} else { } else {
return col.format(row, dashIfEmpty); return col.format(row, dashIfEmpty);
} }
}
if (col?.component === 'date') return dashIfEmpty(toDate(row[col?.name]));
if (col?.component === 'time')
return row[col?.name] >= 5
? dashIfEmpty(date.formatDate(new Date(row[col?.name]), 'HH:mm'))
: row[col?.name];
if (selectRegex.test(col?.component) && $props.isEditable) {
const { find, url } = col.attrs;
const urlRelation = url?.charAt(0)?.toLocaleLowerCase() + url?.slice(1, -1);
if (col?.attrs.options) {
const find = col?.attrs.options.find((option) => option.id === row[col.name]);
if (!col.attrs?.optionLabel || !find) return dashIfEmpty(row[col?.name]);
return dashIfEmpty(find[col.attrs?.optionLabel ?? 'name']);
}
if (typeof row[urlRelation] == 'object') {
if (typeof find == 'object')
return dashIfEmpty(row[urlRelation][find?.label ?? 'name']);
return dashIfEmpty(row[urlRelation][col?.attrs.optionLabel ?? 'name']);
}
if (typeof row[urlRelation] == 'string') return dashIfEmpty(row[urlRelation]);
} else { } else {
return dashIfEmpty(row[col?.name]); return dashIfEmpty(row[col?.name]);
} }
} }
const checkbox = ref(null);
function cardClick(_, row) { function cardClick(_, row) {
if ($props.redirect) router.push({ path: `/${$props.redirect}/${row.id}` }); if ($props.redirect) router.push({ path: `/${$props.redirect}/${row.id}` });
} }
@ -730,7 +753,11 @@ function cardClick(_, row) {
<span <span
v-else v-else
:class="hasEditableFormat(col)" :class="hasEditableFormat(col)"
:style="col?.style ? col.style(row) : null" :style="
typeof col?.style == 'function'
? col.style(row)
: col?.style
"
style="bottom: 0" style="bottom: 0"
> >
{{ formatColumnValue(col, row, dashIfEmpty) }} {{ formatColumnValue(col, row, dashIfEmpty) }}
@ -783,7 +810,7 @@ function cardClick(_, row) {
<QCardSection <QCardSection
vertical vertical
class="no-margin no-padding" class="no-margin no-padding"
:class="colsMap.tableActions ? '' : 'fit'" :class="colsMap.tableActions ? 'w-80' : 'fit'"
> >
<!-- Chips --> <!-- Chips -->
<QCardSection <QCardSection

View File

@ -7,6 +7,7 @@ export function getColAlign(col) {
case 'number': case 'number':
align = 'right'; align = 'right';
break; break;
case 'time':
case 'date': case 'date':
case 'checkbox': case 'checkbox':
align = 'center'; align = 'center';

View File

@ -119,7 +119,7 @@ const columns = computed(() => [
:url="`Workers/${entityId}/trainingCourse`" :url="`Workers/${entityId}/trainingCourse`"
:url-create="`Workers/${entityId}/trainingCourse`" :url-create="`Workers/${entityId}/trainingCourse`"
save-url="TrainingCourses/crud" save-url="TrainingCourses/crud"
:filter="courseFilter" :user-filter="courseFilter"
:create="{ :create="{
urlCreate: 'trainingCourses', urlCreate: 'trainingCourses',
title: t('Create training course'), title: t('Create training course'),

View File

@ -8,6 +8,17 @@ const { t } = useI18n();
const route = useRoute(); const route = useRoute();
const entityId = computed(() => route.params.id); const entityId = computed(() => route.params.id);
const centerFilter = {
include: [
{
relation: 'center',
scope: {
fields: ['id', 'name'],
},
},
],
};
const columns = [ const columns = [
{ {
align: 'left', align: 'left',
@ -33,7 +44,7 @@ const columns = [
create: true, create: true,
component: 'select', component: 'select',
attrs: { attrs: {
url: 'medicalCenters', url: 'centers',
fields: ['id', 'name'], fields: ['id', 'name'],
}, },
}, },
@ -84,6 +95,7 @@ const columns = [
ref="tableRef" ref="tableRef"
data-key="WorkerMedical" data-key="WorkerMedical"
:url="`Workers/${entityId}/medicalReview`" :url="`Workers/${entityId}/medicalReview`"
:user-filter="centerFilter"
save-url="MedicalReviews/crud" save-url="MedicalReviews/crud"
:create="{ :create="{
urlCreate: 'medicalReviews', urlCreate: 'medicalReviews',