Files
cn-rdms-web/src/components/custom/table-search-panel.vue
2026-03-26 20:18:20 +08:00

164 lines
4.5 KiB
Vue

<script setup lang="ts">
import { computed, ref, useSlots } from 'vue';
import type { ComponentPublicInstance } from 'vue';
import type { FormInstance } from 'element-plus';
import { $t } from '@/locales';
defineOptions({ name: 'TableSearchPanel' });
interface Props {
model: object;
rules?: Record<string, App.Global.FormRule | App.Global.FormRule[]>;
formRef?: ((instance: FormInstance | null) => void) | null;
labelWidth?: string | number;
labelPosition?: 'left' | 'right' | 'top';
gutter?: number;
actionColLg?: number;
actionColMd?: number;
actionColSm?: number;
disabled?: boolean;
defaultExpanded?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
rules: undefined,
formRef: null,
labelWidth: 80,
labelPosition: 'right',
gutter: 24,
actionColLg: 6,
actionColMd: 24,
actionColSm: 24,
disabled: false,
defaultExpanded: false
});
interface Emits {
(e: 'reset'): void;
(e: 'search'): void;
}
const emit = defineEmits<Emits>();
const slots = useSlots();
const expanded = ref(props.defaultExpanded);
const hasExtra = computed(() => Boolean(slots.extra));
function handleReset() {
emit('reset');
}
function handleSearch() {
emit('search');
}
function toggleExpanded() {
if (!hasExtra.value) {
return;
}
expanded.value = !expanded.value;
}
function handleFormRef(instance: Element | ComponentPublicInstance | null) {
props.formRef?.(instance as FormInstance | null);
}
</script>
<template>
<ElCard class="card-wrapper">
<ElForm
:ref="handleFormRef"
:model="props.model"
:rules="props.rules"
:label-position="props.labelPosition"
:label-width="props.labelWidth"
@submit.prevent
@keyup.enter="handleSearch"
>
<ElRow :gutter="props.gutter">
<slot />
<ElCol
v-if="!hasExtra"
class="table-search-panel__action-col"
:lg="props.actionColLg"
:md="props.actionColMd"
:sm="props.actionColSm"
>
<ElSpace class="w-full justify-end" alignment="end">
<ElButton :disabled="props.disabled" @click="handleReset">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
</template>
{{ $t('common.reset') }}
</ElButton>
<ElButton type="primary" plain :disabled="props.disabled" @click="handleSearch">
<template #icon>
<icon-ic-round-search class="text-icon" />
</template>
{{ $t('common.search') }}
</ElButton>
</ElSpace>
</ElCol>
</ElRow>
<ElRow v-if="hasExtra && expanded" :gutter="props.gutter">
<slot name="extra" />
<ElCol
class="table-search-panel__action-col"
:lg="props.actionColLg"
:md="props.actionColMd"
:sm="props.actionColSm"
>
<ElSpace class="w-full justify-end" alignment="end">
<ElButton circle :disabled="props.disabled" @click="toggleExpanded">
<icon-mdi-chevron-double-up class="text-16px" />
</ElButton>
<ElButton :disabled="props.disabled" @click="handleReset">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
</template>
{{ $t('common.reset') }}
</ElButton>
<ElButton type="primary" plain :disabled="props.disabled" @click="handleSearch">
<template #icon>
<icon-ic-round-search class="text-icon" />
</template>
{{ $t('common.search') }}
</ElButton>
</ElSpace>
</ElCol>
</ElRow>
<div v-if="hasExtra && !expanded" class="flex justify-end">
<ElSpace>
<ElButton circle :disabled="props.disabled" @click="toggleExpanded">
<icon-mdi-chevron-double-down class="text-16px" />
</ElButton>
<ElButton :disabled="props.disabled" @click="handleReset">
<template #icon>
<icon-ic-round-refresh class="text-icon" />
</template>
{{ $t('common.reset') }}
</ElButton>
<ElButton type="primary" plain :disabled="props.disabled" @click="handleSearch">
<template #icon>
<icon-ic-round-search class="text-icon" />
</template>
{{ $t('common.search') }}
</ElButton>
</ElSpace>
</div>
</ElForm>
</ElCard>
</template>
<style scoped>
.table-search-panel__action-col {
margin-left: auto;
}
</style>