forked from verdnatura/salix-front
feat(VnTable): order column
This commit is contained in:
parent
36fc988df2
commit
fd6b395f34
|
@ -138,7 +138,12 @@ const showFilter = computed(
|
|||
);
|
||||
</script>
|
||||
<template>
|
||||
<div v-if="showFilter" class="full-width" :class="alignRow()">
|
||||
<div
|
||||
v-if="showFilter"
|
||||
class="full-width"
|
||||
:class="alignRow()"
|
||||
style="max-height: 45px; overflow: hidden"
|
||||
>
|
||||
<VnTableColumn
|
||||
:column="$props.column"
|
||||
default="input"
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<script setup>
|
||||
import { ref } from 'vue';
|
||||
import { useArrayData } from 'composables/useArrayData';
|
||||
const model = defineModel({ type: Object, required: true });
|
||||
const $props = defineProps({
|
||||
name: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
default: undefined,
|
||||
},
|
||||
dataKey: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
searchUrl: {
|
||||
type: String,
|
||||
default: 'params',
|
||||
},
|
||||
vertical: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const hover = ref();
|
||||
const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
|
||||
|
||||
async function orderBy(name, direction) {
|
||||
switch (direction) {
|
||||
case 'DESC':
|
||||
direction = undefined;
|
||||
break;
|
||||
case undefined:
|
||||
direction = 'ASC';
|
||||
break;
|
||||
case 'ASC':
|
||||
direction = 'DESC';
|
||||
break;
|
||||
}
|
||||
if (!direction) return await arrayData.deleteOrder(name);
|
||||
await arrayData.addOrder($props.name, direction);
|
||||
}
|
||||
|
||||
defineExpose({ orderBy });
|
||||
</script>
|
||||
<template>
|
||||
<div
|
||||
@mouseenter="hover = true"
|
||||
@mouseleave="hover = false"
|
||||
@click="orderBy(name, model?.direction)"
|
||||
class="row items-center no-wrap cursor-pointer"
|
||||
>
|
||||
<span>{{ label }}</span>
|
||||
<QChip
|
||||
v-if="name != 'tableStatus'"
|
||||
:label="!vertical && model?.index"
|
||||
:icon="
|
||||
(model?.index || hover) && !vertical
|
||||
? model?.direction == 'DESC'
|
||||
? 'arrow_downward'
|
||||
: 'arrow_upward'
|
||||
: undefined
|
||||
"
|
||||
:size="vertical ? '' : 'sm'"
|
||||
:class="[
|
||||
model?.index ? 'color-vn-text' : 'bg-transparent',
|
||||
vertical ? 'q-px-none' : '',
|
||||
]"
|
||||
class="no-box-shadow q-mr-lg"
|
||||
:clickable="true"
|
||||
>
|
||||
<div
|
||||
class="column flex-center"
|
||||
v-if="vertical"
|
||||
:style="!model?.index && 'color: #5d5d5d'"
|
||||
>
|
||||
{{ model?.index }}
|
||||
<QIcon
|
||||
:name="
|
||||
model?.index
|
||||
? model?.direction == 'DESC'
|
||||
? 'arrow_downward'
|
||||
: 'arrow_upward'
|
||||
: 'swap_vert'
|
||||
"
|
||||
size="xs"
|
||||
/>
|
||||
</div>
|
||||
</QChip>
|
||||
</div>
|
||||
</template>
|
|
@ -13,7 +13,8 @@ import VnLv from 'components/ui/VnLv.vue';
|
|||
import VnTableColumn from 'components/VnTable/VnColumn.vue';
|
||||
import VnTableFilter from 'components/VnTable/VnFilter.vue';
|
||||
import VnTableChip from 'components/VnTable/VnChip.vue';
|
||||
import TableVisibleColumns from 'src/components/VnTable/VnVisibleColumn.vue';
|
||||
import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
|
||||
import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
|
||||
|
||||
const $props = defineProps({
|
||||
columns: {
|
||||
|
@ -93,6 +94,7 @@ const mode = ref(DEFAULT_MODE);
|
|||
const selected = ref([]);
|
||||
const routeQuery = JSON.parse(route?.query[$props.searchUrl] ?? '{}');
|
||||
const params = ref({ ...routeQuery, ...routeQuery.filter?.where });
|
||||
const orders = ref(parseOrder(routeQuery.filter?.order));
|
||||
const CrudModelRef = ref({});
|
||||
const showForm = ref(false);
|
||||
const splittedColumns = ref({ columns: [] });
|
||||
|
@ -140,11 +142,15 @@ function setUserParams(watchedParams) {
|
|||
if (!watchedParams) return;
|
||||
|
||||
if (typeof watchedParams == 'string') watchedParams = JSON.parse(watchedParams);
|
||||
const where = JSON.parse(watchedParams?.filter)?.where;
|
||||
const filter = JSON.parse(watchedParams?.filter);
|
||||
const where = filter?.where;
|
||||
const order = filter?.order;
|
||||
|
||||
watchedParams = { ...watchedParams, ...where };
|
||||
delete watchedParams.filter;
|
||||
delete params.value?.filter;
|
||||
params.value = { ...params.value, ...watchedParams };
|
||||
orders.value = parseOrder(order);
|
||||
}
|
||||
|
||||
function splitColumns(columns) {
|
||||
|
@ -204,6 +210,17 @@ function getColAlign(col) {
|
|||
return 'text-' + (col.align ?? 'left');
|
||||
}
|
||||
|
||||
function parseOrder(urlOrders) {
|
||||
const orderObject = {};
|
||||
if (!urlOrders) return orderObject;
|
||||
if (typeof urlOrders == 'string') urlOrders = [urlOrders];
|
||||
for (const [index, orders] of urlOrders.entries()) {
|
||||
const [name, direction] = orders.split(' ');
|
||||
orderObject[name] = { direction, index: index + 1 };
|
||||
}
|
||||
return orderObject;
|
||||
}
|
||||
|
||||
const emit = defineEmits(['onFetch', 'update:selected', 'saveChanges']);
|
||||
defineExpose({
|
||||
reload,
|
||||
|
@ -229,14 +246,25 @@ defineExpose({
|
|||
:redirect="!!redirect"
|
||||
>
|
||||
<template #body>
|
||||
<div
|
||||
class="row no-wrap flex-center"
|
||||
v-for="col of splittedColumns.columns"
|
||||
:key="col.id"
|
||||
>
|
||||
<VnTableFilter
|
||||
:column="col"
|
||||
:data-key="$attrs['data-key']"
|
||||
v-for="col of splittedColumns.columns"
|
||||
:key="col.id"
|
||||
v-model="params[columnName(col)]"
|
||||
:search-url="searchUrl"
|
||||
/>
|
||||
<VnTableOrder
|
||||
v-model="orders[col.name]"
|
||||
:name="col.name"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
:vertical="true"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<slot
|
||||
name="moreFilterPanel"
|
||||
|
@ -289,7 +317,7 @@ defineExpose({
|
|||
<slot name="top-left"></slot>
|
||||
</template>
|
||||
<template #top-right>
|
||||
<TableVisibleColumns
|
||||
<VnVisibleColumn
|
||||
v-if="isTableMode"
|
||||
v-model="splittedColumns.columns"
|
||||
:table-code="tableCode ?? route.name"
|
||||
|
@ -317,15 +345,21 @@ defineExpose({
|
|||
style="min-width: 100px"
|
||||
>
|
||||
<div
|
||||
class="q-pt-sm q-px-sm ellipsis"
|
||||
class="column self-start q-ml-xs ellipsis"
|
||||
:class="`text-${col?.align ?? 'left'}`"
|
||||
:style="
|
||||
$props.columnSearch && col.columnFilter == false
|
||||
? { 'min-height': 72 + 'px' }
|
||||
: ''
|
||||
"
|
||||
style="height: 75px"
|
||||
>
|
||||
{{ col?.label }}
|
||||
<div
|
||||
class="row items-center no-wrap"
|
||||
style="height: 30px"
|
||||
>
|
||||
<VnTableOrder
|
||||
v-model="orders[col.name]"
|
||||
:name="col.name"
|
||||
:label="col?.label"
|
||||
:data-key="$attrs['data-key']"
|
||||
:search-url="searchUrl"
|
||||
/>
|
||||
</div>
|
||||
<VnTableFilter
|
||||
v-if="$props.columnSearch"
|
||||
|
@ -335,6 +369,7 @@ defineExpose({
|
|||
v-model="params[columnName(col)]"
|
||||
:search-url="searchUrl"
|
||||
/>
|
||||
</div>
|
||||
</QTh>
|
||||
</template>
|
||||
<template #header-cell-tableActions>
|
||||
|
@ -561,6 +596,10 @@ es:
|
|||
color: var(--vn-text-color);
|
||||
}
|
||||
|
||||
.color-vn-text {
|
||||
color: var(--vn-text-color);
|
||||
}
|
||||
|
||||
.q-table--dark .q-table__bottom,
|
||||
.q-table--dark thead,
|
||||
.q-table--dark tr,
|
||||
|
|
|
@ -127,7 +127,7 @@ onMounted(async () => {
|
|||
});
|
||||
</script>
|
||||
<template>
|
||||
<QBtn icon="vn:visible_columns" class="bg-vn-section-color q-mr-md" dense>
|
||||
<QBtn icon="vn:visible_columns" class="bg-vn-section-color q-mr-md q-px-sm" dense>
|
||||
<QPopupProxy ref="popupProxyRef">
|
||||
<QCard class="column q-pa-md">
|
||||
<QIcon name="info" size="sm" class="info-icon">
|
||||
|
|
|
@ -24,10 +24,11 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
const searchUrl = store.searchUrl;
|
||||
if (query[searchUrl]) {
|
||||
const params = JSON.parse(query[searchUrl]);
|
||||
const filter = params?.filter;
|
||||
const filter = params?.filter && JSON.parse(params?.filter ?? '{}');
|
||||
delete params.filter;
|
||||
store.userParams = { ...params, ...store.userParams };
|
||||
store.userFilter = { ...JSON.parse(filter ?? '{}'), ...store.userFilter };
|
||||
store.userFilter = { ...filter, ...store.userFilter };
|
||||
if (filter.order) store.order = filter.order;
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -69,7 +70,6 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
canceller = new AbortController();
|
||||
|
||||
const filter = {
|
||||
order: store.order,
|
||||
limit: store.limit,
|
||||
};
|
||||
|
||||
|
@ -94,6 +94,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
|
||||
Object.assign(params, userParams);
|
||||
params.filter.skip = store.skip;
|
||||
if (store.order && store.order.length) params.filter.order = store.order;
|
||||
else delete params.filter.order;
|
||||
|
||||
params.filter = JSON.stringify(params.filter);
|
||||
store.currentFilter = params;
|
||||
|
@ -171,6 +173,37 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
await addFilter({ filter: { where } });
|
||||
}
|
||||
|
||||
async function addOrder(field, direction = 'ASC') {
|
||||
const newOrder = field + ' ' + direction;
|
||||
let order = store.order ?? [];
|
||||
if (typeof order == 'string') order = [order];
|
||||
|
||||
let index = order.findIndex((o) => o.split(' ')[0] === field);
|
||||
if (index > -1) {
|
||||
order[index] = newOrder;
|
||||
} else {
|
||||
index = order.length;
|
||||
order.push(newOrder);
|
||||
}
|
||||
|
||||
store.order = order;
|
||||
fetch({ append: false, updateRouter: true });
|
||||
index++;
|
||||
|
||||
return { index, order };
|
||||
}
|
||||
|
||||
async function deleteOrder(field) {
|
||||
let order = store.order ?? [];
|
||||
if (typeof order == 'string') order = [order];
|
||||
|
||||
const index = order.findIndex((o) => o.split(' ')[0] === field);
|
||||
if (index > -1) order.splice(index, 1);
|
||||
|
||||
store.order = order;
|
||||
fetch({ append: false, updateRouter: true });
|
||||
}
|
||||
|
||||
function sanitizerParams(params, exprBuilder) {
|
||||
for (const param in params) {
|
||||
if (params[param] === '' || params[param] === null) {
|
||||
|
@ -240,6 +273,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|||
applyFilter,
|
||||
addFilter,
|
||||
addFilterWhere,
|
||||
addOrder,
|
||||
deleteOrder,
|
||||
refresh,
|
||||
destroy,
|
||||
loadMore,
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useRouter } from 'vue-router';
|
|||
import * as vueRouter from 'vue-router';
|
||||
|
||||
describe('useArrayData', () => {
|
||||
const filter = '{"order":"","limit":10,"skip":0}';
|
||||
const filter = '{"limit":10,"skip":0}';
|
||||
const params = { supplierFk: 2 };
|
||||
beforeEach(() => {
|
||||
vi.spyOn(useRouter(), 'replace');
|
||||
|
|
Loading…
Reference in New Issue