refs #5673 fix: VnSelect update model, feat(claimDevelopment) add reload function
gitea/salix-front/pipeline/head This commit looks good Details

This commit is contained in:
Alex Moreno 2023-09-25 12:06:49 +02:00
parent 685c835d13
commit 1637921fab
8 changed files with 150 additions and 171 deletions

View File

@ -7,7 +7,7 @@ module.exports = defineConfig({
screenshotsFolder: 'test/cypress/screenshots', screenshotsFolder: 'test/cypress/screenshots',
supportFile: 'test/cypress/support/index.js', supportFile: 'test/cypress/support/index.js',
videosFolder: 'test/cypress/videos', videosFolder: 'test/cypress/videos',
video: true, video: false,
specPattern: 'test/cypress/integration/*.spec.js', specPattern: 'test/cypress/integration/*.spec.js',
experimentalRunAllSpecs: true, experimentalRunAllSpecs: true,
component: { component: {

View File

@ -1,6 +1,6 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { onMounted, onUnmounted, computed, ref, watch } from 'vue'; import { computed, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useValidator } from 'src/composables/useValidator'; import { useValidator } from 'src/composables/useValidator';
@ -33,7 +33,7 @@ const $props = defineProps({
}, },
dataRequired: { dataRequired: {
type: Object, type: Object,
default: null, default: () => {},
}, },
defaultSave: { defaultSave: {
type: Boolean, type: Boolean,
@ -67,6 +67,7 @@ const formUrl = computed(() => $props.url);
const emit = defineEmits(['onFetch', 'update:selected']); const emit = defineEmits(['onFetch', 'update:selected']);
defineExpose({ defineExpose({
reload,
insert, insert,
remove, remove,
onSubmit, onSubmit,
@ -91,8 +92,8 @@ async function fetch(data) {
emit('onFetch', data); emit('onFetch', data);
} }
function reset() { async function reset() {
fetch(originalData.value); await fetch(originalData.value);
hasChanges.value = false; hasChanges.value = false;
} }
// eslint-disable-next-line vue/no-dupe-keys // eslint-disable-next-line vue/no-dupe-keys
@ -215,6 +216,9 @@ function getChanges() {
function getDifferences(obj1, obj2) { function getDifferences(obj1, obj2) {
let diff = {}; let diff = {};
delete obj1.$index;
delete obj2.$index;
for (let key in obj1) { for (let key in obj1) {
if (obj2[key] && obj1[key] !== obj2[key]) { if (obj2[key] && obj1[key] !== obj2[key]) {
diff[key] = obj2[key]; diff[key] = obj2[key];
@ -236,6 +240,10 @@ function isEmpty(obj) {
if (obj.length > 0) return false; if (obj.length > 0) return false;
} }
async function reload() {
vnPaginateRef.value.fetch();
}
watch(formUrl, async () => { watch(formUrl, async () => {
originalData.value = null; originalData.value = null;
reset(); reset();

View File

@ -1,5 +1,5 @@
<script setup> <script setup>
import { ref, toRefs, watch } from 'vue'; import { ref, toRefs, watch, computed } from 'vue';
const emit = defineEmits(['update:modelValue', 'update:options']); const emit = defineEmits(['update:modelValue', 'update:options']);
const $props = defineProps({ const $props = defineProps({
@ -16,8 +16,7 @@ const $props = defineProps({
default: '', default: '',
}, },
}); });
const updateValue = (newValue) => emit('update:modelValue', newValue); const { optionLabel, options } = toRefs($props);
const { modelValue, optionLabel, options } = toRefs($props);
const myOptions = ref([]); const myOptions = ref([]);
const myOptionsOriginal = ref([]); const myOptionsOriginal = ref([]);
function setOptions(data) { function setOptions(data) {
@ -50,12 +49,20 @@ const filterHandler = (val, update) => {
watch(options, (newValue) => { watch(options, (newValue) => {
setOptions(newValue); setOptions(newValue);
}); });
const value = computed({
get() {
return $props.modelValue;
},
set(value) {
emit('update:modelValue', value);
},
});
</script> </script>
<template> <template>
<QSelect <QSelect
v-model="modelValue" v-model="value"
@update:model-value="updateValue"
:options="myOptions" :options="myOptions"
:option-label="optionLabel" :option-label="optionLabel"
v-bind="$attrs" v-bind="$attrs"

View File

@ -29,12 +29,14 @@ const $props = defineProps({
const slots = useSlots(); const slots = useSlots();
const { t } = useI18n(); const { t } = useI18n();
const entity = ref();
onMounted(() => fetch()); onMounted(async () => {
await fetch();
});
const emit = defineEmits(['onFetch']); const emit = defineEmits(['onFetch']);
const entity = ref();
async function fetch() { async function fetch() {
const params = {}; const params = {};

View File

@ -3,6 +3,8 @@ import { ref, computed } from 'vue';
import { useRoute } from 'vue-router'; import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { toDate } from 'src/filters'; import { toDate } from 'src/filters';
import { useState } from 'src/composables/useState';
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue'; import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue';
@ -19,6 +21,7 @@ const $props = defineProps({
}); });
const route = useRoute(); const route = useRoute();
const state = useState();
const { t } = useI18n(); const { t } = useI18n();
const entityId = computed(() => { const entityId = computed(() => {
@ -67,6 +70,7 @@ function stateColor(code) {
const data = ref(useCardDescription()); const data = ref(useCardDescription());
const setData = (entity) => { const setData = (entity) => {
data.value = useCardDescription(entity.client.name, entity.id); data.value = useCardDescription(entity.client.name, entity.id);
state.set('ClaimDescriptor', entity);
}; };
</script> </script>

View File

@ -120,67 +120,65 @@ const columns = computed(() => [
@on-fetch="(data) => (workers = data)" @on-fetch="(data) => (workers = data)"
auto-load auto-load
/> />
<div class="column items-center"> <CrudModel
<CrudModel data-key="ClaimDevelopments"
data-key="ClaimDevelopments" url="ClaimDevelopments"
url="ClaimDevelopments" model="claimDevelopment"
model="claimDevelopment" :filter="developmentsFilter"
:filter="developmentsFilter" ref="claimDevelopmentForm"
ref="claimDevelopmentForm" :data-required="{ claimFk: route.params.id }"
:data-required="{ claimFk: route.params.id }" v-model:selected="selected"
v-model:selected="selected" auto-load
auto-load >
> <template #body="{ rows }">
<template #body="{ rows }"> <QTable
<QTable :columns="columns"
:columns="columns" :rows="rows"
:rows="rows" :pagination="{ rowsPerPage: 0 }"
:pagination="{ rowsPerPage: 0 }" row-key="$index"
row-key="$index" selection="multiple"
selection="multiple" hide-pagination
hide-pagination v-model:selected="selected"
v-model:selected="selected" :grid="$q.screen.lt.md"
:grid="$q.screen.lt.md" >
> <template #body-cell="{ row, col }">
<template #body-cell="{ row, col }"> <QTd auto-width>
<QTd auto-width> <VnSelectFilter
<VnSelectFilter :label="col.label"
:label="col.label" v-model="row[col.model]"
v-model="row[col.model]" :options="col.options"
:options="col.options" :option-value="col.optionValue"
:option-value="col.optionValue" :option-label="col.optionLabel"
:option-label="col.optionLabel" />
/> </QTd>
</QTd> </template>
</template> <template #item="props">
<template #item="props"> <div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition"> <QCard bordered flat>
<QCard bordered flat> <QCardSection>
<QCardSection> <QCheckbox v-model="props.selected" dense />
<QCheckbox v-model="props.selected" dense /> </QCardSection>
</QCardSection> <QSeparator />
<QSeparator /> <QList dense>
<QList dense> <QItem v-for="col in props.cols" :key="col.name">
<QItem v-for="col in props.cols" :key="col.name"> <QItemSection>
<QItemSection> <VnSelectFilter
<VnSelectFilter :label="col.label"
:label="col.label" v-model="props.row[col.model]"
v-model="props.row[col.model]" :options="col.options"
:options="col.options" :option-value="col.optionValue"
:option-value="col.optionValue" :option-label="col.optionLabel"
:option-label="col.optionLabel" dense
dense />
/> </QItemSection>
</QItemSection> </QItem>
</QItem> </QList>
</QList> </QCard>
</QCard> </div>
</div> </template>
</template> </QTable>
</QTable> </template>
</template> </CrudModel>
</CrudModel>
</div>
<QPageSticky position="bottom-right" :offset="[25, 25]"> <QPageSticky position="bottom-right" :offset="[25, 25]">
<QBtn fab color="primary" icon="add" @click="claimDevelopmentForm.insert()" /> <QBtn fab color="primary" icon="add" @click="claimDevelopmentForm.insert()" />
</QPageSticky> </QPageSticky>

View File

@ -1,46 +1,34 @@
<script setup> <script setup>
import axios from 'axios'; import axios from 'axios';
import { ref } from 'vue'; import { watch, ref, computed, onUnmounted, onMounted } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useRoute } from 'vue-router'; import CrudModel from 'components/CrudModel.vue';
import { useArrayData } from 'src/composables/useArrayData'; import { useState } from 'src/composables/useState';
import VnPaginate from 'src/components/ui/VnPaginate.vue';
import FetchData from 'components/FetchData.vue';
import VnConfirm from 'src/components/ui/VnConfirm.vue';
import { toDate } from 'src/filters'; import { toDate } from 'src/filters';
const quasar = useQuasar(); const quasar = useQuasar();
const route = useRoute(); const state = useState();
const { t } = useI18n(); const { t } = useI18n();
const arrayData = useArrayData('ClaimRma'); const selected = ref([]);
const claimRmaRef = ref();
const claim = computed(() => state.get('ClaimDescriptor'));
const claim = ref(); const claimRmaFilter = {
const claimFilter = { include: {
fields: ['rma'], relation: 'worker',
}; scope: {
include: {
async function onFetch(data) { relation: 'user',
claim.value = data;
const filter = {
include: {
relation: 'worker',
scope: {
include: {
relation: 'user',
},
}, },
}, },
order: 'created DESC', },
where: { order: 'created DESC',
code: claim.value.rma, where: {
}, code: claim.value?.rma,
}; },
};
arrayData.applyFilter({ filter });
}
async function addRow() { async function addRow() {
if (!claim.value.rma) { if (!claim.value.rma) {
@ -54,7 +42,7 @@ async function addRow() {
}; };
await axios.post(`ClaimRmas`, formData); await axios.post(`ClaimRmas`, formData);
await arrayData.refresh(); await claimRmaRef.value.reload();
quasar.notify({ quasar.notify({
type: 'positive', type: 'positive',
@ -63,38 +51,33 @@ async function addRow() {
}); });
} }
function confirmRemove(id) { onMounted(() => {
quasar if (claim.value) claimRmaRef.value.reload();
.dialog({ });
component: VnConfirm, watch(
componentProps: { claim,
data: { id }, () => {
promise: remove, claimRmaRef.value.reload();
}, },
}) { deep: true }
.onOk(async () => await arrayData.refresh()); );
}
async function remove({ id }) {
await axios.delete(`ClaimRmas/${id}`);
quasar.notify({
type: 'positive',
message: t('globals.rowRemoved'),
});
}
</script> </script>
<template> <template>
<FetchData
:url="`Claims/${route.params.id}`"
:filter="claimFilter"
@on-fetch="onFetch"
auto-load
/>
<div class="column items-center"> <div class="column items-center">
<div class="list"> <div class="list">
<VnPaginate data-key="ClaimRma" url="ClaimRmas"> <CrudModel
data-key="ClaimRma"
url="ClaimRmas"
model="ClaimRma"
:filter="claimRmaFilter"
v-model:selected="selected"
ref="claimRmaRef"
:default-save="false"
:default-reset="false"
:default-remove="false"
>
<template #body="{ rows }"> <template #body="{ rows }">
<QCard class="card"> <QCard>
<template v-for="(row, index) of rows" :key="row.id"> <template v-for="(row, index) of rows" :key="row.id">
<QItem class="q-pa-none items-start"> <QItem class="q-pa-none items-start">
<QItemSection class="q-pa-md"> <QItemSection class="q-pa-md">
@ -105,7 +88,7 @@ async function remove({ id }) {
{{ t('claim.rma.user') }} {{ t('claim.rma.user') }}
</QItemLabel> </QItemLabel>
<QItemLabel> <QItemLabel>
{{ row.worker.user.name }} {{ row?.worker?.user?.name }}
</QItemLabel> </QItemLabel>
</QItemSection> </QItemSection>
</QItem> </QItem>
@ -131,7 +114,7 @@ async function remove({ id }) {
round round
color="orange" color="orange"
icon="vn:bin" icon="vn:bin"
@click="confirmRemove(row.id)" @click="claimRmaRef.remove([row])"
> >
<QTooltip>{{ t('globals.remove') }}</QTooltip> <QTooltip>{{ t('globals.remove') }}</QTooltip>
</QBtn> </QBtn>
@ -141,24 +124,11 @@ async function remove({ id }) {
</template> </template>
</QCard> </QCard>
</template> </template>
</VnPaginate> </CrudModel>
</div> </div>
</div> </div>
<QPageSticky position="bottom-right" :offset="[25, 25]"> <QPageSticky position="bottom-right" :offset="[25, 25]">
<label for="fileInput"> <QBtn fab color="primary" icon="add" @click="addRow()" />
<QBtn fab @click="inputFile.nativeEl.click()" icon="add" color="primary">
<QInput
ref="inputFile"
type="file"
style="display: none"
multiple
v-model="files"
@update:model-value="create()"
/>
<QTooltip bottom> {{ t('globals.add') }} </QTooltip>
</QBtn>
</label>
</QPageSticky> </QPageSticky>
</template> </template>
@ -167,16 +137,6 @@ async function remove({ id }) {
width: 100%; width: 100%;
max-width: 60em; max-width: 60em;
} }
.q-toolbar {
background-color: $grey-9;
}
.sticky-page {
padding-top: 66px;
}
.q-page-sticky {
z-index: 2998;
}
</style> </style>
<i18n> <i18n>

View File

@ -15,7 +15,6 @@ describe('CrudModel', () => {
'vue-i18n', 'vue-i18n',
], ],
mocks: { mocks: {
fetch: vi.fn(),
validate: vi.fn(), validate: vi.fn(),
}, },
}, },
@ -31,7 +30,7 @@ describe('CrudModel', () => {
}); });
beforeEach(() => { beforeEach(() => {
vm.state.set('crudModel', []); vm.fetch([]);
}); });
afterEach(() => { afterEach(() => {
@ -50,24 +49,25 @@ describe('CrudModel', () => {
describe('getChanges()', () => { describe('getChanges()', () => {
it('should return correct updates and creates', async () => { it('should return correct updates and creates', async () => {
vm.originalData = [ vm.fetch([
{ id: 1, name: 'Tony Starks', $index: 1 }, { id: 1, name: 'New name one' },
{ id: 2, name: 'Jessica Jones', $index: 2 }, { id: 2, name: 'New name two' },
{ id: 3, name: 'Bruce Wayne', $index: 3 }, { id: 3, name: 'Bruce Wayne' },
];
vm.state.set('crudModel', [
{ id: 1, name: 'New name one', $index: 1 },
{ id: 2, name: 'New name two', $index: 2 },
{ id: 3, name: 'Bruce Wayne', $index: 3 },
]); ]);
vm.originalData = [
{ id: 1, name: 'Tony Starks' },
{ id: 2, name: 'Jessica Jones' },
{ id: 3, name: 'Bruce Wayne' },
];
vm.insert(); vm.insert();
const result = vm.getChanges(); const result = vm.getChanges();
const expected = { const expected = {
creates: [ creates: [
{ {
$index: 4, $index: 3,
fk: 1, fk: 1,
}, },
], ],