mirror of
https://github.com/soybeanjs/soybean-admin.git
synced 2025-09-30 15:16:42 +08:00
feat(components): add tablesearch component, reduce page build
This commit is contained in:
parent
a17d68eb11
commit
038dda8967
71
src/components/advanced/table-search.vue
Normal file
71
src/components/advanced/table-search.vue
Normal file
@ -0,0 +1,71 @@
|
||||
<script setup lang="tsx">
|
||||
import { $t } from '@/locales';
|
||||
import type { SelectMixedOption } from '@/utils/selectTypes';
|
||||
import { transformRecordToOption, translateOptions } from '@/utils/common';
|
||||
|
||||
interface Emits {
|
||||
(e: 'reset'): void;
|
||||
(e: 'search'): void;
|
||||
}
|
||||
|
||||
const emit = defineEmits<Emits>();
|
||||
|
||||
const model = defineModel<any>('model', { required: true });
|
||||
|
||||
const rule = defineModel<any>('rule', { required: true });
|
||||
|
||||
function reset() {
|
||||
emit('reset');
|
||||
}
|
||||
|
||||
function search() {
|
||||
emit('search');
|
||||
}
|
||||
|
||||
function recordToOptions(optionsRecord: Record<string, App.I18n.I18nKey>): SelectMixedOption[] {
|
||||
if (!optionsRecord || Object.keys(optionsRecord).length === 0) {
|
||||
return [];
|
||||
}
|
||||
const anyOptions = transformRecordToOption(optionsRecord);
|
||||
return translateOptions(anyOptions);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NCard :bordered="false" size="small" class="card-wrapper">
|
||||
<NForm :model="model" label-placement="left">
|
||||
<NGrid responsive="screen" item-responsive>
|
||||
<NFormItemGi v-for="item in rule" :key="item.model" span="24 s:5 m:5" :label="$t(item.label)" class="pr-24px">
|
||||
<NInput v-if="!item.options" v-model:value="model[item.model]" :placeholder="$t(item.placeholder)" />
|
||||
<NSelect
|
||||
v-else
|
||||
v-model:value="model[item.model]"
|
||||
:placeholder="$t(item.placeholder) === '' ? '' : $t(item.placeholder)"
|
||||
filterable
|
||||
autocomplete="off"
|
||||
clearable
|
||||
:options="recordToOptions(item.options)"
|
||||
></NSelect>
|
||||
</NFormItemGi>
|
||||
<NFormItemGi span="24 s:6 m:6">
|
||||
<NSpace class="w-full" justify="end">
|
||||
<NButton type="primary" ghost @click="search">
|
||||
<template #icon>
|
||||
<icon-ic-round-search class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.search') }}
|
||||
</NButton>
|
||||
<NButton @click="reset">
|
||||
<template #icon>
|
||||
<icon-ic-round-refresh class="text-icon" />
|
||||
</template>
|
||||
{{ $t('common.reset') }}
|
||||
</NButton>
|
||||
</NSpace>
|
||||
</NFormItemGi>
|
||||
</NGrid>
|
||||
</NForm>
|
||||
</NCard>
|
||||
</template>
|
||||
|
||||
<style scoped></style>
|
@ -10,6 +10,13 @@ type BaseData = Record<string, unknown>;
|
||||
|
||||
type ApiFn = (args: any) => Promise<unknown>;
|
||||
|
||||
type ParamType = {
|
||||
label: string;
|
||||
model: string;
|
||||
placeholder: string;
|
||||
options?: Record<string, App.I18n.I18nKey>;
|
||||
};
|
||||
|
||||
export type TableColumn<T extends BaseData = BaseData, CustomColumnKey = never> =
|
||||
| (Omit<TableColumnGroup<T>, 'key'> & { key: keyof T | CustomColumnKey })
|
||||
| (Omit<DataTableBaseColumn<T>, 'key'> & { key: keyof T | CustomColumnKey })
|
||||
@ -34,6 +41,8 @@ export type TableConfig<TableData extends BaseData = BaseData, Fn extends ApiFn
|
||||
apiFn: Fn;
|
||||
/** api params */
|
||||
apiParams?: Parameters<Fn>[0];
|
||||
/** params rule */
|
||||
paramsRule?: ParamType[];
|
||||
/** transform api response to table data */
|
||||
transformer: Transformer<TableData, Awaited<ReturnType<Fn>>>;
|
||||
/** pagination */
|
||||
@ -70,10 +79,12 @@ export function useTable<TableData extends BaseData, Fn extends ApiFn, CustomCol
|
||||
const { loading, startLoading, endLoading } = useLoading();
|
||||
const { bool: empty, setBool: setEmpty } = useBoolean();
|
||||
|
||||
const { apiFn, apiParams, transformer, onPaginationChanged, immediate = true } = config;
|
||||
const { apiFn, apiParams, paramsRule, transformer, onPaginationChanged, immediate = true } = config;
|
||||
|
||||
const searchParams: NonNullable<Parameters<Fn>[0]> = reactive({ ...apiParams });
|
||||
|
||||
const rule = ref({ ...paramsRule });
|
||||
|
||||
const { columns, filteredColumns, reloadColumns } = useTableColumn(config.columns);
|
||||
|
||||
const data: Ref<TableData[]> = ref([]);
|
||||
@ -172,6 +183,7 @@ export function useTable<TableData extends BaseData, Fn extends ApiFn, CustomCol
|
||||
updatePagination,
|
||||
getData,
|
||||
searchParams,
|
||||
rule,
|
||||
updateSearchParams,
|
||||
resetSearchParams
|
||||
};
|
||||
|
1
src/typings/components.d.ts
vendored
1
src/typings/components.d.ts
vendored
@ -79,6 +79,7 @@ declare module 'vue' {
|
||||
SystemLogo: typeof import('./../components/common/system-logo.vue')['default']
|
||||
TableColumnSetting: typeof import('./../components/advanced/table-column-setting.vue')['default']
|
||||
TableHeaderOperation: typeof import('./../components/advanced/table-header-operation.vue')['default']
|
||||
TableSearch: typeof import('./../components/advanced/table-search.vue')['default']
|
||||
ThemeSchemaSwitch: typeof import('./../components/common/theme-schema-switch.vue')['default']
|
||||
WaveBg: typeof import('./../components/custom/wave-bg.vue')['default']
|
||||
}
|
||||
|
18
src/utils/selectTypes.ts
Normal file
18
src/utils/selectTypes.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { type CSSProperties, type VNode, type VNodeChild } from 'vue';
|
||||
import type { SelectGroupOption } from 'naive-ui';
|
||||
|
||||
type UpdatedSelectBaseOption<
|
||||
V = boolean | string | number,
|
||||
L = boolean | string | ((option: UpdatedSelectBaseOption<V>, selected: boolean) => VNodeChild)
|
||||
> = {
|
||||
value?: V;
|
||||
label?: L;
|
||||
class?: string;
|
||||
style?: string | CSSProperties;
|
||||
disabled?: boolean;
|
||||
render?: (info: { node: VNode; option: UpdatedSelectBaseOption<V>; selected: boolean }) => VNodeChild;
|
||||
[k: string]: unknown;
|
||||
};
|
||||
|
||||
type UpdatedSelectMixedOption = UpdatedSelectBaseOption | SelectGroupOption | any;
|
||||
export type SelectMixedOption = UpdatedSelectMixedOption;
|
@ -7,8 +7,8 @@ import { useAppStore } from '@/store/modules/app';
|
||||
import { useTable } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import { enableStatusRecord } from '@/constants/business';
|
||||
import TableSearch from '@/components/advanced/table-search.vue';
|
||||
import RoleOperateDrawer, { type OperateType } from './modules/role-operate-drawer.vue';
|
||||
import RoleSearch from './modules/role-search.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { bool: drawerVisible, setTrue: openDrawer } = useBoolean();
|
||||
@ -21,6 +21,7 @@ const {
|
||||
pagination,
|
||||
getData,
|
||||
searchParams,
|
||||
rule,
|
||||
updateSearchParams,
|
||||
resetSearchParams
|
||||
} = useTable<Api.SystemManage.Role, typeof fetchGetRoleList, 'index' | 'operate'>({
|
||||
@ -34,6 +35,19 @@ const {
|
||||
roleName: null,
|
||||
roleCode: null
|
||||
},
|
||||
paramsRule: [
|
||||
{ label: 'page.manage.role.roleName', model: 'roleName', placeholder: 'page.manage.role.form.roleName' },
|
||||
{ label: 'page.manage.role.roleCode', model: 'roleCode', placeholder: 'page.manage.role.form.roleCode' },
|
||||
{
|
||||
label: 'page.manage.role.roleStatus',
|
||||
model: 'status',
|
||||
placeholder: 'page.manage.role.form.roleStatus',
|
||||
options: {
|
||||
'1': 'page.manage.common.status.enable',
|
||||
'2': 'page.manage.common.status.disable'
|
||||
}
|
||||
}
|
||||
],
|
||||
transformer: res => {
|
||||
const { records = [], current = 1, size = 10, total = 0 } = res.data || {};
|
||||
|
||||
@ -175,7 +189,7 @@ function getIndex(index: number) {
|
||||
|
||||
<template>
|
||||
<div class="flex-vertical-stretch gap-16px overflow-hidden <sm:overflow-auto">
|
||||
<RoleSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
|
||||
<TableSearch v-model:model="searchParams" :rule="rule" @reset="resetSearchParams" @search="getData" />
|
||||
<NCard :title="$t('page.manage.role.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
|
@ -7,8 +7,8 @@ import { useAppStore } from '@/store/modules/app';
|
||||
import { useTable } from '@/hooks/common/table';
|
||||
import { $t } from '@/locales';
|
||||
import { enableStatusRecord, userGenderRecord } from '@/constants/business';
|
||||
import TableSearch from '@/components/advanced/table-search.vue';
|
||||
import UserOperateDrawer, { type OperateType } from './modules/user-operate-drawer.vue';
|
||||
import UserSearch from './modules/user-search.vue';
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { bool: drawerVisible, setTrue: openDrawer } = useBoolean();
|
||||
@ -21,6 +21,7 @@ const {
|
||||
pagination,
|
||||
getData,
|
||||
searchParams,
|
||||
rule,
|
||||
updateSearchParams,
|
||||
resetSearchParams
|
||||
} = useTable<Api.SystemManage.User, typeof fetchGetUserList, 'index' | 'operate'>({
|
||||
@ -37,6 +38,42 @@ const {
|
||||
userPhone: null,
|
||||
userEmail: null
|
||||
},
|
||||
paramsRule: [
|
||||
{ label: 'page.manage.user.userName', model: 'userName', placeholder: 'page.manage.user.form.userName' },
|
||||
{
|
||||
label: 'page.manage.user.userGender',
|
||||
model: 'userGender',
|
||||
placeholder: 'page.manage.user.form.userGender',
|
||||
options: {
|
||||
'1': 'page.manage.user.gender.male',
|
||||
'2': 'page.manage.user.gender.female'
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'page.manage.user.nickName',
|
||||
model: 'nickName',
|
||||
placeholder: 'page.manage.user.form.nickName'
|
||||
},
|
||||
{
|
||||
label: 'page.manage.user.userPhone',
|
||||
model: 'userPhone',
|
||||
placeholder: 'page.manage.user.form.userPhone'
|
||||
},
|
||||
{
|
||||
label: 'page.manage.user.userEmail',
|
||||
model: 'userEmail',
|
||||
placeholder: 'page.manage.user.form.userEmail'
|
||||
},
|
||||
{
|
||||
label: 'page.manage.user.userStatus',
|
||||
model: 'status',
|
||||
placeholder: 'page.manage.user.form.userStatus',
|
||||
options: {
|
||||
'1': 'page.manage.common.status.enable',
|
||||
'2': 'page.manage.common.status.disable'
|
||||
}
|
||||
}
|
||||
],
|
||||
transformer: res => {
|
||||
const { records = [], current = 1, size = 10, total = 0 } = res.data || {};
|
||||
|
||||
@ -205,7 +242,7 @@ function getIndex(index: number) {
|
||||
|
||||
<template>
|
||||
<div class="flex-vertical-stretch gap-16px overflow-hidden <sm:overflow-auto">
|
||||
<UserSearch v-model:model="searchParams" @reset="resetSearchParams" @search="getData" />
|
||||
<TableSearch v-model:model="searchParams" :rule="rule" @reset="resetSearchParams" @search="getData" />
|
||||
<NCard :title="$t('page.manage.user.title')" :bordered="false" size="small" class="card-wrapper sm:flex-1-hidden">
|
||||
<template #header-extra>
|
||||
<TableHeaderOperation
|
||||
|
Loading…
Reference in New Issue
Block a user