IM聊天功能优化
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { reactive, ref, Ref, unref } from 'vue';
|
||||
import { reactive, ref, Ref, unref, onUnmounted, watch, nextTick } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { merge } from 'lodash-es';
|
||||
import { DynamicProps } from '/#/utils';
|
||||
import { BasicTableProps, TableActionType, useTable } from '/@/components/Table';
|
||||
@@ -9,6 +10,16 @@ import { useMethods } from '/@/hooks/system/useMethods';
|
||||
import { useDesign } from '/@/hooks/web/useDesign';
|
||||
import { filterObj } from '/@/utils/common/compUtils';
|
||||
import { isFunction } from '@/utils/is';
|
||||
import { registerImPageListProvider } from '/@/views/system/im/imPageListRegistry';
|
||||
import { buildImPageListSnapshot } from '/@/views/system/im/imPageListUtil';
|
||||
import { IM_RECORD_QUERY_KEY } from '/@/views/system/im/imBizRecordMessage';
|
||||
import {
|
||||
IM_RECORD_LOCATE_CLEAR_EVENT,
|
||||
IM_RECORD_LOCATE_EVENT,
|
||||
removeImRecordQueryFromRoute,
|
||||
resolveImLocateRecordId,
|
||||
scrollToImRecordRowWithRetry,
|
||||
} from '/@/views/system/im/imRecordLocate';
|
||||
const { handleExportXls, handleImportXls } = useMethods();
|
||||
|
||||
// 定义 useListPage 方法所需参数
|
||||
@@ -59,7 +70,168 @@ export function useListPage(options: ListPageOptions) {
|
||||
|
||||
const tableContext = useListTable(options.tableProps);
|
||||
|
||||
const [, { getForm, reload, setLoading, getColumns }, { selectedRowKeys }] = tableContext;
|
||||
const route = useRoute();
|
||||
const [, tableMethods, { selectedRowKeys }] = tableContext;
|
||||
const { getForm, reload, setLoading, getColumns } = tableMethods;
|
||||
const imHighlightRecordId = ref('');
|
||||
let clearHighlightTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let locatingRecordId = '';
|
||||
|
||||
onUnmounted(() => {
|
||||
if (clearHighlightTimer) {
|
||||
clearTimeout(clearHighlightTimer);
|
||||
clearHighlightTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】列表页注册 IM 明细快照提供器-----------
|
||||
onUnmounted(
|
||||
registerImPageListProvider(() => {
|
||||
const sourceColumns = tableMethods.getColumns?.() || options.tableProps?.columns || [];
|
||||
return buildImPageListSnapshot({
|
||||
title: (options.tableProps?.title as string) || '',
|
||||
pagePath: route.fullPath,
|
||||
rowKey: (options.tableProps?.rowKey as string) || 'id',
|
||||
sourceColumns,
|
||||
records: tableMethods.getDataSource?.() || [],
|
||||
});
|
||||
}),
|
||||
);
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】列表页注册 IM 明细快照提供器-----------
|
||||
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】IM 消息链接跳转后定位列表行-----------
|
||||
function isTableReady() {
|
||||
try {
|
||||
tableMethods.getDataSource?.();
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function applyImRecordRowClassName() {
|
||||
if (!isTableReady()) {
|
||||
return;
|
||||
}
|
||||
const rowKey = (options.tableProps?.rowKey as string) || 'id';
|
||||
tableMethods.setProps?.({
|
||||
rowClassName: (record: Recordable) => {
|
||||
if (imHighlightRecordId.value && String(record[rowKey]) === imHighlightRecordId.value) {
|
||||
return 'im-record-locate-row';
|
||||
}
|
||||
return '';
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/** 等待列表首屏数据加载(兼容 immediate:false + 左侧树页面) */
|
||||
async function waitForLocateContext(maxWaitMs = 3500) {
|
||||
const start = Date.now();
|
||||
while (Date.now() - start < maxWaitMs) {
|
||||
if (!isTableReady()) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 50));
|
||||
continue;
|
||||
}
|
||||
const data = tableMethods.getDataSource?.() || [];
|
||||
if (data.length > 0) {
|
||||
return true;
|
||||
}
|
||||
await new Promise((resolve) => setTimeout(resolve, 100));
|
||||
}
|
||||
return isTableReady();
|
||||
}
|
||||
|
||||
function findRecordInTable(recordId: string) {
|
||||
const rowKey = (options.tableProps?.rowKey as string) || 'id';
|
||||
const data = tableMethods.getDataSource?.() || [];
|
||||
return data.some((item) => String(item[rowKey]) === recordId);
|
||||
}
|
||||
|
||||
function clearImRecordHighlight() {
|
||||
imHighlightRecordId.value = '';
|
||||
applyImRecordRowClassName();
|
||||
}
|
||||
|
||||
function scheduleClearImRecordHighlight(delayMs = 3500) {
|
||||
if (clearHighlightTimer) {
|
||||
clearTimeout(clearHighlightTimer);
|
||||
}
|
||||
clearHighlightTimer = setTimeout(() => {
|
||||
clearImRecordHighlight();
|
||||
clearHighlightTimer = null;
|
||||
}, delayMs);
|
||||
}
|
||||
|
||||
async function applyImRecordHighlight(recordId: string) {
|
||||
imHighlightRecordId.value = recordId;
|
||||
applyImRecordRowClassName();
|
||||
await nextTick();
|
||||
await new Promise((resolve) => requestAnimationFrame(() => resolve(undefined)));
|
||||
applyImRecordRowClassName();
|
||||
await scrollToImRecordRowWithRetry(recordId);
|
||||
}
|
||||
|
||||
async function locateImRecordRow(recordId: string) {
|
||||
if (locatingRecordId === recordId) {
|
||||
return;
|
||||
}
|
||||
locatingRecordId = recordId;
|
||||
try {
|
||||
if (!(await waitForLocateContext())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!findRecordInTable(recordId)) {
|
||||
$message.createMessage.warning('当前列表中未找到对应数据');
|
||||
removeImRecordQueryFromRoute();
|
||||
return;
|
||||
}
|
||||
|
||||
await applyImRecordHighlight(recordId);
|
||||
scheduleClearImRecordHighlight();
|
||||
// 直链 URL 场景:仅改地址栏,避免 router.replace 导致 fullPath 变化 remount
|
||||
removeImRecordQueryFromRoute();
|
||||
} finally {
|
||||
locatingRecordId = '';
|
||||
}
|
||||
}
|
||||
|
||||
watch(
|
||||
() => [route.path, route.query[IM_RECORD_QUERY_KEY]] as const,
|
||||
([path, queryRecordId]) => {
|
||||
const recordId = resolveImLocateRecordId(path, queryRecordId);
|
||||
if (!recordId) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => locateImRecordRow(recordId));
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
function handleImRecordLocateEvent(e: Event) {
|
||||
const detail = (e as CustomEvent<{ path: string; recordId: string }>).detail;
|
||||
if (!detail?.path || detail.path !== route.path || !detail.recordId) {
|
||||
return;
|
||||
}
|
||||
nextTick(() => locateImRecordRow(detail.recordId));
|
||||
}
|
||||
|
||||
function handleImRecordLocateClearEvent() {
|
||||
if (clearHighlightTimer) {
|
||||
clearTimeout(clearHighlightTimer);
|
||||
clearHighlightTimer = null;
|
||||
}
|
||||
locatingRecordId = '';
|
||||
clearImRecordHighlight();
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
window.removeEventListener(IM_RECORD_LOCATE_EVENT, handleImRecordLocateEvent);
|
||||
window.removeEventListener(IM_RECORD_LOCATE_CLEAR_EVENT, handleImRecordLocateClearEvent);
|
||||
});
|
||||
window.addEventListener(IM_RECORD_LOCATE_EVENT, handleImRecordLocateEvent);
|
||||
window.addEventListener(IM_RECORD_LOCATE_CLEAR_EVENT, handleImRecordLocateClearEvent);
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】IM 消息链接跳转后定位列表行-----------
|
||||
|
||||
// 导出 excel
|
||||
async function onExportXls() {
|
||||
|
||||
@@ -8,6 +8,7 @@ import { useRouter } from 'vue-router';
|
||||
import { REDIRECT_NAME } from '/@/router/constant';
|
||||
import { useUserStore } from '/@/store/modules/user';
|
||||
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
|
||||
import { clearImRecordLocateState, stripImRecordQuery } from '/@/views/system/im/imRecordLocate';
|
||||
|
||||
export type RouteLocationRawEx = Omit<RouteLocationRaw, 'path'> & { path: PageEnum };
|
||||
|
||||
@@ -43,10 +44,17 @@ export function useGo(_router?: Router) {
|
||||
* @description: redo current page
|
||||
*/
|
||||
export const useRedo = (_router?: Router, otherQuery?: Recordable) => {
|
||||
const { push, currentRoute } = _router || useRouter();
|
||||
const { query, params = {}, name, fullPath } = unref(currentRoute.value);
|
||||
const router = _router || useRouter();
|
||||
const { push, currentRoute, resolve: resolveRoute } = router;
|
||||
function redo(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
//update-begin---author:xsl ---date:20260528 for:【IM聊天-OA】标签页刷新时取消 IM 定位-----------
|
||||
clearImRecordLocateState();
|
||||
const rawRoute = unref(currentRoute.value);
|
||||
let { query, params = {}, name, fullPath } = rawRoute;
|
||||
query = stripImRecordQuery(query as Recordable);
|
||||
fullPath = resolveRoute({ path: rawRoute.path, query, hash: rawRoute.hash }).fullPath;
|
||||
//update-end---author:xsl ---date:20260528 for:【IM聊天-OA】标签页刷新时取消 IM 定位-----------
|
||||
if (name === REDIRECT_NAME) {
|
||||
resolve(false);
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user