249 lines
7.4 KiB
JavaScript
249 lines
7.4 KiB
JavaScript
import { onMounted, ref, computed } from 'vue';
|
|
import { useRouter, useRoute } from 'vue-router';
|
|
import axios from 'axios';
|
|
import { useArrayDataStore } from 'stores/useArrayDataStore';
|
|
import { buildFilter } from 'filters/filterPanel';
|
|
|
|
const arrayDataStore = useArrayDataStore();
|
|
|
|
export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
|
|
if (!key) throw new Error('ArrayData: A key is required to use this composable');
|
|
|
|
if (!arrayDataStore.get(key)) arrayDataStore.set(key);
|
|
|
|
const store = arrayDataStore.get(key);
|
|
const route = useRoute();
|
|
const router = useRouter();
|
|
let canceller = null;
|
|
|
|
const page = ref(1);
|
|
|
|
onMounted(() => {
|
|
setOptions();
|
|
store.skip = 0;
|
|
|
|
const query = route.query;
|
|
const searchUrl = store.searchUrl;
|
|
if (query[searchUrl]) {
|
|
const params = JSON.parse(query[searchUrl]);
|
|
const filter = params?.filter;
|
|
delete params.filter;
|
|
store.userParams = { ...params, ...store.userParams };
|
|
store.userFilter = { ...JSON.parse(filter ?? '{}'), ...store.userFilter };
|
|
}
|
|
});
|
|
|
|
if (key && userOptions) setOptions();
|
|
|
|
function setOptions() {
|
|
const allowedOptions = [
|
|
'url',
|
|
'filter',
|
|
'where',
|
|
'order',
|
|
'limit',
|
|
'skip',
|
|
'userParams',
|
|
'userFilter',
|
|
'exprBuilder',
|
|
'searchUrl',
|
|
'navigate',
|
|
];
|
|
if (typeof userOptions === 'object') {
|
|
for (const option in userOptions) {
|
|
const isEmpty = userOptions[option] == null || userOptions[option] === '';
|
|
if (isEmpty || !allowedOptions.includes(option)) continue;
|
|
|
|
if (Object.hasOwn(store, option)) {
|
|
const defaultOpts = userOptions[option];
|
|
store[option] = userOptions.keepOpts?.includes(option)
|
|
? Object.assign(defaultOpts, store[option])
|
|
: defaultOpts;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
async function fetch({ append = false, updateRouter = true }) {
|
|
if (!store.url) return;
|
|
|
|
cancelRequest();
|
|
canceller = new AbortController();
|
|
|
|
const filter = {
|
|
order: store.order,
|
|
limit: store.limit,
|
|
};
|
|
|
|
let exprFilter;
|
|
let userParams = { ...store.userParams };
|
|
if (store?.exprBuilder) {
|
|
const where = buildFilter(userParams, (param, value) => {
|
|
const res = store.exprBuilder(param, value);
|
|
if (res) delete userParams[param];
|
|
return res;
|
|
});
|
|
exprFilter = where ? { where } : null;
|
|
}
|
|
|
|
Object.assign(filter, store.userFilter, exprFilter);
|
|
Object.assign(store.filter, { ...filter, skip: store.skip });
|
|
const params = {
|
|
filter: JSON.stringify(store.filter),
|
|
};
|
|
|
|
Object.assign(params, userParams);
|
|
|
|
store.currentFilter = params;
|
|
store.isLoading = true;
|
|
const response = await axios.get(store.url, {
|
|
signal: canceller.signal,
|
|
params,
|
|
});
|
|
|
|
const { limit } = filter;
|
|
store.hasMoreData = limit && response.data.length >= limit;
|
|
|
|
if (append) {
|
|
if (!store.data) store.data = [];
|
|
for (const row of response.data) store.data.push(row);
|
|
} else {
|
|
store.data = response.data;
|
|
if (!document.querySelectorAll('[role="dialog"]').length)
|
|
updateRouter && updateStateParams();
|
|
}
|
|
|
|
store.isLoading = false;
|
|
|
|
canceller = null;
|
|
return response;
|
|
}
|
|
|
|
function destroy() {
|
|
if (arrayDataStore.get(key)) {
|
|
arrayDataStore.clear(key);
|
|
}
|
|
}
|
|
|
|
function deleteOption(option) {
|
|
delete store[option];
|
|
}
|
|
|
|
function cancelRequest() {
|
|
if (canceller) {
|
|
canceller.abort();
|
|
canceller = null;
|
|
}
|
|
}
|
|
|
|
async function applyFilter({ filter, params }) {
|
|
if (filter) store.userFilter = filter;
|
|
store.filter = {};
|
|
if (params) store.userParams = { ...params };
|
|
|
|
const response = await fetch({ append: false });
|
|
return response;
|
|
}
|
|
|
|
async function addFilter({ filter, params }) {
|
|
if (filter) store.userFilter = Object.assign(store.userFilter, filter);
|
|
|
|
let userParams = { ...store.userParams, ...params };
|
|
userParams = sanitizerParams(userParams, store?.exprBuilder);
|
|
|
|
store.userParams = userParams;
|
|
store.skip = 0;
|
|
store.filter.skip = 0;
|
|
page.value = 1;
|
|
|
|
await fetch({ append: false });
|
|
return { filter, params };
|
|
}
|
|
|
|
async function addFilterWhere(where) {
|
|
const storedFilter = { ...store.userFilter };
|
|
if (!storedFilter?.where) storedFilter.where = {};
|
|
where = { ...storedFilter.where, ...where };
|
|
await addFilter({ filter: { where } });
|
|
}
|
|
|
|
function sanitizerParams(params, exprBuilder) {
|
|
for (const param in params) {
|
|
if (params[param] === '' || params[param] === null) {
|
|
delete store.userParams[param];
|
|
delete params[param];
|
|
if (store.filter?.where) {
|
|
const key = Object.keys(exprBuilder ? exprBuilder(param) : param);
|
|
if (key[0]) delete store.filter.where[key[0]];
|
|
if (Object.keys(store.filter.where).length === 0) {
|
|
delete store.filter.where;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return params;
|
|
}
|
|
|
|
async function loadMore() {
|
|
if (!store.hasMoreData) return;
|
|
|
|
store.skip = store.limit * page.value;
|
|
page.value += 1;
|
|
|
|
await fetch({ append: true });
|
|
}
|
|
|
|
async function refresh() {
|
|
if (Object.values(store.userParams).length) await fetch({ append: false });
|
|
}
|
|
|
|
function updateStateParams() {
|
|
const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
|
|
newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
|
|
|
|
if (store.navigate) {
|
|
const { customRouteRedirectName, searchText } = store.navigate;
|
|
if (customRouteRedirectName)
|
|
return router.push({
|
|
name: customRouteRedirectName,
|
|
params: { id: searchText },
|
|
});
|
|
const { matched: matches } = router.currentRoute.value;
|
|
const { path } = matches.at(-1);
|
|
|
|
const to =
|
|
store?.data?.length === 1
|
|
? path.replace(/\/(list|:id)|-list/, `/${store.data[0].id}`)
|
|
: path.replace(/:id.*/, '');
|
|
|
|
if (route.path != to) {
|
|
const pushUrl = { path: to };
|
|
if (to.endsWith('/list') || to.endsWith('/'))
|
|
pushUrl.query = newUrl.query;
|
|
destroy();
|
|
return router.push(pushUrl);
|
|
}
|
|
}
|
|
|
|
router.replace(newUrl);
|
|
}
|
|
|
|
const totalRows = computed(() => (store.data && store.data.length) || 0);
|
|
const isLoading = computed(() => store.isLoading || false);
|
|
|
|
return {
|
|
fetch,
|
|
applyFilter,
|
|
addFilter,
|
|
addFilterWhere,
|
|
refresh,
|
|
destroy,
|
|
loadMore,
|
|
store,
|
|
totalRows,
|
|
updateStateParams,
|
|
isLoading,
|
|
deleteOption,
|
|
};
|
|
}
|