mirror of
https://gitee.com/pnoker/iot-dc3.git
synced 2026-09-19 10:11:36 +08:00
refactor(composable): support server-paginated lists in usePagedList
Add an optional request handler so usePagedList can drive both client-side filtering and server-paginated tables. Migrate the settings list views (api, group, label, role, user, command, event) to consume the composable, removing duplicated pagination/sort/search boilerplate.
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
|
||||
import { reactive } from 'vue';
|
||||
|
||||
import type { Order } from '@/config/types';
|
||||
import type { Order, PageQuery, PageResult } from '@/config/types';
|
||||
|
||||
export interface PagedListPage {
|
||||
total: number;
|
||||
@@ -37,6 +37,7 @@ export interface PagedListState<T, Q extends Record<string, any> = Record<string
|
||||
export interface UsePagedListOptions<T, Q extends Record<string, any> = Record<string, any>> {
|
||||
pageSize?: number;
|
||||
sortColumn?: string;
|
||||
request?: (query: PageQuery & Partial<Q>) => Promise<R<PageResult<T>>>;
|
||||
filter?: (rows: T[], query: Partial<Q>) => T[];
|
||||
sortValue?: (row: T) => string | number | null | undefined;
|
||||
}
|
||||
@@ -70,15 +71,42 @@ export const usePagedList = <T, Q extends Record<string, any> = Record<string, a
|
||||
applyFilters();
|
||||
};
|
||||
|
||||
const load = async () => {
|
||||
if (!options.request) {
|
||||
applyFilters();
|
||||
return;
|
||||
}
|
||||
|
||||
state.loading = true;
|
||||
try {
|
||||
const response = await options.request({ page: state.page, ...state.query } as PageQuery & Partial<Q>);
|
||||
const data = response.data || ({ records: [], total: 0 } as PageResult<T>);
|
||||
state.listData = data.records || [];
|
||||
state.page.total = data.total || 0;
|
||||
} catch {
|
||||
// handled globally
|
||||
} finally {
|
||||
state.loading = false;
|
||||
}
|
||||
};
|
||||
|
||||
const search = (params?: Partial<Q>) => {
|
||||
state.query = params || {};
|
||||
state.page.current = 1;
|
||||
if (options.request) {
|
||||
void load();
|
||||
return;
|
||||
}
|
||||
applyFilters();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
state.query = {};
|
||||
state.page.current = 1;
|
||||
if (options.request) {
|
||||
void load();
|
||||
return;
|
||||
}
|
||||
applyFilters();
|
||||
};
|
||||
|
||||
@@ -86,6 +114,11 @@ export const usePagedList = <T, Q extends Record<string, any> = Record<string, a
|
||||
state.order = !state.order;
|
||||
state.page.orders = [{ column: options.sortColumn ?? 'create_time', asc: state.order }];
|
||||
|
||||
if (options.request) {
|
||||
void load();
|
||||
return;
|
||||
}
|
||||
|
||||
if (options.sortValue) {
|
||||
const asc = state.order;
|
||||
state.allData = [...state.allData].sort((a, b) => {
|
||||
@@ -102,11 +135,19 @@ export const usePagedList = <T, Q extends Record<string, any> = Record<string, a
|
||||
const sizeChange = (size: number) => {
|
||||
state.page.size = size;
|
||||
state.page.current = 1;
|
||||
if (options.request) {
|
||||
void load();
|
||||
return;
|
||||
}
|
||||
applyFilters();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
state.page.current = current;
|
||||
if (options.request) {
|
||||
void load();
|
||||
return;
|
||||
}
|
||||
applyFilters();
|
||||
};
|
||||
|
||||
@@ -123,6 +164,7 @@ export const usePagedList = <T, Q extends Record<string, any> = Record<string, a
|
||||
state,
|
||||
setAllData,
|
||||
applyFilters,
|
||||
load,
|
||||
search,
|
||||
reset,
|
||||
sort,
|
||||
|
||||
@@ -14,14 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, reactive } from 'vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { listApi } from '@/api/api';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn } from '@/utils/dateUtil';
|
||||
|
||||
import type { ApiRecord, Order } from '@/config/types';
|
||||
import type { ApiRecord } from '@/config/types';
|
||||
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
import EnableTag from '@/components/tag/EnableTag.vue';
|
||||
@@ -41,71 +42,26 @@ export default defineComponent({
|
||||
const { t } = useI18n();
|
||||
const router = useRouter();
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as ApiRecord[],
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
const {
|
||||
state: reactiveData,
|
||||
load,
|
||||
search,
|
||||
reset,
|
||||
sort,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<ApiRecord, Record<string, unknown>>({
|
||||
request: (query) => listApi(query),
|
||||
});
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listApi({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.catch(() => {
|
||||
// handled globally
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = params || {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openDetail = (row: ApiRecord) => {
|
||||
router.push({ name: 'settingsApiDetail', query: { id: String(row.id) } }).catch(() => {
|
||||
// handled globally
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
load();
|
||||
|
||||
return {
|
||||
|
||||
@@ -155,18 +155,23 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { getCommandHistoryById, listCommandHistory } from '@/api/command';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn, timestampLabel } from '@/utils/dateUtil';
|
||||
import { prettyJson } from '@/utils/jsonUtil';
|
||||
import type { CommandHistory, Order } from '@/config/types';
|
||||
import type { CommandHistory } from '@/config/types';
|
||||
import ToolCard from '@/components/card/tool/ToolCard.vue';
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
import { cleanSearchParams, resetSearchForm } from '@/utils/searchParamUtil';
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as CommandHistory[],
|
||||
query: {} as Record<string, unknown>,
|
||||
page: { total: 0, size: 12, current: 1, orders: [] as Order[] },
|
||||
const {
|
||||
state: reactiveData,
|
||||
load,
|
||||
search,
|
||||
reset,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<CommandHistory, Record<string, unknown>>({
|
||||
request: (query) => listCommandHistory(query),
|
||||
});
|
||||
|
||||
const formData = reactive<Record<string, string>>({});
|
||||
@@ -175,43 +180,17 @@
|
||||
|
||||
const formatJson = (value: unknown) => prettyJson(value);
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listCommandHistory({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const onSearch = (data: Record<string, string>) => {
|
||||
reactiveData.query = cleanSearchParams(data);
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
search(cleanSearchParams(data));
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
resetSearchForm(formData, {});
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
reset();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
const openDetail = (row: CommandHistory) => {
|
||||
getCommandHistoryById(row.recordId)
|
||||
.then((res) => {
|
||||
|
||||
@@ -99,7 +99,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {
|
||||
addCommand,
|
||||
@@ -110,11 +110,12 @@
|
||||
updateCommand,
|
||||
updateCommandParam,
|
||||
} from '@/api/command';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampLabel } from '@/utils/dateUtil';
|
||||
import { failMessage, successMessage } from '@/utils/notificationUtil';
|
||||
import { commandTimeoutLabel } from '@/utils/thingModelFormatUtil';
|
||||
import { isNull } from '@/utils/validationUtil';
|
||||
import type { CommandForm, CommandParamRecord, CommandRecord, Order } from '@/config/types';
|
||||
import type { CommandForm, CommandParamRecord, CommandRecord } from '@/config/types';
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
import SkeletonCard from '@/components/card/skeleton/SkeletonCard.vue';
|
||||
import EnableTag from '@/components/tag/EnableTag.vue';
|
||||
@@ -142,66 +143,46 @@
|
||||
const canManage = computed(() => props.embedded === '' || props.embedded === 'edit');
|
||||
const hasData = computed(() => !reactiveData.loading && reactiveData.listData.length < 1);
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as CommandRecord[],
|
||||
detailVisible: false,
|
||||
detailRecord: null as CommandRecord | null,
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
});
|
||||
|
||||
const withFixedQuery = (params: Record<string, unknown> = {}) => {
|
||||
const q = { ...params };
|
||||
if (!isNull(props.profileId)) q.profileId = props.profileId;
|
||||
return q;
|
||||
};
|
||||
|
||||
const {
|
||||
state,
|
||||
load,
|
||||
search: searchList,
|
||||
reset: resetList,
|
||||
sort,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<CommandRecord, Record<string, unknown>>({
|
||||
request: (query) => listCommand(withFixedQuery(query)),
|
||||
});
|
||||
|
||||
const reactiveData = state as typeof state & {
|
||||
detailVisible: boolean;
|
||||
detailRecord: CommandRecord | null;
|
||||
};
|
||||
reactiveData.detailVisible = false;
|
||||
reactiveData.detailRecord = null;
|
||||
|
||||
const withFixedProfile = (form: CommandForm) => {
|
||||
const profileId = !isNull(props.profileId) ? props.profileId : form.profileId;
|
||||
return isNull(profileId) ? { ...form } : { ...form, profileId };
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
const query = withFixedQuery(reactiveData.query);
|
||||
listCommand({ page: reactiveData.page, ...query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = withFixedQuery(params || {});
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
searchList(params || {});
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = withFixedQuery({});
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
resetList();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openAdd = () => editRef.value?.show(props.profileId);
|
||||
const openDetail = (row: CommandRecord) => {
|
||||
reactiveData.detailRecord = row;
|
||||
@@ -299,16 +280,6 @@
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
const preHandle = () => {
|
||||
emit('pre-handle');
|
||||
};
|
||||
|
||||
@@ -135,19 +135,24 @@
|
||||
<script lang="ts" setup>
|
||||
import { reactive, ref } from 'vue';
|
||||
import { getEventHistoryById, listEventHistory } from '@/api/event';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn, timestampLabel } from '@/utils/dateUtil';
|
||||
import { prettyJson } from '@/utils/jsonUtil';
|
||||
import { eventLevelLabel, eventLevelTag, eventTypeLabel } from '@/utils/thingModelFormatUtil';
|
||||
import type { EventHistory, Order } from '@/config/types';
|
||||
import type { EventHistory } from '@/config/types';
|
||||
import ToolCard from '@/components/card/tool/ToolCard.vue';
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
import { cleanSearchParams, resetSearchForm } from '@/utils/searchParamUtil';
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as EventHistory[],
|
||||
query: {} as Record<string, unknown>,
|
||||
page: { total: 0, size: 12, current: 1, orders: [] as Order[] },
|
||||
const {
|
||||
state: reactiveData,
|
||||
load,
|
||||
search,
|
||||
reset,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<EventHistory, Record<string, unknown>>({
|
||||
request: (query) => listEventHistory(query),
|
||||
});
|
||||
|
||||
const formData = reactive<Record<string, string>>({});
|
||||
@@ -156,43 +161,17 @@
|
||||
|
||||
const formatJson = (value: unknown) => prettyJson(value);
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listEventHistory({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const onSearch = (data: Record<string, string>) => {
|
||||
reactiveData.query = cleanSearchParams(data);
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
search(cleanSearchParams(data));
|
||||
};
|
||||
|
||||
const onReset = () => {
|
||||
resetSearchForm(formData, {});
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
reset();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
const openDetail = (row: EventHistory) => {
|
||||
getEventHistoryById(row.recordId)
|
||||
.then((res) => {
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, reactive, ref, watch } from 'vue';
|
||||
import { computed, ref, watch } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import {
|
||||
addEvent,
|
||||
@@ -107,11 +107,12 @@
|
||||
updateEvent,
|
||||
updateEventParam,
|
||||
} from '@/api/event';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampLabel } from '@/utils/dateUtil';
|
||||
import { failMessage, successMessage } from '@/utils/notificationUtil';
|
||||
import { eventLevelLabel, eventTypeLabel } from '@/utils/thingModelFormatUtil';
|
||||
import { isNull } from '@/utils/validationUtil';
|
||||
import type { EventForm, EventParamRecord, EventRecord, Order } from '@/config/types';
|
||||
import type { EventForm, EventParamRecord, EventRecord } from '@/config/types';
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
import SkeletonCard from '@/components/card/skeleton/SkeletonCard.vue';
|
||||
import EnableTag from '@/components/tag/EnableTag.vue';
|
||||
@@ -139,66 +140,46 @@
|
||||
const canManage = computed(() => props.embedded === '' || props.embedded === 'edit');
|
||||
const hasData = computed(() => !reactiveData.loading && reactiveData.listData.length < 1);
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as EventRecord[],
|
||||
detailVisible: false,
|
||||
detailRecord: null as EventRecord | null,
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
});
|
||||
|
||||
const withFixedQuery = (params: Record<string, unknown> = {}) => {
|
||||
const q = { ...params };
|
||||
if (!isNull(props.profileId)) q.profileId = props.profileId;
|
||||
return q;
|
||||
};
|
||||
|
||||
const {
|
||||
state,
|
||||
load,
|
||||
search: searchList,
|
||||
reset: resetList,
|
||||
sort,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<EventRecord, Record<string, unknown>>({
|
||||
request: (query) => listEvent(withFixedQuery(query)),
|
||||
});
|
||||
|
||||
const reactiveData = state as typeof state & {
|
||||
detailVisible: boolean;
|
||||
detailRecord: EventRecord | null;
|
||||
};
|
||||
reactiveData.detailVisible = false;
|
||||
reactiveData.detailRecord = null;
|
||||
|
||||
const withFixedProfile = (form: EventForm) => {
|
||||
const profileId = !isNull(props.profileId) ? props.profileId : form.profileId;
|
||||
return isNull(profileId) ? { ...form } : { ...form, profileId };
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
const query = withFixedQuery(reactiveData.query);
|
||||
listEvent({ page: reactiveData.page, ...query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = withFixedQuery(params || {});
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
searchList(params || {});
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = withFixedQuery({});
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
resetList();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openAdd = () => editRef.value?.show(props.profileId);
|
||||
const openDetail = (row: EventRecord) => {
|
||||
reactiveData.detailRecord = row;
|
||||
@@ -292,16 +273,6 @@
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
const preHandle = () => {
|
||||
emit('pre-handle');
|
||||
};
|
||||
|
||||
@@ -19,10 +19,10 @@ import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { addGroup, deleteGroup, listGroup, updateGroup } from '@/api/group';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn } from '@/utils/dateUtil';
|
||||
import { successMessage } from '@/utils/notificationUtil';
|
||||
|
||||
import type { Order } from '@/config/types';
|
||||
import type { GroupForm, GroupRecord } from '@/config/types/manager';
|
||||
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
@@ -43,20 +43,16 @@ export default defineComponent({
|
||||
const router = useRouter();
|
||||
const editRef = ref<InstanceType<typeof groupEditForm>>();
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as GroupRecord[],
|
||||
groupOptions: [] as GroupRecord[],
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
const { state, load, search, reset, sort, sizeChange, currentChange } = usePagedList<
|
||||
GroupRecord,
|
||||
Record<string, unknown>
|
||||
>({
|
||||
request: (query) => listGroup(query),
|
||||
});
|
||||
|
||||
const reactiveData = state as typeof state & { groupOptions: GroupRecord[] };
|
||||
reactiveData.groupOptions = [];
|
||||
|
||||
const parentNameMap = reactive<Record<string, string>>({});
|
||||
|
||||
const loadOptions = () => {
|
||||
@@ -74,50 +70,16 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listGroup({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.catch(() => {
|
||||
// handled globally
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const parentName = (id: string | number | null | undefined) => {
|
||||
if (!id || String(id) === '0') return t('settings.group.rootGroup');
|
||||
return parentNameMap[String(id)] || String(id);
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = params || {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const refresh = () => {
|
||||
load();
|
||||
loadOptions();
|
||||
};
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openAdd = () => editRef.value?.show();
|
||||
const openDetail = (row: GroupRecord) => {
|
||||
router.push({ name: 'settingsGroupDetail', query: { id: String(row.id) } }).catch(() => {
|
||||
@@ -161,16 +123,6 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
load();
|
||||
loadOptions();
|
||||
|
||||
|
||||
@@ -14,15 +14,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, reactive, ref } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { addLabel, deleteLabel, listLabel, updateLabel } from '@/api/label';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn } from '@/utils/dateUtil';
|
||||
import { successMessage } from '@/utils/notificationUtil';
|
||||
|
||||
import type { Order } from '@/config/types';
|
||||
import type { LabelForm, LabelRecord } from '@/config/types/manager';
|
||||
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
@@ -43,55 +43,20 @@ export default defineComponent({
|
||||
const router = useRouter();
|
||||
const editRef = ref<InstanceType<typeof labelEditForm>>();
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as LabelRecord[],
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
const {
|
||||
state: reactiveData,
|
||||
load,
|
||||
search,
|
||||
reset,
|
||||
sort,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<LabelRecord, Record<string, unknown>>({
|
||||
request: (query) => listLabel(query),
|
||||
});
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listLabel({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.catch(() => {
|
||||
// handled globally
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = params || {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openAdd = () => editRef.value?.show();
|
||||
const openDetail = (row: LabelRecord) => {
|
||||
router.push({ name: 'settingsLabelDetail', query: { id: String(row.id) } }).catch(() => {
|
||||
@@ -135,16 +100,6 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
load();
|
||||
|
||||
return {
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, reactive, ref } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { addRole, deleteRole, listRole, listRoleTree, updateRole } from '@/api/role';
|
||||
import { addRoleResourceBind, deleteRoleResourceBind } from '@/api/roleResourceBind';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn } from '@/utils/dateUtil';
|
||||
import { successMessage } from '@/utils/notificationUtil';
|
||||
|
||||
import type { Order } from '@/config/types';
|
||||
import type { RoleForm, RoleRecord } from '@/config/types/auth';
|
||||
|
||||
import BlankCard from '@/components/card/blank/BlankCard.vue';
|
||||
@@ -48,20 +48,16 @@ export default defineComponent({
|
||||
const editRef = ref<InstanceType<typeof roleEditForm>>();
|
||||
const assignRef = ref<InstanceType<typeof roleAssignResources>>();
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as RoleRecord[],
|
||||
roleTreeData: [] as RoleRecord[],
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
const { state, load, search, reset, sort, sizeChange, currentChange } = usePagedList<
|
||||
RoleRecord,
|
||||
Record<string, unknown>
|
||||
>({
|
||||
request: (query) => listRole(query),
|
||||
});
|
||||
|
||||
const reactiveData = state as typeof state & { roleTreeData: RoleRecord[] };
|
||||
reactiveData.roleTreeData = [];
|
||||
|
||||
const loadTree = () => {
|
||||
listRoleTree()
|
||||
.then((res) => {
|
||||
@@ -72,42 +68,8 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listRole({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.catch(() => {
|
||||
// handled globally
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = params || {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openAdd = () => editRef.value?.show();
|
||||
const openEdit = (row: RoleRecord) => editRef.value?.showEdit(row);
|
||||
const openAssignResources = (row: RoleRecord) => assignRef.value?.show(row);
|
||||
@@ -168,16 +130,6 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
load();
|
||||
loadTree();
|
||||
|
||||
|
||||
@@ -14,16 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { defineComponent, reactive, ref } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { addUser, deleteUser, listUser, updateUser } from '@/api/user';
|
||||
import { addRoleUserBind, deleteRoleUserBind } from '@/api/roleUserBind';
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
import { timestampColumn } from '@/utils/dateUtil';
|
||||
import { successMessage } from '@/utils/notificationUtil';
|
||||
|
||||
import type { Order } from '@/config/types';
|
||||
import type { UserForm, UserRecord } from '@/config/types/auth';
|
||||
|
||||
import userTool from './tool/UserTool.vue';
|
||||
@@ -48,55 +48,20 @@ export default defineComponent({
|
||||
const editRef = ref<InstanceType<typeof userEditForm>>();
|
||||
const assignRef = ref<InstanceType<typeof userAssignRoles>>();
|
||||
|
||||
const reactiveData = reactive({
|
||||
loading: false,
|
||||
listData: [] as UserRecord[],
|
||||
query: {} as Record<string, unknown>,
|
||||
order: false,
|
||||
page: {
|
||||
total: 0,
|
||||
size: 12,
|
||||
current: 1,
|
||||
orders: [] as Order[],
|
||||
},
|
||||
const {
|
||||
state: reactiveData,
|
||||
load,
|
||||
search,
|
||||
reset,
|
||||
sort,
|
||||
sizeChange,
|
||||
currentChange,
|
||||
} = usePagedList<UserRecord, Record<string, unknown>>({
|
||||
request: (query) => listUser(query),
|
||||
});
|
||||
|
||||
const load = () => {
|
||||
reactiveData.loading = true;
|
||||
listUser({ page: reactiveData.page, ...reactiveData.query })
|
||||
.then((res) => {
|
||||
const data = res.data || {};
|
||||
reactiveData.listData = data.records || [];
|
||||
reactiveData.page.total = data.total || 0;
|
||||
})
|
||||
.catch(() => {
|
||||
// handled globally
|
||||
})
|
||||
.finally(() => {
|
||||
reactiveData.loading = false;
|
||||
});
|
||||
};
|
||||
|
||||
const search = (params: Record<string, unknown>) => {
|
||||
reactiveData.query = params || {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const reset = () => {
|
||||
reactiveData.query = {};
|
||||
reactiveData.page.current = 1;
|
||||
load();
|
||||
};
|
||||
|
||||
const refresh = () => load();
|
||||
|
||||
const sort = () => {
|
||||
reactiveData.order = !reactiveData.order;
|
||||
reactiveData.page.orders = [{ column: 'create_time', asc: reactiveData.order }];
|
||||
load();
|
||||
};
|
||||
|
||||
const openAdd = () => editRef.value?.show();
|
||||
const openEdit = (row: UserRecord) => editRef.value?.showEdit(row);
|
||||
const openAssignRoles = (row: UserRecord) => assignRef.value?.show(row);
|
||||
@@ -154,16 +119,6 @@ export default defineComponent({
|
||||
});
|
||||
};
|
||||
|
||||
const sizeChange = (size: number) => {
|
||||
reactiveData.page.size = size;
|
||||
load();
|
||||
};
|
||||
|
||||
const currentChange = (current: number) => {
|
||||
reactiveData.page.current = current;
|
||||
load();
|
||||
};
|
||||
|
||||
load();
|
||||
|
||||
return {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { usePagedList } from '@/composables/usePagedList';
|
||||
|
||||
@@ -146,4 +146,44 @@ describe('usePagedList', () => {
|
||||
).rejects.toThrow('boom');
|
||||
expect(state.loading).toBe(false);
|
||||
});
|
||||
|
||||
it('loads server-paginated rows when a request handler is provided', async () => {
|
||||
const request = vi.fn(() =>
|
||||
Promise.resolve({
|
||||
ok: true,
|
||||
code: 'ok',
|
||||
message: 'ok',
|
||||
data: { records: sampleRows.slice(0, 2), total: sampleRows.length },
|
||||
})
|
||||
);
|
||||
const { state, load, search, sort, sizeChange, currentChange } = usePagedList<Row, { keyword?: string }>({
|
||||
request,
|
||||
});
|
||||
|
||||
await load();
|
||||
expect(state.listData).toEqual(sampleRows.slice(0, 2));
|
||||
expect(state.page.total).toBe(sampleRows.length);
|
||||
expect(request).toHaveBeenLastCalledWith(expect.objectContaining({ page: state.page }));
|
||||
|
||||
search({ keyword: 'Row 02' });
|
||||
await Promise.resolve();
|
||||
expect(request).toHaveBeenLastCalledWith(expect.objectContaining({ keyword: 'Row 02' }));
|
||||
|
||||
sort();
|
||||
await Promise.resolve();
|
||||
expect(request).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ page: expect.objectContaining({ orders: [{ column: 'create_time', asc: true }] }) })
|
||||
);
|
||||
|
||||
sizeChange(24);
|
||||
await Promise.resolve();
|
||||
expect(state.page.current).toBe(1);
|
||||
expect(request).toHaveBeenLastCalledWith(expect.objectContaining({ page: expect.objectContaining({ size: 24 }) }));
|
||||
|
||||
currentChange(2);
|
||||
await Promise.resolve();
|
||||
expect(request).toHaveBeenLastCalledWith(
|
||||
expect.objectContaining({ page: expect.objectContaining({ current: 2 }) })
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user