diff --git a/packages/components/message/__tests__/message.test.tsx b/packages/components/message/__tests__/message.test.tsx
index ade25edd98..3a4d0d07a5 100644
--- a/packages/components/message/__tests__/message.test.tsx
+++ b/packages/components/message/__tests__/message.test.tsx
@@ -73,6 +73,56 @@ describe('Message', () => {
MessagePlugin.closeAll();
});
+ it('should reset timer when messages are merged', async () => {
+ vi.useFakeTimers();
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 发送第一条消息,设置较短的duration
+ MessagePlugin.info({
+ content: '定时器测试',
+ mergeIdentical: true,
+ duration: 2000, // 2秒后消失
+ });
+
+ await nextTick();
+
+ let messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+
+ // 等待1500ms,第一条消息即将消失
+ vi.advanceTimersByTime(1500);
+ await nextTick();
+
+ messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+
+ // 发送第二条相同消息,duration为5000ms,应该重置定时器
+ MessagePlugin.info({
+ content: '定时器测试',
+ mergeIdentical: true,
+ duration: 5000, // 5秒后消失
+ });
+
+ await nextTick();
+
+ // 应该仍然只有1条消息,但计数为2
+ messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('(×2)');
+
+ // 再等待1000ms(总共2500ms),如果定时器没有重置,消息应该已经消失了
+ // 但是因为定时器被重置为5000ms,所以消息应该还存在
+ vi.advanceTimersByTime(1000);
+ await nextTick();
+
+ messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1); // 这里应该通过,证明定时器被重置了
+
+ vi.useRealTimers();
+ MessagePlugin.closeAll();
+ });
+
it('should create only one instance per placement', async () => {
MessagePlugin.info('msg1', 0);
MessagePlugin.info('msg2', 0);
@@ -100,5 +150,200 @@ describe('Message', () => {
expect(document.querySelectorAll('.t-message').length).toBe(0);
});
+
+ it('should merge identical messages correctly when count > 2', async () => {
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 测试基本的2条消息合并
+ MessagePlugin.info({
+ content: '测试合并消息',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ MessagePlugin.info({
+ content: '测试合并消息',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ // 检查是否合并为1条消息
+ const messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('(×2)');
+
+ MessagePlugin.closeAll();
+ });
+
+ it('should handle merge with different content correctly', async () => {
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 发送不同内容的消息,不应该合并
+ MessagePlugin.info({
+ content: '消息1',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ MessagePlugin.info({
+ content: '消息2',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ // 应该有2条不同的消息
+ const messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(2);
+
+ MessagePlugin.closeAll();
+ });
+
+ it('should merge multiple identical messages step by step', async () => {
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 逐步发送相同消息并验证每一步
+ MessagePlugin.info({
+ content: '步骤测试',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ let messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('步骤测试');
+ expect(messages[0].textContent).not.toContain('×');
+
+ // 第2条消息
+ MessagePlugin.info({
+ content: '步骤测试',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('(×2)');
+
+ // 第3条消息
+ MessagePlugin.info({
+ content: '步骤测试',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('(×3)');
+
+ MessagePlugin.closeAll();
+ });
+
+ it('should merge messages with custom merge window', async () => {
+ vi.useFakeTimers();
+
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 发送第一条消息
+ MessagePlugin.info({
+ content: '长窗口合并测试',
+ mergeIdentical: true,
+ mergeWindow: 2000,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ // 1.5秒后发送第二条相同消息,应该能合并
+ vi.advanceTimersByTime(1500);
+ MessagePlugin.info({
+ content: '长窗口合并测试',
+ mergeIdentical: true,
+ mergeWindow: 2000,
+ duration: 0,
+ });
+
+ await nextTick();
+ vi.runOnlyPendingTimers();
+ await nextTick();
+
+ // 应该只有1条消息(合并后的)
+ const messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('(×2)');
+
+ vi.useRealTimers();
+ MessagePlugin.closeAll();
+ });
+
+ it('should not merge messages with different themes', async () => {
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 发送相同内容但不同主题的消息
+ MessagePlugin.info({
+ content: '相同内容不同主题',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ MessagePlugin.error({
+ content: '相同内容不同主题',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ // 应该有2条消息(不同主题不合并)
+ const messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(2);
+
+ MessagePlugin.closeAll();
+ });
+
+ it('should merge messages with custom merge key', async () => {
+ MessagePlugin.closeAll();
+ await nextTick();
+
+ // 发送不同内容但相同 mergeKey 的消息
+ MessagePlugin.info({
+ content: '消息内容1',
+ mergeKey: 'custom-key',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ MessagePlugin.warning({
+ content: '消息内容2',
+ mergeKey: 'custom-key',
+ mergeIdentical: true,
+ duration: 0,
+ });
+
+ await nextTick();
+
+ // 应该只有1条消息(相同 mergeKey 会合并)
+ const messages = document.querySelectorAll('.t-message');
+ expect(messages.length).toBe(1);
+ expect(messages[0].textContent).toContain('(×2)');
+
+ MessagePlugin.closeAll();
+ });
});
});
diff --git a/packages/components/message/_example-ts/merge.vue b/packages/components/message/_example-ts/merge.vue
new file mode 100644
index 0000000000..e127b8b1c4
--- /dev/null
+++ b/packages/components/message/_example-ts/merge.vue
@@ -0,0 +1,228 @@
+
+
+
+ 基础合并功能
+
+ 触发相同消息(会合并)
+
+ 不同主题相同内容(不会合并)
+
+ 自定义合并标识
+
+
+
+
+ 合并配置选项
+
+ 不显示合并计数
+ 自定义计数格式
+ 自定义合并窗口
+
+
+
+
+ 全局配置
+
+ 启用全局合并
+ 测试全局合并
+ 禁用全局合并
+
+
+
+
+ 管理功能
+
+ 清除特定类型消息
+ 清除所有消息
+
+
+
+
+ 压力测试
+
+ 快速触发多个相同消息
+ 混合消息测试
+
+
+
+
+
+
diff --git a/packages/components/message/_example/merge.vue b/packages/components/message/_example/merge.vue
new file mode 100644
index 0000000000..337eb9802d
--- /dev/null
+++ b/packages/components/message/_example/merge.vue
@@ -0,0 +1,225 @@
+
+
+
+ 基础合并功能
+
+ 触发相同消息(会合并)
+
+ 不同主题相同内容(不会合并)
+
+ 自定义合并标识
+
+
+
+
+ 合并配置选项
+
+ 不显示合并计数
+ 自定义计数格式
+ 自定义合并窗口
+
+
+
+
+ 全局配置
+
+ 启用全局合并
+ 测试全局合并
+ 禁用全局合并
+
+
+
+
+ 管理功能
+
+ 清除特定类型消息
+ 清除所有消息
+
+
+
+
+ 压力测试
+
+ 快速触发多个相同消息
+ 混合消息测试
+
+
+
+
+
+
diff --git a/packages/components/message/message-list.tsx b/packages/components/message/message-list.tsx
index 519bb93e5f..5bcf28ec49 100644
--- a/packages/components/message/message-list.tsx
+++ b/packages/components/message/message-list.tsx
@@ -2,8 +2,9 @@ import { computed, defineComponent, ref } from 'vue';
import type { CSSProperties } from 'vue';
import { PLACEMENT_OFFSET } from './consts';
import TMessage from './message';
-import { MessageOptions } from './type';
+import { MessageOptions, MessageItemInternal, MessageMergeConfig } from './type';
import { usePrefixClass } from '@tdesign/shared-hooks';
+import { isString } from 'lodash-es';
export const DEFAULT_Z_INDEX = 6000;
@@ -29,16 +30,159 @@ export const MessageList = defineComponent({
},
setup(props, { expose }) {
const COMPONENT_NAME = usePrefixClass('message__list');
- const list = ref([]);
+ const list = ref([]);
const messageList = ref([]);
+ // 全局合并配置
+ const globalMergeConfig = ref({
+ mergeIdentical: false,
+ mergeWindow: 500,
+ maxMergeCount: 99,
+ showMergeCount: true,
+ mergeCountFormat: '(×{count})',
+ });
+
+ // 合并定时器映射
+ const mergeTimers = new Map();
+
+ /**
+ * 生成消息合并标识
+ */
+ const generateMergeKey = (msg: MessageOptions): string => {
+ if (msg.mergeKey) {
+ return msg.mergeKey;
+ }
+ const content = isString(msg.content) ? msg.content : JSON.stringify(msg.content);
+ return `${msg.theme || 'info'}-${content}`;
+ };
+
+ /**
+ * 格式化显示内容,添加合并计数
+ */
+ const formatContentWithCount = (item: MessageItemInternal): string | any => {
+ const {
+ originalContent,
+ mergeCount,
+ showMergeCount: _showMergeCount,
+ mergeCountFormat: _mergeCountFormat,
+ } = item;
+ const config = { ...globalMergeConfig.value, ...item };
+
+ if (!config.showMergeCount || !mergeCount || mergeCount <= 1) {
+ return originalContent || item.content;
+ }
+
+ const countText = (config.mergeCountFormat || '(×{count})').replace('{count}', String(mergeCount));
+
+ if (isString(originalContent)) {
+ return `${originalContent} ${countText}`;
+ }
+
+ return originalContent || item.content;
+ };
+
const styles = computed(() => ({
...(PLACEMENT_OFFSET[props.placement as keyof typeof PLACEMENT_OFFSET] as CSSProperties),
zIndex: props.zIndex !== DEFAULT_Z_INDEX ? props.zIndex : DEFAULT_Z_INDEX,
}));
const add = (msg: MessageOptions): number => {
- const mg = { ...msg, key: getUniqueId() };
+ const config = { ...globalMergeConfig.value, ...msg };
+
+ // 如果启用了合并功能
+ if (config.mergeIdentical) {
+ const mergeKey = generateMergeKey(msg);
+
+ // 查找是否存在相同的消息
+ const existingIndex = list.value.findIndex((item) => {
+ // 使用原始内容生成合并键,避免合并计数影响匹配
+ const itemMergeKey = generateMergeKey({
+ ...item,
+ content: item.originalContent || item.content,
+ });
+ return itemMergeKey === mergeKey;
+ });
+
+ if (existingIndex !== -1) {
+ const existingItem = list.value[existingIndex];
+ const newCount = (existingItem.mergeCount || 1) + 1;
+
+ // 检查是否超过最大合并次数
+ if (newCount <= (config.maxMergeCount || 99)) {
+ // 清除之前的合并定时器
+ if (existingItem.mergeTimer) {
+ clearTimeout(existingItem.mergeTimer);
+ }
+
+ // 更新现有消息
+ const updatedItem: MessageItemInternal = {
+ ...existingItem,
+ ...msg, // 使用新消息的配置覆盖
+ key: existingItem.key, // 保持原有的 key
+ mergeCount: newCount,
+ originalContent: existingItem.originalContent || existingItem.content,
+ content: formatContentWithCount({
+ ...existingItem,
+ mergeCount: newCount,
+ originalContent: existingItem.originalContent || existingItem.content,
+ }),
+ };
+
+ // 清除旧的定时器并重新设置消息显示定时器
+ if (existingItem.mergeTimer) {
+ clearTimeout(existingItem.mergeTimer);
+ }
+ if (updatedItem.duration && updatedItem.duration > 0) {
+ updatedItem.mergeTimer = window.setTimeout(() => {
+ // 重新查找消息索引,因为数组可能已经变化
+ const currentIndex = list.value.findIndex((item) => item.key === updatedItem.key);
+ if (currentIndex !== -1) {
+ remove(currentIndex);
+ }
+ }, updatedItem.duration);
+ }
+
+ list.value[existingIndex] = updatedItem;
+
+ return existingItem.key;
+ }
+ }
+
+ // 设置合并定时器,在合并窗口期内等待相同消息
+ if (config.mergeWindow && config.mergeWindow > 0) {
+ const timer = mergeTimers.get(mergeKey);
+ if (timer) {
+ clearTimeout(timer);
+ }
+
+ mergeTimers.set(
+ mergeKey,
+ window.setTimeout(() => {
+ mergeTimers.delete(mergeKey);
+ }, config.mergeWindow),
+ );
+ }
+ }
+
+ // 创建新消息
+ const mg: MessageItemInternal = {
+ ...msg,
+ key: getUniqueId(),
+ mergeCount: 1,
+ originalContent: msg.content,
+ };
+
+ // 为新消息设置初始定时器
+ if (mg.duration && mg.duration > 0) {
+ mg.mergeTimer = window.setTimeout(() => {
+ // 重新查找消息索引,因为数组可能已经变化
+ const currentIndex = list.value.findIndex((item) => item.key === mg.key);
+ if (currentIndex !== -1) {
+ remove(currentIndex);
+ }
+ }, mg.duration);
+ }
+
list.value.push(mg);
return mg.key;
};
@@ -48,15 +192,59 @@ export const MessageList = defineComponent({
};
const removeAll = () => {
+ // 清除所有合并定时器
+ list.value.forEach((item) => {
+ if (item.mergeTimer) {
+ clearTimeout(item.mergeTimer);
+ }
+ });
+ mergeTimers.forEach((timer) => clearTimeout(timer));
+ mergeTimers.clear();
+
list.value = [];
};
+ /**
+ * 根据合并标识清除消息
+ */
+ const clearByKey = (mergeKey: string) => {
+ const indexesToRemove: number[] = [];
+
+ list.value.forEach((item, index) => {
+ const itemMergeKey = generateMergeKey(item);
+ if (itemMergeKey === mergeKey) {
+ if (item.mergeTimer) {
+ clearTimeout(item.mergeTimer);
+ }
+ indexesToRemove.push(index);
+ }
+ });
+
+ // 从后往前删除,避免索引变化
+ indexesToRemove.reverse().forEach((index) => {
+ list.value.splice(index, 1);
+ });
+
+ // 清除对应的合并定时器
+ if (mergeTimers.has(mergeKey)) {
+ clearTimeout(mergeTimers.get(mergeKey));
+ mergeTimers.delete(mergeKey);
+ }
+ };
+
+ /**
+ * 配置全局合并选项
+ */
+ const configMerge = (config: Partial) => {
+ Object.assign(globalMergeConfig.value, config);
+ };
+
const getOffset = (val: string | number) => {
if (!val) return;
return isNaN(Number(val)) ? val : `${val}px`;
};
- const msgStyles = (item: { offset: Array }) => {
+ const msgStyles = (item: MessageItemInternal) => {
return (
item.offset && {
position: 'relative',
@@ -69,6 +257,8 @@ export const MessageList = defineComponent({
const getProps = (index: number, item: MessageOptions) => {
return {
...item,
+ // 禁用Message组件内部的duration定时器,由MessageList统一管理
+ duration: 0,
onCloseBtnClick: (e: any) => {
if (item.onCloseBtnClick) {
item.onCloseBtnClick(e);
@@ -90,7 +280,7 @@ export const MessageList = defineComponent({
}
};
- expose({ add, removeAll, list, messageList });
+ expose({ add, removeAll, clearByKey, configMerge, list, messageList });
return () => {
if (!list.value.length) return;
diff --git a/packages/components/message/message.en-US.md b/packages/components/message/message.en-US.md
index 3d7ee64b84..68df8a98a6 100644
--- a/packages/components/message/message.en-US.md
+++ b/packages/components/message/message.en-US.md
@@ -29,6 +29,11 @@ name | type | default | description | required
-- | -- | -- | -- | --
attach | String / Function | 'body' | Typescript:`AttachNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue-next/blob/develop/packages/components/common.ts) | N
className | String | - | HTMLElement class | N
+mergeIdentical | Boolean | false | Whether to enable merging of identical messages | N
+mergeKey | String | - | Custom merge identifier for distinguishing different types of messages. If not set, `${theme}-${content}` will be used as the merge identifier | N
+mergeWindow | Number | 500 | Merge time window in milliseconds. Messages with the same identifier within this time will be merged | N
+showMergeCount | Boolean | true | Whether to show merge count | N
+mergeCountFormat | String | '(×{count})' | Format for displaying merge count, {count} will be replaced with the actual merge count | N
offset | Array | - | Typescript:`Array` | N
placement | String | top | options: center/top/left/right/bottom/top-left/top-right/bottom-left/bottom-right。Typescript:`MessagePlacementList` `type MessagePlacementList = 'center' \| 'top' \| 'left' \| 'right' \| 'bottom' \| 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'`。[see more ts definition](https://github.com/Tencent/tdesign-vue-next/blob/develop/packages/components/message/type.ts) | N
style | Object | - | CSS style。Typescript:`CSSProperties` | N
@@ -122,6 +127,22 @@ name | params | default | description
-- | -- | -- | --
\- | \- | - | \-
+### MessagePlugin.clearByKey
+
+Also supports `this.$message.clearByKey`. Clear specific type of messages by merge key.
+
+name | params | default | description
+-- | -- | -- | --
+mergeKey | String | - | required. The merge key of messages to be cleared
+
+### MessagePlugin.configMerge
+
+Also supports `this.$message.configMerge`. Configure global message merge options.
+
+name | params | default | description
+-- | -- | -- | --
+config | Object | - | required. Global merge configuration options. Typescript:`MessageMergeConfig`
+
### MessagePlugin.config
同时也支持 `this.$message.config`。
diff --git a/packages/components/message/message.md b/packages/components/message/message.md
index d7a08d2c38..f8bf56e380 100644
--- a/packages/components/message/message.md
+++ b/packages/components/message/message.md
@@ -30,6 +30,12 @@
{{ plugin }}
+### 消息合并功能
+
+当多个相同内容的消息同时出现时,可以启用合并功能来避免重复显示,提升用户体验。
+
+{{ merge }}
+
## API
### Message Props
@@ -58,6 +64,11 @@ duration-end | \- | 计时结束后触发
-- | -- | -- | -- | --
attach | String / Function | 'body' | 指定弹框挂载的父节点。数据类型为 String 时,会被当作选择器处理,进行节点查询。示例:'body' 或 () => document.body。TS 类型:`AttachNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/packages/components/common.ts) | N
className | String | - | 类名 | N
+mergeIdentical | Boolean | false | 是否启用相同内容消息合并功能 | N
+mergeKey | String | - | 自定义合并标识,用于区分不同类型的消息。如果不设置,则使用 `${theme}-${content}` 作为合并标识 | N
+mergeWindow | Number | 500 | 合并时间窗口,单位毫秒。在此时间内的相同消息将被合并 | N
+showMergeCount | Boolean | true | 是否显示合并计数 | N
+mergeCountFormat | String | '(×{count})' | 合并计数显示格式,{count} 会被替换为实际的合并次数 | N
offset | Array | - | 相对于 placement 的偏移量,示例:[-10, 20] 或 ['10em', '8rem']。TS 类型:`Array` | N
placement | String | top | 弹出消息位置。可选项:center/top/left/right/bottom/top-left/top-right/bottom-left/bottom-right。TS 类型:`MessagePlacementList` `type MessagePlacementList = 'center' \| 'top' \| 'left' \| 'right' \| 'bottom' \| 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right'`。[详细类型定义](https://github.com/Tencent/tdesign-vue-next/blob/develop/packages/components/message/type.ts) | N
style | Object | - | 内敛样式。TS 类型:`CSSProperties` | N
@@ -153,6 +164,22 @@ options | Object | - | 必需。该插件参数为 $Message.info() 等插件执
-- | -- | -- | --
\- | \- | - | \-
+### MessagePlugin.clearByKey
+
+同时也支持 `this.$message.clearByKey`。根据合并标识清除特定类型的消息。
+
+参数名称 | 参数类型 | 参数默认值 | 参数说明
+-- | -- | -- | --
+mergeKey | String | - | 必需。要清除的消息的合并标识
+
+### MessagePlugin.configMerge
+
+同时也支持 `this.$message.configMerge`。配置全局消息合并选项。
+
+参数名称 | 参数类型 | 参数默认值 | 参数说明
+-- | -- | -- | --
+config | Object | - | 必需。全局合并配置选项。TS 类型:`MessageMergeConfig`
+
### MessagePlugin.config
同时也支持 `this.$message.config`。
diff --git a/packages/components/message/plugin.tsx b/packages/components/message/plugin.tsx
index 39e8d14849..a1951fd798 100644
--- a/packages/components/message/plugin.tsx
+++ b/packages/components/message/plugin.tsx
@@ -38,6 +38,9 @@ import {
MessageQuestionMethod,
MessageCloseMethod,
MessageCloseAllMethod,
+ MessageClearByKeyMethod,
+ MessageConfigMergeMethod,
+ MessageMergeConfig,
} from './type';
import { AttachNodeReturnValue } from '../common';
import { isObject, isString } from 'lodash-es';
@@ -118,6 +121,8 @@ interface ExtraApi {
loading: MessageLoadingMethod;
close: MessageCloseMethod;
closeAll: MessageCloseAllMethod;
+ clearByKey: MessageClearByKeyMethod;
+ configMerge: MessageConfigMergeMethod;
}
export type MessagePluginType = Plugin & ExtraApi & MessageMethod;
@@ -142,6 +147,30 @@ const extraApi: ExtraApi = {
});
}
},
+ clearByKey: (mergeKey: string) => {
+ if (instanceMap instanceof Map) {
+ instanceMap.forEach((attach) => {
+ Object.keys(attach).forEach((placement) => {
+ const instance = attach[placement];
+ if (instance.component.exposed.clearByKey) {
+ instance.component.exposed.clearByKey(mergeKey);
+ }
+ });
+ });
+ }
+ },
+ configMerge: (config: MessageMergeConfig) => {
+ if (instanceMap instanceof Map) {
+ instanceMap.forEach((attach) => {
+ Object.keys(attach).forEach((placement) => {
+ const instance = attach[placement];
+ if (instance.component.exposed.configMerge) {
+ instance.component.exposed.configMerge(config);
+ }
+ });
+ });
+ }
+ },
};
export const MessagePlugin = showThemeMessage as MessagePluginType & {
diff --git a/packages/components/message/type.ts b/packages/components/message/type.ts
index 90ddc34510..4cb0eb4f9d 100644
--- a/packages/components/message/type.ts
+++ b/packages/components/message/type.ts
@@ -73,6 +73,30 @@ export interface MessageOptions extends TdMessageProps {
* @default 5000
*/
zIndex?: number;
+ /**
+ * 是否启用相同内容消息合并
+ * @default false
+ */
+ mergeIdentical?: boolean;
+ /**
+ * 自定义合并标识,用于区分不同类型的消息
+ */
+ mergeKey?: string;
+ /**
+ * 合并时间窗口,单位毫秒。在此时间内的相同消息将被合并
+ * @default 500
+ */
+ mergeWindow?: number;
+ /**
+ * 是否显示合并计数
+ * @default true
+ */
+ showMergeCount?: boolean;
+ /**
+ * 合并计数显示格式
+ * @default '(×{count})'
+ */
+ mergeCountFormat?: string;
}
export type MessageThemeList = 'info' | 'success' | 'warning' | 'error' | 'question' | 'loading';
@@ -142,3 +166,54 @@ export type MessageCloseMethod = (options: Promise) => void;
export type MessageCloseAllMethod = () => void;
export type MessageConfigMethod = (message: MessageOptions) => void;
+
+/**
+ * 消息合并配置接口
+ */
+export interface MessageMergeConfig {
+ /**
+ * 是否启用相同内容消息合并
+ * @default false
+ */
+ mergeIdentical?: boolean;
+ /**
+ * 合并时间窗口,单位毫秒。在此时间内的相同消息将被合并
+ * @default 500
+ */
+ mergeWindow?: number;
+ /**
+ * 最大合并次数
+ * @default 99
+ */
+ maxMergeCount?: number;
+ /**
+ * 是否显示合并计数
+ * @default true
+ */
+ showMergeCount?: boolean;
+ /**
+ * 合并计数显示格式
+ * @default '(×{count})'
+ */
+ mergeCountFormat?: string;
+}
+
+/**
+ * 消息项内部接口,包含合并相关属性
+ */
+export interface MessageItemInternal extends MessageOptions {
+ key: number;
+ mergeCount?: number;
+ mergeTimer?: number;
+ originalContent?: string | TNode;
+}
+
+/**
+ * 根据合并标识清除消息的方法
+ */
+export type MessageClearByKeyMethod = (mergeKey: string) => void;
+
+/**
+ * 配置全局合并选项的方法
+ */
+export type MessageConfigMergeMethod = (config: MessageMergeConfig) => void;