Parcourir la source

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

layhuts il y a 1 mois
Parent
commit
9c49f4bb1e

+ 8 - 19
packages/effects/plugins/src/vxe-table/api.ts

@@ -6,6 +6,7 @@ import type {
 } from '@vben-core/form-ui';
 
 import type { VxeGridProps } from './types';
+import type {ViewedRowHelper} from './use-viewed-row';
 
 import { toRaw } from 'vue';
 
@@ -42,20 +43,15 @@ export class VxeGridApi<
 
   public store: Store<VxeGridProps<T, D, P>>;
 
+  /**
+   * 已读行 helper(在 mount 中初始化,业务能力全部封装在 useViewedRow 中)
+   */
+  public viewedRowHelper: null | ViewedRowHelper<T> = null;
+
   private isMounted = false;
 
   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>) {
     const storeState = { ...options };
 
@@ -82,7 +78,7 @@ export class VxeGridApi<
   }
 
   /**
-   * 获取所有已读的 key 集合
+   * 获取所有已读的 key 集合(返回副本,避免外部修改内部状态)
    */
   getViewedKeys(): Set<number | string> {
     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) {
     this.setState({
       showSearchForm: isBoolean(show) ? show : !this.state?.showSearchForm,
@@ -191,5 +179,6 @@ export class VxeGridApi<
   unmount() {
     this.isMounted = false;
     this.stateHandler.reset();
+    this.viewedRowHelper = null;
   }
 }

+ 1 - 1
packages/effects/plugins/src/vxe-table/types.ts

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

+ 44 - 2
packages/effects/plugins/src/vxe-table/use-viewed-row.ts

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

+ 19 - 35
packages/effects/plugins/src/vxe-table/use-vxe-grid.vue

@@ -19,12 +19,10 @@ import {
   nextTick,
   onMounted,
   onUnmounted,
-  shallowRef,
   toRaw,
   useSlots,
   useTemplateRef,
   watch,
-  watchEffect,
 } from 'vue';
 
 import { usePriorityValues } from '@vben/hooks';
@@ -79,45 +77,31 @@ const {
   tableTitleHelp,
   showSearchForm,
   separator,
-  viewedRow,
+  viewedRowOptions,
 } = usePriorityValues(props, state);
 
-// ========== 已读行:响应 viewedRow 配置变化 ==========
-const defaultKeyField = (gridOptions.value?.rowConfig as any)?.keyField || 'id';
+// viewedRowOptions:helper 只创建一次(persist/keyField 不支持运行时切换)
+// actionCodes、rowClassName、rowStyle、viewedKeys 的变化通过 options computed 自然响应
+const gridApi = props.api;
 
-const viewedRowHelper = shallowRef<null | ReturnType<typeof useViewedRow>>(
-  null,
-);
-
-// 初始化 + 监听配置变化时重建 helper
 watch(
-  viewedRow,
+  viewedRowOptions,
   (cfg) => {
-    if (!cfg) {
-      viewedRowHelper.value = null;
-      props.api?.setViewedRowHelper?.(null);
-      return;
-    }
-    const resolvedOptions = isBoolean(cfg)
-      ? {keyField: defaultKeyField}
-      : {keyField: defaultKeyField, ...cfg};
-    viewedRowHelper.value = useViewedRow(resolvedOptions);
-    // 同步更新 API 中的 helper 引用
-    if (props.api?.setViewedRowHelper) {
-      props.api.setViewedRowHelper(viewedRowHelper.value);
-    }
+    // helper 已存在则不重建
+    if (gridApi.viewedRowHelper) return;
+
+    if (!cfg) return;
+
+    const keyField =
+      (gridOptions.value?.rowConfig as any)?.keyField || 'id';
+    const resolved = isBoolean(cfg)
+      ? {keyField}
+      : {keyField, ...cfg};
+    gridApi.viewedRowHelper = useViewedRow(resolved);
   },
   {immediate: true},
 );
 
-// viewedSet 变化时,主动刷新 grid 行样式
-watchEffect(() => {
-  const helper = viewedRowHelper.value;
-  if (!helper) return;
-  // 访问 viewedSet.value 建立依赖追踪
-  void helper.viewedSet.value;
-});
-
 const { isMobile } = usePreferences();
 const isSeparator = computed(() => {
   if (
@@ -276,11 +260,11 @@ const options = computed(() => {
   }
 
   // 注入已读行功能(rowClassName、rowStyle、columns 拦截)
-  if (viewedRow.value && viewedRowHelper.value) {
+  if (viewedRowOptions.value && gridApi.viewedRowHelper) {
     applyViewedRowOptions(
       mergedOptions,
-      viewedRow.value,
-      viewedRowHelper.value,
+      viewedRowOptions.value,
+      gridApi.viewedRowHelper,
     );
   }
 

+ 35 - 2
playground/src/views/examples/vxe-table/viewed.vue

@@ -87,7 +87,7 @@ const gridOptions: VxeGridProps<RowType> = {
 
 const [Grid, gridApi] = useVbenVxeGrid({
   gridOptions,
-  viewedRow: {
+  viewedRowOptions: {
     // 触发已读的操作码
     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() {
   const tableData = gridApi.grid.getData();
   const keys = tableData.slice(0, 2).map((row) => row.id);
@@ -168,7 +195,13 @@ function onClearViewed() {
     <Grid table-title="已查看行标记" table-title-help="提示">
       <template #toolbar-tools>
         <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 type="primary" @click="onClearViewed"> 清空缓存</Button>
       </template>