| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821 |
- <template>
- <BasicModal
- :open="open"
- :title="title"
- :showOkBtn="false"
- :showCancelBtn="false"
- :width="width"
- :zIndex="zIndex"
- :canFullscreen="canFullscreen"
- :centered="true"
- :wrapClassName="modalWrapClassName"
- @update:open="(v) => emit('update:open', v)"
- @cancel="emit('update:open', false)"
- >
- <div ref="wrapRef" class="query-table-wrap">
- <div ref="searchRef" class="query-table-search">
- <div v-if="switchOptions?.length" class="query-table-switches">
- <button
- v-for="option in switchOptions"
- :key="String(option.value)"
- type="button"
- class="query-table-switch"
- :class="{ active: activeSwitch === option.value, disabled: option.disabled }"
- :disabled="option.disabled"
- @click="emit('switch-change', option.value)"
- >
- {{ option.label }}
- </button>
- </div>
- <div
- v-for="item in filterSchema"
- :key="item.field"
- :class="['search-item', { 'search-item--compare-input': item.type === 'compare-input' }]"
- >
- <span class="label">{{ item.label }}:</span>
- <div v-if="item.type === 'compare-input'" class="compare-input-group">
- <Select
- :value="getFilterValue(item.operatorField)"
- :options="item.options ? [...item.options] : []"
- popupClassName="query-table-modal-popup"
- :getPopupContainer="getModalPopupContainer"
- :style="{ width: `${item.operatorWidth || 72}px`, maxWidth: `${item.operatorWidth || 72}px` }"
- @update:value="(value) => setFilterValue(item.operatorField, value)"
- />
- <Input
- v-model:value="filters[item.field]"
- :placeholder="item.placeholder || ''"
- :style="{
- width: `${item.inputWidth || 180}px`,
- maxWidth: `${item.inputMaxWidth || item.inputWidth || 180}px`,
- }"
- />
- </div>
- <Input
- v-else-if="item.type === 'input'"
- v-model:value="filters[item.field]"
- :placeholder="item.placeholder || ''"
- :style="{ width: `${item.width || 260}px`, maxWidth: `${item.maxWidth || 300}px` }"
- />
- <DatePicker
- v-else-if="item.type === 'date'"
- v-model:value="filters[item.field]"
- :placeholder="item.placeholder || ''"
- value-format="YYYY-MM-DD"
- popupClassName="query-table-modal-popup"
- :getPopupContainer="getModalPopupContainer"
- :style="{ width: `${item.width || 260}px`, maxWidth: `${item.maxWidth || 300}px` }"
- />
- <RangePicker
- v-else-if="item.type === 'date-range'"
- :value="getDateRangeValue(item)"
- :placeholder="item.rangePlaceholder || ['开始日期', '结束日期']"
- :value-format="item.valueFormat || 'YYYY-MM-DD'"
- popupClassName="query-table-modal-popup"
- :getPopupContainer="getModalPopupContainer"
- :style="{ width: `${item.width || 300}px`, maxWidth: `${item.maxWidth || 360}px` }"
- @update:value="(value) => handleDateRangeChange(item, value)"
- />
- <Select
- v-else-if="item.type === 'select'"
- v-model:value="filters[item.field]"
- :options="item.options ? [...item.options] : []"
- popupClassName="query-table-modal-popup"
- :getPopupContainer="getModalPopupContainer"
- :style="{ width: `${item.width || 260}px`, maxWidth: `${item.maxWidth || 300}px` }"
- />
- </div>
- <div class="search-actions">
- <Button type="primary" :loading="loading" @click="emit('search')">查询</Button>
- <Button @click="emit('reset')">重置</Button>
- </div>
- <div v-if="$slots.searchActions || isTableSettingReady" class="search-utility-actions">
- <div v-if="$slots.searchActions" class="search-extra-actions">
- <slot name="searchActions"></slot>
- </div>
- <div v-if="isTableSettingReady" class="search-table-setting">
- <TableSetting :setting="tableSetting" :getPopupContainer="getTableSettingPopupContainer" />
- </div>
- </div>
- </div>
- <div ref="contentRef" class="query-table-content">
- <BasicTable
- v-if="dataSource?.length"
- :columns="columns"
- :action-column="actionColumn"
- :data-source="dataSource"
- :row-selection="tableRowSelection"
- :pagination="false"
- :show-index-column="showIndexColumn"
- :index-column-props="indexColumnProps"
- :show-table-setting="false"
- :table-setting="tableSetting"
- :can-resize="true"
- :max-height="tableMaxHeight"
- row-key="rowKey"
- @register="handleTableRegister"
- @change="handleTableChange"
- />
- <div v-else class="query-table-empty">
- <EmptyState variant="compact" :min-height="Math.max(260, tableMaxHeight)" />
- </div>
- </div>
- </div>
- <template #footer>
- <div ref="paginationRef" class="pagination-bar">
- <AnalysisPagination
- :total="pagination.total"
- :current="pagination.page"
- :page-size="pagination.limit"
- :page-size-options="pageSizeOptions"
- :loading="loading"
- @change="handlePaginationChange"
- />
- </div>
- </template>
- </BasicModal>
- </template>
- <script lang="ts" setup>
- import { computed, nextTick, onBeforeUnmount, ref, watch } from 'vue';
- import type { Dayjs } from 'dayjs';
- import { Button, DatePicker, Input, Select } from 'ant-design-vue';
- import AnalysisPagination from '@jeesite/core/components/AnalysisPagination/src/AnalysisPagination.vue';
- import { EmptyState } from '@jeesite/core/components/EmptyState';
- import { BasicTable } from '@jeesite/core/components/Table';
- import { BasicModal } from '@jeesite/core/components/Modal';
- import TableSetting from '@jeesite/core/components/Table/src/components/settings/index.vue';
- import { createTableContext } from '@jeesite/core/components/Table/src/hooks/useTableContext';
- import { getModalPopupContainer as resolveModalPopupContainer } from '@jeesite/core/utils';
- const RangePicker = DatePicker.RangePicker;
- interface FilterItem {
- field: string;
- label: string;
- type: 'input' | 'date' | 'date-range' | 'select' | 'compare-input';
- placeholder?: string;
- width?: number;
- maxWidth?: number;
- options?: ReadonlyArray<{ label: string; value: string | number }>;
- operatorField?: string;
- operatorWidth?: number;
- inputWidth?: number;
- inputMaxWidth?: number;
- startField?: string;
- endField?: string;
- rangePlaceholder?: [string, string];
- valueFormat?: string;
- }
- interface SwitchOption {
- label: string;
- value: string | number;
- disabled?: boolean;
- }
- const props = defineProps<{
- open: boolean;
- title: string;
- width?: number;
- zIndex?: number;
- loading?: boolean;
- filters: Record<string, any>;
- filterSchema: ReadonlyArray<FilterItem>;
- columns: any[];
- actionColumn?: any;
- dataSource: any[];
- rowSelection?: any;
- pagination: { page: number; limit: number; total: number };
- totalPages: number;
- pageSizeOptions: Array<{ label: string | number; value: string | number }>;
- showIndexColumn?: boolean;
- showTableSetting?: boolean;
- tableSetting?: {
- size?: boolean;
- setting?: boolean;
- redo?: boolean;
- fullScreen?: boolean;
- };
- canFullscreen?: boolean;
- wrapClassName?: string;
- switchOptions?: ReadonlyArray<SwitchOption>;
- activeSwitch?: string | number;
- }>();
- const emit = defineEmits<{
- (e: 'update:open', v: boolean): void;
- (e: 'search'): void;
- (e: 'reset'): void;
- (e: 'prev-page'): void;
- (e: 'next-page'): void;
- (e: 'page-change', page: number, pageSize: number): void;
- (e: 'page-size-change', v: number): void;
- (e: 'switch-change', v: string | number): void;
- (e: 'sort-change', payload: { orderKey: string; sort: 'ASC' | 'DESC' }): void;
- }>();
- const handlePaginationChange = (page: number, pageSize: number) => {
- if (props.loading || (page === props.pagination.page && pageSize === props.pagination.limit)) return;
- emit('page-change', page, pageSize);
- };
- const handleTableChange = (_pagination: any, _filters: any, sorter: any) => {
- const currentSorter = Array.isArray(sorter) ? sorter[0] : sorter;
- if (!currentSorter?.order) return;
- const sort = currentSorter.order === 'ascend' ? 'ASC' : currentSorter.order === 'descend' ? 'DESC' : '';
- const orderKey = currentSorter?.column?.key || currentSorter?.field;
- if (!sort || !orderKey) return;
- emit('sort-change', { orderKey: String(orderKey), sort });
- };
- const width = props.width ?? 1100;
- const zIndex = props.zIndex;
- const canFullscreen = props.canFullscreen ?? true;
- const showTableSetting = props.showTableSetting ?? false;
- const showIndexColumn = ref(props.showIndexColumn ?? false);
- const modalWrapClassName = computed(() => ['query-table-modal', props.wrapClassName].filter(Boolean).join(' '));
- const wrapRef = ref<HTMLElement | null>(null);
- const searchRef = ref<HTMLElement | null>(null);
- const contentRef = ref<HTMLElement | null>(null);
- const paginationRef = ref<HTMLElement | null>(null);
- const tableAction = ref<any>(null);
- const tableMaxHeight = ref(420);
- const tableRowSelection = ref(props.rowSelection);
- const getContentContainer = () => {
- const scrollContainer = wrapRef.value?.closest('.scroll-container') as HTMLElement | null;
- const modalBody = wrapRef.value?.closest('.ant-modal-body') as HTMLElement | null;
- return scrollContainer || modalBody || wrapRef.value?.parentElement || null;
- };
- const getModalPopupContainer = (triggerNode?: HTMLElement) =>
- resolveModalPopupContainer(triggerNode || wrapRef.value || getContentContainer() || undefined);
- const getTableSettingPopupContainer = () =>
- (wrapRef.value?.closest('.ant-modal-wrap') as HTMLElement | null) ||
- (wrapRef.value?.closest('.ant-modal-root') as HTMLElement | null) ||
- document.body;
- let resizeObserver: ResizeObserver | null = null;
- const tableSetting = props.tableSetting ?? {
- size: true,
- setting: true,
- redo: false,
- fullScreen: false,
- };
- const handleTableRegister = (action: any) => {
- tableAction.value = action;
- scheduleTableHeightUpdate();
- };
- const isTableSettingReady = computed(() => showTableSetting && !!tableAction.value && !!props.dataSource?.length);
- const getTableColumns = (...args: any[]) => tableAction.value?.getColumns?.(...args) || [];
- const indexColumnProps = computed(() => ({
- width: 70,
- customRender: ({ index }: { index: number }) => (props.pagination.page - 1) * props.pagination.limit + index + 1,
- }));
- const setTableProps = (nextProps: any) => {
- const resolvedProps = { ...(nextProps || {}) };
- if (Object.prototype.hasOwnProperty.call(resolvedProps, 'showIndexColumn')) {
- showIndexColumn.value = resolvedProps.showIndexColumn ?? false;
- Reflect.deleteProperty(resolvedProps, 'showIndexColumn');
- }
- if (Object.prototype.hasOwnProperty.call(resolvedProps, 'rowSelection')) {
- tableRowSelection.value = resolvedProps.rowSelection ?? undefined;
- }
- if (Object.keys(resolvedProps).length) {
- tableAction.value?.setProps?.(resolvedProps);
- }
- };
- createTableContext({
- wrapRef,
- getBindValues: computed(() => ({
- columns: getTableColumns({ ignoreIndex: true, ignoreAction: true }),
- showIndexColumn: showIndexColumn.value,
- rowSelection: tableRowSelection.value,
- isTreeTable: false,
- })),
- getDefaultRowSelection: () => tableAction.value?.getDefaultRowSelection?.(),
- getColumns: getTableColumns,
- getCacheColumns: () => tableAction.value?.getCacheColumns?.() || [],
- setColumns: (nextColumns: any) => tableAction.value?.setColumns?.(nextColumns),
- setProps: setTableProps,
- } as any);
- const getTableExtraHeight = () => {
- const tableRoot = wrapRef.value?.querySelector('.query-table-content .jeesite-basic-table') as HTMLElement | null;
- if (!tableRoot) return 96;
- const toolbarHeight = (tableRoot.querySelector('.jeesite-basic-table-header-container') as HTMLElement | null)
- ?.offsetHeight;
- const tableHeadHeight = (tableRoot.querySelector('.ant-table-thead') as HTMLElement | null)?.offsetHeight;
- const wrapperPaddingHeight = 8;
- return (toolbarHeight ?? 0) + (tableHeadHeight ?? 40) + wrapperPaddingHeight;
- };
- const getWrapGap = () => {
- if (!wrapRef.value) return 12;
- const wrapStyle = window.getComputedStyle(wrapRef.value);
- const gapValue = wrapStyle.rowGap || wrapStyle.gap || '12px';
- const gap = Number.parseFloat(gapValue);
- return Number.isFinite(gap) ? gap : 12;
- };
- const getVerticalMargin = (element: HTMLElement | null) => {
- if (!element) return 0;
- const style = window.getComputedStyle(element);
- const marginTop = Number.parseFloat(style.marginTop || '0');
- const marginBottom = Number.parseFloat(style.marginBottom || '0');
- return (Number.isFinite(marginTop) ? marginTop : 0) + (Number.isFinite(marginBottom) ? marginBottom : 0);
- };
- const getContentBorderHeight = () => {
- if (!contentRef.value) return 2;
- return Math.max(0, contentRef.value.offsetHeight - contentRef.value.clientHeight);
- };
- const getDateRangeValue = (item: FilterItem): [string, string] | undefined => {
- if (item.type !== 'date-range' || !item.startField || !item.endField) return undefined;
- const startValue = props.filters[item.startField];
- const endValue = props.filters[item.endField];
- if (!startValue || !endValue) return undefined;
- return [String(startValue), String(endValue)];
- };
- const handleDateRangeChange = (item: FilterItem, value?: [string, string] | [Dayjs, Dayjs] | null) => {
- if (item.type !== 'date-range' || !item.startField || !item.endField) return;
- const startValue = Array.isArray(value) ? value[0] : undefined;
- const endValue = Array.isArray(value) ? value[1] : undefined;
- props.filters[item.startField] = typeof startValue === 'string' ? startValue : '';
- props.filters[item.endField] = typeof endValue === 'string' ? endValue : '';
- };
- const getFilterValue = (field?: string) => {
- if (!field) return undefined;
- return props.filters[field];
- };
- const setFilterValue = (field: string | undefined, value: unknown) => {
- if (!field) return;
- props.filters[field] = value;
- };
- const updateTableMaxHeight = () => {
- const contentHeight = getContentContainer()?.clientHeight ?? 0;
- if (!contentHeight) return;
- const searchHeight = searchRef.value?.offsetHeight ?? 0;
- const reservedGap = getWrapGap();
- const searchMarginHeight = getVerticalMargin(searchRef.value);
- const contentMarginHeight = getVerticalMargin(contentRef.value);
- const borderOffset = getContentBorderHeight();
- const availableHeight =
- contentHeight -
- searchHeight -
- searchMarginHeight -
- reservedGap -
- contentMarginHeight -
- borderOffset -
- getTableExtraHeight();
- tableMaxHeight.value = Math.max(260, availableHeight);
- };
- const scheduleTableHeightUpdate = () => {
- nextTick(() => {
- requestAnimationFrame(() => {
- updateTableMaxHeight();
- tableAction.value?.redoHeight?.();
- });
- });
- };
- const bindResizeObserver = () => {
- if (typeof ResizeObserver === 'undefined') return;
- resizeObserver?.disconnect();
- resizeObserver = new ResizeObserver(() => {
- scheduleTableHeightUpdate();
- });
- [getContentContainer(), wrapRef.value, searchRef.value, contentRef.value, paginationRef.value].forEach(
- (element) => {
- if (element) resizeObserver?.observe(element);
- },
- );
- };
- watch(
- () => props.showIndexColumn,
- (value) => {
- showIndexColumn.value = value ?? false;
- },
- );
- watch(
- () => props.rowSelection,
- (value) => {
- tableRowSelection.value = value;
- },
- );
- watch(
- () => props.open,
- (open) => {
- if (!open) {
- resizeObserver?.disconnect();
- return;
- }
- scheduleTableHeightUpdate();
- nextTick(() => {
- bindResizeObserver();
- scheduleTableHeightUpdate();
- });
- },
- );
- watch(
- () => props.dataSource?.length,
- (length) => {
- if (!length) {
- tableAction.value = null;
- }
- if (!props.open) return;
- scheduleTableHeightUpdate();
- },
- { flush: 'post' },
- );
- onBeforeUnmount(() => {
- resizeObserver?.disconnect();
- });
- </script>
- <style lang="less">
- .query-table-modal {
- .ant-modal {
- max-width: calc(100vw - 48px);
- }
- .ant-modal-body > .scroll-container,
- .ant-modal-body > .scroll-container > .scrollbar__wrap,
- .ant-modal-body > .scroll-container > .scrollbar__wrap > .scrollbar__view,
- .ant-modal-body > .scroll-container > .scrollbar__wrap > .scrollbar__view > div {
- height: 100%;
- min-height: 0 !important;
- max-height: none !important;
- }
- .ant-modal-body > .scroll-container > .scrollbar__wrap {
- overflow: hidden;
- }
- .ant-modal-body > .scroll-container > .scrollbar__wrap > .scrollbar__view {
- margin: 0 !important;
- }
- .ant-modal-body > .scroll-container > .scrollbar__bar.is-vertical,
- .ant-modal-body > .scroll-container > .is-vertical {
- display: none !important;
- opacity: 0 !important;
- pointer-events: none;
- }
- .ant-modal-close {
- display: flex;
- align-items: center;
- justify-content: flex-end;
- width: auto !important;
- height: auto !important;
- padding: 0;
- .jeesite-basic-modal-close {
- display: flex;
- align-items: center;
- gap: 2px;
- margin-right: 0;
- }
- }
- .ant-modal-content {
- border-radius: 6px;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- height: min(720px, calc(100vh - 80px));
- max-height: min(720px, calc(100vh - 80px));
- }
- .query-table-modal-popup,
- .ant-select-dropdown,
- .ant-picker-dropdown,
- .ant-dropdown,
- .ant-tooltip {
- z-index: 1060;
- }
- .ant-modal-header {
- padding: 12px 16px;
- border-bottom: 1px solid #e5e7eb;
- flex: 0 0 auto;
- background: @modal-header-bg-color;
- }
- .ant-modal-footer {
- padding: 8px 16px 10px;
- margin-top: 0;
- border-top: 1px solid #e5e7eb;
- background: #fff;
- }
- .ant-modal-body {
- flex: 1 1 auto;
- min-height: 0;
- overflow: hidden;
- display: flex;
- flex-direction: column;
- padding: 16px;
- }
- }
- .fullscreen-modal.query-table-modal {
- .ant-modal-content {
- max-height: calc(100vh - 16px);
- height: calc(100vh - 16px);
- }
- }
- .query-table-wrap {
- display: flex;
- flex-direction: column;
- flex: 1 1 auto;
- gap: 12px;
- min-height: 0;
- min-width: 0;
- height: 100%;
- }
- .query-table-search {
- margin: 0;
- padding: 14px 16px;
- border: 1px solid #e2e8f0;
- border-radius: 6px;
- background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
- box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.8);
- flex: 0 0 auto;
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- gap: 8px 16px;
- .query-table-switches {
- width: 100%;
- display: flex;
- align-items: center;
- gap: 12px;
- margin-bottom: 2px;
- }
- .query-table-switch {
- height: 36px;
- padding: 0 18px;
- border: 1px solid #dbe3ee;
- border-radius: 999px;
- background: #fff;
- color: #64748b;
- font-size: 13px;
- font-weight: 600;
- cursor: pointer;
- transition: all 0.2s ease;
- &:hover:not(.disabled) {
- color: #2563eb;
- border-color: #93c5fd;
- }
- &.active {
- color: #fff;
- border-color: #315efb;
- background: #315efb;
- box-shadow: 0 6px 14px rgba(49, 94, 251, 0.18);
- }
- &.disabled {
- color: #94a3b8;
- background: #f8fafc;
- cursor: not-allowed;
- box-shadow: none;
- }
- }
- .search-item {
- display: flex;
- align-items: center;
- gap: 8px;
- .label {
- color: #475569;
- font-size: 12px;
- font-weight: 600;
- white-space: nowrap;
- }
- }
- .search-item--compare-input {
- .compare-input-group {
- display: flex;
- align-items: center;
- min-width: 0;
- .ant-select {
- flex: 0 0 auto;
- }
- .ant-select-selector {
- border-top-right-radius: 0 !important;
- border-bottom-right-radius: 0 !important;
- }
- .ant-input,
- .ant-input-affix-wrapper {
- border-top-left-radius: 0 !important;
- border-bottom-left-radius: 0 !important;
- margin-left: -1px;
- }
- }
- }
- .ant-input,
- .ant-input-affix-wrapper,
- .ant-picker,
- .ant-select-selector {
- border-radius: 6px !important;
- }
- .search-actions {
- display: flex;
- align-items: center;
- gap: 12px;
- .ant-btn {
- height: 36px;
- min-width: 76px;
- padding: 0 18px;
- border-radius: 18px;
- font-size: 15px;
- font-weight: 500;
- box-shadow: none;
- }
- .ant-btn-primary {
- border-color: #315efb;
- background: #315efb;
- }
- .ant-btn:not(.ant-btn-primary) {
- color: #1f2937;
- border-color: #d6dbe6;
- background: #ffffff;
- }
- }
- .search-utility-actions {
- margin-left: auto;
- display: flex;
- align-items: center;
- gap: 10px;
- flex-wrap: wrap;
- justify-content: flex-end;
- }
- .search-extra-actions {
- display: flex;
- align-items: center;
- gap: 8px;
- flex-wrap: wrap;
- }
- .search-table-setting {
- display: inline-flex;
- align-items: center;
- padding: 0 2px;
- }
- }
- .query-table-content {
- margin: 0;
- flex: 1 1 auto;
- min-height: 0;
- display: flex;
- overflow: hidden;
- box-sizing: border-box;
- border: 0;
- border-radius: 0;
- background: transparent;
- box-shadow: none;
- > .jeesite-basic-table,
- > .query-table-empty {
- flex: 1 1 auto;
- min-height: 0;
- width: 100%;
- }
- > .jeesite-basic-table,
- > .jeesite-basic-table > .ant-spin-nested-loading,
- > .jeesite-basic-table > .ant-spin-nested-loading > .ant-spin-container,
- > .jeesite-basic-table .ant-table-wrapper {
- height: 100%;
- min-height: 0;
- }
- .ant-table-thead > tr > th {
- background: #f8fafc;
- color: #334155;
- font-weight: 600;
- }
- .ant-table-tbody > tr:nth-child(even) > td {
- background: #fcfdff;
- }
- .ant-table-tbody > tr:hover > td {
- background: #eff6ff;
- }
- }
- .query-table-empty {
- display: flex;
- align-items: center;
- justify-content: center;
- background: linear-gradient(180deg, #f8fafc 0%, #ffffff 100%);
- }
- .pagination-bar {
- min-height: 32px;
- display: flex;
- align-items: center;
- justify-content: flex-end;
- gap: 12px;
- color: #6b7280;
- font-size: 12px;
- flex-wrap: nowrap;
- .pagination-total {
- margin-right: 0;
- color: #64748b;
- white-space: nowrap;
- }
- .pagination-size {
- display: flex;
- align-items: center;
- gap: 6px;
- white-space: nowrap;
- .ant-select {
- width: 86px;
- }
- .ant-select-selector {
- border-radius: 6px;
- }
- }
- .pagination-actions {
- display: flex;
- align-items: center;
- gap: 6px;
- white-space: nowrap;
- .page-total {
- color: #6b7280;
- min-width: 28px;
- text-align: center;
- }
- .ant-btn {
- border-radius: 6px;
- }
- .ant-btn-primary {
- min-width: 36px;
- }
- }
- }
- </style>
|