refactor: 优化viewed实例初始化,rowStyle rowClassName 从最新的配置中读取

master^2
layhuts 2026-05-09 18:27:09 +08:00
parent 28905b0bec
commit 9c49f4bb1e
5 changed files with 107 additions and 59 deletions

View File

@ -6,6 +6,7 @@ import type {
} from '@vben-core/form-ui'; } from '@vben-core/form-ui';
import type { VxeGridProps } from './types'; import type { VxeGridProps } from './types';
import type {ViewedRowHelper} from './use-viewed-row';
import { toRaw } from 'vue'; import { toRaw } from 'vue';
@ -42,20 +43,15 @@ export class VxeGridApi<
public store: Store<VxeGridProps<T, D, P>>; public store: Store<VxeGridProps<T, D, P>>;
/**
* helper mount useViewedRow
*/
public viewedRowHelper: null | ViewedRowHelper<T> = null;
private isMounted = false; private isMounted = false;
private stateHandler: StateHandler; private stateHandler: StateHandler;
// 已读行相关方法(由 use-vxe-grid.vue 注入)
private viewedRowHelper: null | {
clearViewed: () => void;
isViewed: (record: T) => boolean;
markAsViewed: (record: T) => void;
markKeysAsViewed: (keys: Array<number | string>) => void;
removeKeys: (keys: Array<number | string>) => void;
viewedSet: { value: Set<number | string> };
} = null;
constructor(options: VxeGridProps<T, D, P> = {} as VxeGridProps<T, D, P>) { constructor(options: VxeGridProps<T, D, P> = {} as VxeGridProps<T, D, P>) {
const storeState = { ...options }; const storeState = { ...options };
@ -82,7 +78,7 @@ export class VxeGridApi<
} }
/** /**
* key * key
*/ */
getViewedKeys(): Set<number | string> { getViewedKeys(): Set<number | string> {
const raw = this.viewedRowHelper?.viewedSet.value; const raw = this.viewedRowHelper?.viewedSet.value;
@ -170,14 +166,6 @@ export class VxeGridApi<
} }
} }
/**
* helper
* @internal
*/
setViewedRowHelper(helper: VxeGridApi<T, D, P>['viewedRowHelper']) {
this.viewedRowHelper = helper;
}
toggleSearchForm(show?: boolean) { toggleSearchForm(show?: boolean) {
this.setState({ this.setState({
showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm, showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm,
@ -191,5 +179,6 @@ export class VxeGridApi<
unmount() { unmount() {
this.isMounted = false; this.isMounted = false;
this.stateHandler.reset(); this.stateHandler.reset();
this.viewedRowHelper = null;
} }
} }

View File

@ -168,7 +168,7 @@ export interface VxeGridProps<
/** /**
* *
*/ */
viewedRow?: boolean | ViewedRowOptions<T>; viewedRowOptions?: boolean | ViewedRowOptions<T>;
} }
export type ExtendedVxeGridApi< export type ExtendedVxeGridApi<

View File

@ -373,6 +373,8 @@ export function useViewedRow<T = any>(
}; };
} }
export type ViewedRowHelper<T = any> = ReturnType<typeof useViewedRow<T>>;
// ========== 工具函数 ========== // ========== 工具函数 ==========
function normalizeClassName(value: any): string { function normalizeClassName(value: any): string {
@ -445,24 +447,64 @@ export function applyViewedRowOptions(
viewedRowConfig: boolean | ViewedRowOptions, viewedRowConfig: boolean | ViewedRowOptions,
helper: ReturnType<typeof useViewedRow>, helper: ReturnType<typeof useViewedRow>,
) { ) {
// 从最新的配置中读取 rowClassName 和 rowStyle支持运行时修改
const viewedRowClassName = isBoolean(viewedRowConfig)
? undefined
: viewedRowConfig.rowClassName;
const viewedRowStyle = isBoolean(viewedRowConfig)
? undefined
: viewedRowConfig.rowStyle;
// 注入 rowClassName // 注入 rowClassName
const originalRowClassName = mergedOptions.rowClassName; const originalRowClassName = mergedOptions.rowClassName;
mergedOptions.rowClassName = (params: any) => { mergedOptions.rowClassName = (params: any) => {
if (!helper.isViewed(params.row)) {
return normalizeClassName(
isFunction(originalRowClassName)
? originalRowClassName(params)
: originalRowClassName,
);
}
let viewedClass: string;
if (viewedRowClassName === undefined || viewedRowClassName === null) {
viewedClass = DEFAULT_VIEWED_CLASS;
} else if (typeof viewedRowClassName === 'string') {
viewedClass = viewedRowClassName;
} else if (isFunction(viewedRowClassName)) {
viewedClass = normalizeClassName(viewedRowClassName(params));
} else {
viewedClass = DEFAULT_VIEWED_CLASS;
}
return mergeClassNames( return mergeClassNames(
isFunction(originalRowClassName) isFunction(originalRowClassName)
? originalRowClassName(params) ? originalRowClassName(params)
: originalRowClassName, : originalRowClassName,
helper.getRowClassName(params), viewedClass,
); );
}; };
// 注入 rowStyle // 注入 rowStyle
const originalRowStyle = mergedOptions.rowStyle; const originalRowStyle = mergedOptions.rowStyle;
mergedOptions.rowStyle = (params: any) => { mergedOptions.rowStyle = (params: any) => {
const viewedStyle = helper.getRowStyle(params);
const originalStyle = isFunction(originalRowStyle) const originalStyle = isFunction(originalRowStyle)
? originalRowStyle(params) ? originalRowStyle(params)
: originalRowStyle; : originalRowStyle;
if (!helper.isViewed(params.row)) {
return originalStyle || undefined;
}
let viewedStyle: any;
if (viewedRowStyle === undefined || viewedRowStyle === null) {
viewedStyle = undefined;
} else if (isFunction(viewedRowStyle)) {
viewedStyle = viewedRowStyle(params);
} else {
viewedStyle = viewedRowStyle;
}
if (!viewedStyle && !originalStyle) return undefined; if (!viewedStyle && !originalStyle) return undefined;
if (!originalStyle) return viewedStyle; if (!originalStyle) return viewedStyle;
if (!viewedStyle) return originalStyle; if (!viewedStyle) return originalStyle;

View File

@ -19,12 +19,10 @@ import {
nextTick, nextTick,
onMounted, onMounted,
onUnmounted, onUnmounted,
shallowRef,
toRaw, toRaw,
useSlots, useSlots,
useTemplateRef, useTemplateRef,
watch, watch,
watchEffect,
} from 'vue'; } from 'vue';
import { usePriorityValues } from '@vben/hooks'; import { usePriorityValues } from '@vben/hooks';
@ -79,45 +77,31 @@ const {
tableTitleHelp, tableTitleHelp,
showSearchForm, showSearchForm,
separator, separator,
viewedRow, viewedRowOptions,
} = usePriorityValues(props, state); } = usePriorityValues(props, state);
// ========== viewedRow ========== // viewedRowOptionshelper persist/keyField
const defaultKeyField = (gridOptions.value?.rowConfig as any)?.keyField || 'id'; // actionCodesrowClassNamerowStyleviewedKeys options computed
const gridApi = props.api;
const viewedRowHelper = shallowRef<null | ReturnType<typeof useViewedRow>>(
null,
);
// + helper
watch( watch(
viewedRow, viewedRowOptions,
(cfg) => { (cfg) => {
if (!cfg) { // helper
viewedRowHelper.value = null; if (gridApi.viewedRowHelper) return;
props.api?.setViewedRowHelper?.(null);
return; if (!cfg) return;
}
const resolvedOptions = isBoolean(cfg) const keyField =
? {keyField: defaultKeyField} (gridOptions.value?.rowConfig as any)?.keyField || 'id';
: {keyField: defaultKeyField, ...cfg}; const resolved = isBoolean(cfg)
viewedRowHelper.value = useViewedRow(resolvedOptions); ? {keyField}
// API helper : {keyField, ...cfg};
if (props.api?.setViewedRowHelper) { gridApi.viewedRowHelper = useViewedRow(resolved);
props.api.setViewedRowHelper(viewedRowHelper.value);
}
}, },
{immediate: true}, {immediate: true},
); );
// viewedSet grid
watchEffect(() => {
const helper = viewedRowHelper.value;
if (!helper) return;
// 访 viewedSet.value
void helper.viewedSet.value;
});
const { isMobile } = usePreferences(); const { isMobile } = usePreferences();
const isSeparator = computed(() => { const isSeparator = computed(() => {
if ( if (
@ -276,11 +260,11 @@ const options = computed(() => {
} }
// rowClassNamerowStylecolumns // rowClassNamerowStylecolumns
if (viewedRow.value && viewedRowHelper.value) { if (viewedRowOptions.value && gridApi.viewedRowHelper) {
applyViewedRowOptions( applyViewedRowOptions(
mergedOptions, mergedOptions,
viewedRow.value, viewedRowOptions.value,
viewedRowHelper.value, gridApi.viewedRowHelper,
); );
} }

View File

@ -87,7 +87,7 @@ const gridOptions: VxeGridProps<RowType> = {
const [Grid, gridApi] = useVbenVxeGrid({ const [Grid, gridApi] = useVbenVxeGrid({
gridOptions, gridOptions,
viewedRow: { viewedRowOptions: {
// //
actionCodes: ['view'], actionCodes: ['view'],
// //
@ -144,6 +144,33 @@ function onView(row: RowType) {
}); });
} }
const isStyle = ref(false);
function onStyleSet() {
isStyle.value = !isStyle.value;
gridApi.setState({
viewedRowOptions: {
rowStyle: () => {
return isStyle.value ? {backgroundColor: 'gray'} : '';
},
},
});
}
const isClassName = ref(false);
function onClassNameSet() {
isClassName.value = !isClassName.value;
gridApi.setState({
viewedRowOptions: {
rowClassName: () => {
return isClassName.value ? 'bg-red-100 vxe-row--viewed' : 'vxe-row--viewed';
},
},
});
}
function onCustomSet() { function onCustomSet() {
const tableData = gridApi.grid.getData(); const tableData = gridApi.grid.getData();
const keys = tableData.slice(0, 2).map((row) => row.id); const keys = tableData.slice(0, 2).map((row) => row.id);
@ -168,7 +195,13 @@ function onClearViewed() {
<Grid table-title="" table-title-help=""> <Grid table-title="" table-title-help="">
<template #toolbar-tools> <template #toolbar-tools>
<Button class="mr-2" type="primary" @click="onCustomSet"> <Button class="mr-2" type="primary" @click="onCustomSet">
手动设置 手动标记
</Button>
<Button class="mr-2" type="primary" @click="onStyleSet">
设置Style
</Button>
<Button class="mr-2" type="primary" @click="onClassNameSet">
设置ClassName
</Button> </Button>
<Button type="primary" @click="onClearViewed"> </Button> <Button type="primary" @click="onClearViewed"> </Button>
</template> </template>