feat: sorting, searching
This commit is contained in:
@@ -31,7 +31,6 @@ const load = async () => {
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await load();
|
await load();
|
||||||
console.log(await SortedByOrder({"Comments": "DESC"}))
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const scheme: Scheme<Author> = reactive({
|
const scheme: Scheme<Author> = reactive({
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ const pushOrRemove = (option: T) => {
|
|||||||
} else {
|
} else {
|
||||||
selected.value.push(option)
|
selected.value.push(option)
|
||||||
}
|
}
|
||||||
//setNullIds()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const setNullIds = () => {
|
const setNullIds = () => {
|
||||||
@@ -27,12 +26,11 @@ const setNullIds = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
//onMounted(setNullIds)
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="">
|
<div class="">
|
||||||
<ul class="max-h-48 h-auto overflow-y-auto background rounded-md p-3 w-full border-gray-500 border">
|
<ul class="max-h-48 h-auto overflow-y-auto background rounded-md p-3 w-full native-border">
|
||||||
<li v-for="option in options" :key="option.Id" class="flex items-center gap-2">
|
<li v-for="option in options" :key="option.Id" class="flex items-center gap-2">
|
||||||
<Checkbox :checked="selected.some(item => item.Id == option.Id)" @click="pushOrRemove(option)" />
|
<Checkbox :checked="selected.some(item => item.Id == option.Id)" @click="pushOrRemove(option)" />
|
||||||
<label :for="option.Id.toString()">{{ structView(option, path) }}</label>
|
<label :for="option.Id.toString()">{{ structView(option, path) }}</label>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import Table from "../table/Table.vue";
|
import Table from "../table/Table.vue";
|
||||||
import { onMounted, reactive } from "vue";
|
import {onMounted, reactive, watch} from "vue";
|
||||||
import { getDefaultValues } from "../utils/structs/defaults.util";
|
import { getDefaultValues } from "../utils/structs/defaults.util";
|
||||||
import Service from "./post.service.ts";
|
import Service from "./post.service.ts";
|
||||||
import type { Scheme } from "../types/scheme.type";
|
import type { Scheme } from "../types/scheme.type";
|
||||||
@@ -16,6 +16,7 @@ const posttypeService = new PosttypeService();
|
|||||||
|
|
||||||
import CommentService from "../comment/comment.service.ts";
|
import CommentService from "../comment/comment.service.ts";
|
||||||
import { SortedByOrder } from "../../bindings/app/internal/services/postservice.ts";
|
import { SortedByOrder } from "../../bindings/app/internal/services/postservice.ts";
|
||||||
|
import {getDefaultSortOptions} from "../utils/structs/default-sort-options.util.ts";
|
||||||
|
|
||||||
const commentService = new CommentService();
|
const commentService = new CommentService();
|
||||||
|
|
||||||
@@ -32,13 +33,12 @@ const load = async () => {
|
|||||||
(scheme as any).Comments.type!.nested!.values =
|
(scheme as any).Comments.type!.nested!.values =
|
||||||
await commentService.readAll();
|
await commentService.readAll();
|
||||||
|
|
||||||
items.value = await service.readAll();
|
items.value = await service.sort(sortOptions.value) ;
|
||||||
return items.value;
|
return items.value;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
await load();
|
await load();
|
||||||
console.log(await SortedByOrder({ "Author": "DESC", "Text": "ASC" }));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const scheme: Scheme<Post> = reactive({
|
const scheme: Scheme<Post> = reactive({
|
||||||
@@ -116,9 +116,6 @@ const scheme: Scheme<Post> = reactive({
|
|||||||
values: [],
|
values: [],
|
||||||
field: ["Text"],
|
field: ["Text"],
|
||||||
},
|
},
|
||||||
},
|
|
||||||
customWindow: {
|
|
||||||
create: true,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -132,12 +129,16 @@ const validate: Validate<Post> = (entity) => {
|
|||||||
status: "success",
|
status: "success",
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const search = async (input: string) => {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const sortOptions = ref(getDefaultSortOptions(scheme))
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Table :scheme :service :get-defaults :load :items :validate >
|
<Table :scheme :service :get-defaults :load :items :validate @on-search="search" v-model:sort-options="sortOptions"></Table>
|
||||||
<template #CommentsCreate="{ data }">
|
|
||||||
<p>{{ data }}</p>
|
|
||||||
</template>
|
|
||||||
</Table>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -4,10 +4,11 @@ import {
|
|||||||
Delete,
|
Delete,
|
||||||
GetById,
|
GetById,
|
||||||
Update,
|
Update,
|
||||||
Count,
|
Count, SortedByOrder,
|
||||||
} from "../../bindings/app/internal/services/postservice.ts";
|
} from "../../bindings/app/internal/services/postservice.ts";
|
||||||
import type { Post } from "../../bindings/app/internal/services";
|
import type { Post } from "../../bindings/app/internal/services";
|
||||||
import type { IService } from "../types/service.type.ts";
|
import type { IService } from "../types/service.type.ts";
|
||||||
|
import type {SortOptions} from "../types/sort-options.type.ts";
|
||||||
|
|
||||||
export default class PostService implements IService<Post> {
|
export default class PostService implements IService<Post> {
|
||||||
async read(id: number) {
|
async read(id: number) {
|
||||||
@@ -33,4 +34,8 @@ export default class PostService implements IService<Post> {
|
|||||||
async count() {
|
async count() {
|
||||||
return await Count();
|
return await Count();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sort(options: SortOptions<Post>) {
|
||||||
|
return await SortedByOrder(options) as Post[]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,25 @@
|
|||||||
|
|
||||||
html, body, .background {
|
html, body, .background {
|
||||||
background: white;
|
background: white;
|
||||||
|
color: rgb(51, 65, 85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.secondary-background {
|
||||||
|
background: var(--p-content-background)
|
||||||
|
}
|
||||||
|
|
||||||
|
.native-border {
|
||||||
|
border: 1px solid var(--p-select-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html, body, .background {
|
html, body, .background {
|
||||||
background: #121212;
|
background: #121212;
|
||||||
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.native-border {
|
||||||
|
border: 1px solid rgb(100, 100, 109);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,7 @@ async function handleSave() {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<Dialog v-model:visible="showCreate">
|
<Dialog v-model:visible="showCreate" class="w-[500px]">
|
||||||
<template #header>
|
<template #header>
|
||||||
<h1>{{ props.updateMode ? 'Изменить' : 'Создать' }} {{ props.name?.toLowerCase() }}</h1>
|
<h1>{{ props.updateMode ? 'Изменить' : 'Создать' }} {{ props.name?.toLowerCase() }}</h1>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
25
frontend/src/table/SortingOptions.vue
Normal file
25
frontend/src/table/SortingOptions.vue
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<script setup lang="ts" generic="T extends IEntity">
|
||||||
|
import type {IEntity} from "../types/entity.type.ts";
|
||||||
|
import type {SortOptions} from "../types/sort-options.type.ts";
|
||||||
|
import type {Scheme} from "../types/scheme.type.ts";
|
||||||
|
import {Select} from 'primevue'
|
||||||
|
import {watch} from "vue";
|
||||||
|
|
||||||
|
|
||||||
|
const options = defineModel<SortOptions<T>>()
|
||||||
|
const optionsKeys = Object.keys(options.value as T) as (keyof T)[]
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
scheme: Scheme<T>
|
||||||
|
load: () => Promise<T[]>
|
||||||
|
}>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ul class="flex flex-col gap-2 native-border secondary-background p-3 rounded-md">
|
||||||
|
<li v-for="optionKey in optionsKeys" class="flex items-center justify-between w-full">
|
||||||
|
<h1>{{ scheme[optionKey].russian }}</h1>
|
||||||
|
<Select size="small" class="w-24" :options="['ASC', 'DESC']" v-model="options![optionKey]" @value-change="load"></Select>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
@@ -1,20 +1,18 @@
|
|||||||
<script setup lang="ts" generic="T extends IEntity">
|
<script setup lang="ts" generic="T extends IEntity">
|
||||||
import { onMounted, ref, watch, type UnwrapRef } from "vue";
|
import {onMounted, ref, watch, type UnwrapRef} from "vue";
|
||||||
import type { TableProps } from "../types/table-props.type";
|
import type {TableProps} from "../types/table-props.type";
|
||||||
import { DataTable, Column, Button } from "primevue";
|
import {DataTable, Column, Button, InputText} from "primevue";
|
||||||
import { manyStructsView } from "../utils/structs/structs-view.util";
|
import {manyStructsView} from "../utils/structs/structs-view.util";
|
||||||
import type { TableEmits } from "../types/table-emits.type";
|
import type {TableEmits} from "../types/table-emits.type";
|
||||||
import FloatingButton from "../components/buttons/FloatingButton.vue";
|
import FloatingButton from "../components/buttons/FloatingButton.vue";
|
||||||
import type { IEntity } from "../types/entity.type";
|
import type {IEntity} from "../types/entity.type";
|
||||||
import DialogWindow from "./DialogWindow.vue";
|
import DialogWindow from "./DialogWindow.vue";
|
||||||
import { viewDate } from "../utils/date/view.util";
|
import {viewDate} from "../utils/date/view.util";
|
||||||
|
import SortingOptions from "./SortingOptions.vue";
|
||||||
|
import type {SortOptions} from "../types/sort-options.type.ts";
|
||||||
|
|
||||||
const props = defineProps<TableProps<T>>();
|
const props = defineProps<TableProps<T>>();
|
||||||
|
|
||||||
onMounted(async () => {
|
|
||||||
props.load()
|
|
||||||
});
|
|
||||||
|
|
||||||
type Key = keyof T;
|
type Key = keyof T;
|
||||||
const keys = Object.keys(props.scheme) as Key[];
|
const keys = Object.keys(props.scheme) as Key[];
|
||||||
const emits = defineEmits<TableEmits>();
|
const emits = defineEmits<TableEmits>();
|
||||||
@@ -76,27 +74,49 @@ watch(() => props.items, () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const input = ref('')
|
||||||
|
const sortOptions = defineModel<SortOptions<T>>('sort-options');
|
||||||
|
const showSortOptions = ref(false)
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<div class="m-2 flex flex-col items-center gap-2">
|
||||||
|
<div class="flex flex-col gap-2 relative">
|
||||||
|
<div class="flex items-center justify-center gap-2 h-10">
|
||||||
|
<Button severity="secondary" class="h-full aspect-square" @click="showSortOptions = !showSortOptions">
|
||||||
|
<span class="pi pi-sort"></span>
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<InputText class="h-full w-64" v-model="input"/>
|
||||||
|
<Button severity="secondary" class="h-full aspect-square" @click="emits('onSearch', input)">
|
||||||
|
<span class="pi pi-search"></span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<SortingOptions v-model="sortOptions as SortOptions<T>" :scheme v-if="showSortOptions"
|
||||||
|
class="absolute z-10 w-full top-full translate-y-2" :load="props.load"></SortingOptions>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
<DialogWindow :name :load :items :validate :scheme :service :get-defaults v-model:item="<T>createItem"
|
<DialogWindow :name :load :items :validate :scheme :service :get-defaults v-model:item="<T>createItem"
|
||||||
v-model:show="showCreate" @on-save="data => emits('onSave', data)"
|
v-model:show="showCreate" @on-save="data => emits('onSave', data)"
|
||||||
@on-save-create="data => emits('onSaveCreate', data)">
|
@on-save-create="data => emits('onSaveCreate', data)">
|
||||||
<template v-for="key in keys" #[key]="{ data }">
|
<template v-for="key in keys" #[key]="{ data }">
|
||||||
<slot :name="<string>key" :data></slot>
|
<slot :name="<string>key" :data="data"></slot>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="key in keys" #[createSlotName(key)]="{ data }">
|
<template v-for="key in keys" #[createSlotName(key)]="{ data }">
|
||||||
<slot :name="createSlotName(key)" :data></slot>
|
<slot :name="createSlotName(key)" :data="data"></slot>
|
||||||
</template>
|
</template>
|
||||||
</DialogWindow>
|
</DialogWindow>
|
||||||
<DialogWindow :name :load :items :validate :scheme update-mode :service :get-defaults v-model:item="<T>updateItem"
|
<DialogWindow :name :load :items :validate :scheme update-mode :service :get-defaults v-model:item="<T>updateItem"
|
||||||
v-model:show="showUpdate" @on-save="data => emits('onSave', data)"
|
v-model:show="showUpdate" @on-save="data => emits('onSave', data)"
|
||||||
@on-save-update="data => emits('onSaveUpdate', data)">
|
@on-save-update="data => emits('onSaveUpdate', data)">
|
||||||
<template v-for="key in keys" #[key]="{ data }">
|
<template v-for="key in keys" #[key]="{ data }">
|
||||||
<slot :name="<string>key" :data></slot>
|
<slot :name="<string>key" :data="data"></slot>
|
||||||
</template>
|
</template>
|
||||||
<template v-for="key in keys" #[updateSlotName(key)]="{ data }">
|
<template v-for="key in keys" #[updateSlotName(key)]="{ data }">
|
||||||
<slot :name="updateSlotName(key)" :data></slot>
|
<slot :name="updateSlotName(key)" :data="data"></slot>
|
||||||
</template>
|
</template>
|
||||||
</DialogWindow>
|
</DialogWindow>
|
||||||
<div>
|
<div>
|
||||||
@@ -137,7 +157,15 @@ watch(() => props.items, () => {
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</Column>
|
</Column>
|
||||||
|
<template #paginatorstart>
|
||||||
|
<Button severity="secondary" @click="props.load">
|
||||||
|
<span class="pi pi-refresh"></span>
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
<template #paginatorend>
|
||||||
|
<span></span>
|
||||||
|
</template>
|
||||||
</DataTable>
|
</DataTable>
|
||||||
<FloatingButton @click="handleFloatingButtonClick" />
|
<FloatingButton @click="handleFloatingButtonClick"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
6
frontend/src/types/sort-options.type.ts
Normal file
6
frontend/src/types/sort-options.type.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import type { IEntity } from "./entity.type";
|
||||||
|
import type { Sorting } from "./sorting.type";
|
||||||
|
|
||||||
|
export type SortOptions<T extends IEntity> = {
|
||||||
|
[key in keyof Partial<T>]: Sorting
|
||||||
|
}
|
||||||
1
frontend/src/types/sorting.type.ts
Normal file
1
frontend/src/types/sorting.type.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export type Sorting = "DESC" | "ASC"
|
||||||
@@ -9,4 +9,5 @@ export type TableEmits = {
|
|||||||
(e: 'onSaveUpdate', data: any): Promise<void> | void
|
(e: 'onSaveUpdate', data: any): Promise<void> | void
|
||||||
(e: 'onSaveCreate', data: any): Promise<void> | void
|
(e: 'onSaveCreate', data: any): Promise<void> | void
|
||||||
(e: 'onSave', data: any): Promise<void> | void
|
(e: 'onSave', data: any): Promise<void> | void
|
||||||
|
(e: 'onSearch', input: string): Promise<void> | void
|
||||||
}
|
}
|
||||||
16
frontend/src/utils/structs/default-sort-options.util.ts
Normal file
16
frontend/src/utils/structs/default-sort-options.util.ts
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import type { IEntity } from "../../types/entity.type";
|
||||||
|
import type { Scheme } from "../../types/scheme.type";
|
||||||
|
import type { SortOptions } from "../../types/sort-options.type";
|
||||||
|
|
||||||
|
export const getDefaultSortOptions = <T extends IEntity>(scheme: Scheme<T>): SortOptions<T> => {
|
||||||
|
const keys = Object.keys(scheme) as (keyof T)[]
|
||||||
|
const result: Partial<SortOptions<T>> = {}
|
||||||
|
|
||||||
|
keys.forEach(key => {
|
||||||
|
if (!scheme[key].hidden && key !== 'entityId' && !scheme[key].many) {
|
||||||
|
result[key] = 'ASC'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return result as SortOptions<T>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user