Repository: Tencent/tdesign-mobile-vue
Pull request #907 (2023-06-27 08:03:03)

feat: remove discard api
<!--
首先，感谢你的贡献！😄
请阅读并遵循 [TDesign 贡献指南](https://github.com/Tencent/tdesign/blob/main/docs/contributing.md)，填写以下 pull request 的信息。
PR 在维护者审核通过后会合并，谢谢！
-->

### 🤔 这个 PR 的性质是？

- [ ] 日常 bug 修复
- [ ] 新特性提交
- [x] 文档改进
- [ ] 演示代码改进
- [ ] 组件样式/交互改进
- [ ] CI/CD 改进
- [ ] 重构
- [ ] 代码风格优化
- [ ] 测试用例
- [ ] 分支合并
- [ ] 其他

### 🔗 相关 Issue

<!--
1. 描述相关需求的来源，如相关的 issue 讨论链接。
-->

### 💡 需求背景和解决方案

<!--
1. 要解决的具体问题。
2. 列出最终的 API 实现和用法。
3. 涉及UI/交互变动需要有截图或 GIF。
-->

### 📝 更新日志

<!--
从用户角度描述具体变化，以及可能的 breaking change 和其他风险。
-->

- feat: 移除废弃 API

- [ ] 本条 PR 不需要纳入 Changelog

### ☑️ 请求合并前的自查清单

⚠️ 请自检并全部**勾选全部选项**。⚠️

- [x] 文档已补充或无须补充
- [x] 代码演示已提供或无须提供
- [x] TypeScript 定义已补充或无须补充
- [x] Changelog 已提供或无须提供


github-actions[bot]: **Size Change:** -172 B (0%) 

**Total Size:** 473 kB

| Filename | Size | Change |
| :--- | :---: | :---: |
| `./dist/tdesign.js` | 276 kB | -98 B (0%) |
| `./dist/tdesign.min.js` | 133 kB | -74 B (0%) |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size |
| :--- | :---: |
| `./dist/reset.css` | 672 B |
| `./dist/tdesign.css` | 36.4 kB |
| `./dist/tdesign.min.css` | 26.3 kB |

</details>


github-actions[bot]: <a href="https://preview-pr907-tdesign-mobile-vue.surge.sh"><img height="96" alt="完成" src="https://user-images.githubusercontent.com/15634204/150816437-9f5bb788-cd67-4cbc-9897-b82d74e9aa65.png" /></a>
<!-- [工作流地址](https://github.com/Tencent/tdesign-mobile-vue/actions/runs/5387353741) -->
<!-- AUTO_PREVIEW_HOOK -->
github-actions[bot]: **Size Change:** -167 B (0%) 

**Total Size:** 497 kB

| Filename | Size | Change |
| :--- | :---: | :---: |
| `./dist/tdesign.js` | 292 kB | -97 B (0%) |
| `./dist/tdesign.min.js` | 141 kB | -70 B (0%) |

<details><summary>ℹ️ <strong>View Unchanged</strong></summary>

| Filename | Size |
| :--- | :---: |
| `./dist/reset.css` | 672 B |
| `./dist/tdesign.css` | 37.4 kB |
| `./dist/tdesign.min.css` | 27.1 kB |

</details>



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2aee8a4..7ed6f251 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,25 @@ toc: false
 spline: explain
 ---
 
+## 🌈 1.0.0-rc `2023-06-27` 
+### ❗ Breaking Changes
+- `Divider`: 调整 DOM，移除 lineColor 废弃属性 @LeeJim ([#896](https://github.com/Tencent/tdesign-mobile-vue/pull/896))
+- `CheckTag`: 移除废弃属性 closable @LeeJim ([#899](https://github.com/Tencent/tdesign-mobile-vue/pull/899))
+
+### 🚀 Features
+- `Popover`: 新增 Popover 弹出气泡组件 @anlyyao ([#898](https://github.com/Tencent/tdesign-mobile-vue/pull/898))
+### 🐞 Bug Fixes
+- `PullDownRefresh`: 修复下拉刷新加载样式未居中问题 @ccccpj ([#888](https://github.com/Tencent/tdesign-mobile-vue/pull/888))
+- `Fab`: 改用 setup 语法糖 @LeeJim ([#897](https://github.com/Tencent/tdesign-mobile-vue/pull/897))
+- `Textarea`: 修复计数器错误 @anlyyao ([#902](https://github.com/Tencent/tdesign-mobile-vue/pull/902))
+- `Table`: 修复事件 cell-click 和 row-click 返回参数错误的问题 @LeeJim ([#899](https://github.com/Tencent/tdesign-mobile-vue/pull/899))
+- `Drawer`: 修复事件 close 返回参数错误的问题 @LeeJim ([#899](https://github.com/Tencent/tdesign-mobile-vue/pull/899))
+- `Image`: 修复事件 load 和 error 返回参数错误的问题 @LeeJim ([#899](https://github.com/Tencent/tdesign-mobile-vue/pull/899))
+- `Search`: 修复事件 action-click 返回参数错误的问题 @LeeJim ([#899](https://github.com/Tencent/tdesign-mobile-vue/pull/899))
+- `Upload`: 修复事件 select-change 返回参数错误的问题 @LeeJim ([#899](https://github.com/Tencent/tdesign-mobile-vue/pull/899))
+- `Search`: 修复 `action-click` 事件无效 @anlyyao ([#908](https://github.com/Tencent/tdesign-mobile-vue/pull/908))
+- `DateTimePicker`: 修复 `modelValue` 属性无效的问题 @anlyyao ([#903](https://github.com/Tencent/tdesign-mobile-vue/pull/903))
+
 ## 🌈 0.26.0 `2023-06-20` 
 ### ❗ Breaking Changes
 - `NavBar`: 调整布局方式 @LeeJim ([#871](https://github.com/Tencent/tdesign-mobile-vue/pull/871))
diff --git a/package.json b/package.json
index 820ca859..59009bbe 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "tdesign-mobile-vue",
   "purename": "tdesign",
-  "version": "0.26.0",
+  "version": "1.0.0-rc",
   "description": "tdesign-mobile-vue",
   "author": "TDesign Group",
   "homepage": "https://tdesign.tencent.com/mobile-vue/",
diff --git a/src/_common b/src/_common
index c4dc2ec8..6c1dabc0 160000
--- a/src/_common
+++ b/src/_common
@@ -1,1 +1,1 @@
-Subproject commit c4dc2ec832a3df597a436e18fcfb0d2cd7149925
+Subproject commit 6c1dabc067c6902c8583581c0ab3ab4f6897632c
diff --git a/src/action-sheet/action-sheet-grid.vue b/src/action-sheet/action-sheet-grid.vue
index 33eb0403..a925717d 100644
--- a/src/action-sheet/action-sheet-grid.vue
+++ b/src/action-sheet/action-sheet-grid.vue
@@ -37,7 +37,6 @@
 <script lang="ts">
 import { ref, defineComponent, computed } from 'vue';
 import config from '../config';
-import { useEmitEvent } from '../shared';
 import { Grid as TGrid, GridItem as TGridItem } from '../grid';
 import { Swiper as TSwiper, SwiperItem as TSwiperItem } from '../swiper';
 
@@ -56,10 +55,13 @@ export default defineComponent({
       type: Number,
       default: 8,
     },
+    selected: {
+      type: Function,
+      default: undefined,
+    },
   },
   emits: ['selected'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const gridColumn = computed(() => Math.ceil(props.count / 2));
     const pageNum = computed(() => Math.ceil(props.items.length / props.count));
     const actionItems = computed(() => {
@@ -76,7 +78,7 @@ export default defineComponent({
       [`${name}__dots`]: pageNum.value > 1,
     }));
     const handleSelected = (i: any) => {
-      emitEvent('selected', i);
+      context.emit('selected', i);
     };
 
     return {
diff --git a/src/action-sheet/action-sheet.vue b/src/action-sheet/action-sheet.vue
index 04f64bab..cc42e03b 100644
--- a/src/action-sheet/action-sheet.vue
+++ b/src/action-sheet/action-sheet.vue
@@ -18,7 +18,7 @@
 
 <script lang="ts">
 import { ref, watch, defineComponent, computed } from 'vue';
-import { useEmitEvent, useDefault } from '../shared';
+import { useDefault } from '../shared';
 import ActionSheetList from './action-sheet-list.vue';
 import ActionSheetGrid from './action-sheet-grid.vue';
 import TPopup from '../popup';
@@ -41,7 +41,6 @@ export default defineComponent({
   props: ActionSheetProps,
   emits: ['selected', 'update:modelValue', 'cancel', 'close'],
   setup(props: any, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const actionItems = computed(() => {
       return props.items.map((item: String | ActionSheetItem) => {
         if (typeof item === 'string') {
@@ -70,7 +69,6 @@ export default defineComponent({
       () => currentVisible.value,
       (val) => {
         currentVisible.value = val;
-        // emitEvent('update:modelValue', val);
       },
       {
         immediate: true,
@@ -80,16 +78,16 @@ export default defineComponent({
 
     const hide = (trigger: string) => {
       context.emit('update:modelValue', false);
-      emitEvent('close', { trigger });
+      props.onClose?.({ trigger });
     };
 
     const handleCancel = () => {
-      emitEvent('cancel');
+      props.onCancel?.();
       context.emit('update:modelValue', false);
     };
 
     const handleSelected = (index: number) => {
-      emitEvent('selected', props?.items[index], index);
+      props.onSelected?.(props?.items[index], index);
       hide('selected');
     };
 
diff --git a/src/avatar/avatar.vue b/src/avatar/avatar.vue
index a96b5b34..86a2686f 100644
--- a/src/avatar/avatar.vue
+++ b/src/avatar/avatar.vue
@@ -29,7 +29,7 @@ import config from '../config';
 import AvatarProps from './props';
 import { TdAvatarGroupProps } from './type';
 import CLASSNAMES from '../shared/constants';
-import { renderContent, renderTNode, TNode, useEmitEvent } from '../shared';
+import { renderContent, renderTNode, TNode } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-avatar`;
@@ -39,8 +39,7 @@ export default defineComponent({
   components: { TNode, TBadge, TImage },
   props: AvatarProps,
   emits: ['error'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     const internalInstance = getCurrentInstance();
     const avatarGroupProps = inject('avatarGroup', {}) as TdAvatarGroupProps;
     const hasAvatarGroupProps = Object.keys(avatarGroupProps).length > 0;
@@ -69,7 +68,7 @@ export default defineComponent({
     });
 
     const handleImgLoadError = (e: any) => {
-      emitEvent('error', e);
+      props.onError?.(e);
     };
 
     const customBadgeProps = computed(() => ({
diff --git a/src/back-top/back-top.vue b/src/back-top/back-top.vue
index d78878a3..5eb2e644 100644
--- a/src/back-top/back-top.vue
+++ b/src/back-top/back-top.vue
@@ -10,7 +10,7 @@ import { computed, defineComponent, getCurrentInstance, h } from 'vue';
 import { useElementBounding } from '@vueuse/core';
 import { BacktopIcon as TIconBackTop } from 'tdesign-icons-vue-next';
 
-import { useEmitEvent, renderTNode, TNode } from '../shared';
+import { renderTNode, TNode } from '../shared';
 import BackTopProps from './props';
 import config from '../config';
 
@@ -21,7 +21,6 @@ export default defineComponent({
   components: { TNode },
   props: BackTopProps,
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const el = computed(() => {
       return props.target ? props.target() : window.document.documentElement;
     });
@@ -45,7 +44,7 @@ export default defineComponent({
 
     const clickBackBtn = () => {
       window.document.documentElement.scrollTop += top.value;
-      emitEvent('to-top');
+      props.onToTop?.();
     };
     return {
       name,
diff --git a/src/button/button.vue b/src/button/button.vue
index 92aac823..de2eb8ae 100644
--- a/src/button/button.vue
+++ b/src/button/button.vue
@@ -21,7 +21,7 @@
 import { computed, toRefs, defineComponent, getCurrentInstance } from 'vue';
 
 import Loading from '../loading';
-import { useEmitEvent, renderContent, renderTNode, TNode, Hover } from '../shared';
+import { renderContent, renderTNode, TNode, Hover } from '../shared';
 import ButtonProps from './props';
 import config from '../config';
 import { useFormDisabled } from '../form/hooks';
@@ -36,7 +36,6 @@ export default defineComponent({
   props: ButtonProps,
   emits: ['click'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const disabled = useFormDisabled();
     const internalInstance = getCurrentInstance();
     const buttonClass = computed(() => [
@@ -58,7 +57,7 @@ export default defineComponent({
 
     const onClick = (e: Event) => {
       if (!props.loading && !disabled.value) {
-        emitEvent('click', e);
+        props.onClick?.(e);
       } else {
         e.stopPropagation();
       }
diff --git a/src/cascader/cascader.vue b/src/cascader/cascader.vue
index ce7fcce7..953808c0 100644
--- a/src/cascader/cascader.vue
+++ b/src/cascader/cascader.vue
@@ -88,7 +88,7 @@ import { Tabs as TTabs, TabPanel as TTabPanel } from '../tabs';
 import { RadioGroup as TRadioGroup } from '../radio';
 import config from '../config';
 import TdCascaderProps from './props';
-import { useEmitEvent, useVModel, renderTNode, TNode } from '../shared';
+import { useVModel, renderTNode, TNode } from '../shared';
 import { TreeOptionData } from '../common';
 
 const { prefix } = config;
@@ -115,7 +115,6 @@ export default defineComponent({
   props: TdCascaderProps,
   emits: ['change', 'close', 'pick', 'update:modelValue', 'update:value', 'update:visible'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const { visible, value, modelValue, subTitles, options, keys } = toRefs(props);
     const open = ref(visible.value || false);
     const [cascaderValue, setCascaderValue] = useVModel(value, modelValue, props.defaultValue, props.onChange);
@@ -193,7 +192,7 @@ export default defineComponent({
         return;
       }
 
-      emitEvent('pick', { value: item[keys.value?.value ?? 'value'], index });
+      props.onPick?.({ value: item[keys.value?.value ?? 'value'], index });
 
       if (item[(keys as Ref<KeysType>).value?.children ?? 'children']?.length) {
         items[level + 1] = item[(keys as Ref<KeysType>).value?.children ?? 'children'];
@@ -203,8 +202,7 @@ export default defineComponent({
         steps.length = level + 2;
       } else {
         setCascaderValue(item[keys.value?.value ?? 'value']);
-        emitEvent(
-          'change',
+        props.onChange?.(
           item[keys.value?.value ?? 'value'],
           items.map((item, index) => toRaw(item?.[selectedIndexes[index]])),
         );
@@ -213,7 +211,7 @@ export default defineComponent({
     };
 
     watch(open, () => {
-      emitEvent('update:visible', open.value);
+      context.emit('update:visible', open.value);
     });
 
     watch(visible, () => {
@@ -221,7 +219,7 @@ export default defineComponent({
     });
 
     const close = (trigger: string) => {
-      emitEvent('close', { trigger });
+      props.onClose?.({ trigger });
     };
 
     const onVisibleChange = (visible: boolean) => {
@@ -260,7 +258,6 @@ export default defineComponent({
       selectedIndexes,
       items,
       setCascaderValue,
-      emitEvent,
       onClose,
     };
   },
diff --git a/src/cell/cell.vue b/src/cell/cell.vue
index 8c3e2a45..7821efa2 100644
--- a/src/cell/cell.vue
+++ b/src/cell/cell.vue
@@ -27,7 +27,7 @@
 <script lang="ts">
 import { computed, defineComponent, getCurrentInstance, toRefs, h } from 'vue';
 import { ChevronRightIcon } from 'tdesign-icons-vue-next';
-import { renderTNode, renderContent, TNode, useEmitEvent, Hover } from '../shared';
+import { renderTNode, renderContent, TNode, Hover } from '../shared';
 import config from '../config';
 import CellProps from './props';
 import { useFormDisabled } from '../form/hooks';
@@ -41,8 +41,7 @@ export default defineComponent({
   directives: { Hover },
   props: CellProps,
   emits: ['click'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     const disabled = useFormDisabled();
     const internalInstance = getCurrentInstance();
     const noteContent = computed(() => renderContent(internalInstance, 'default', 'note'));
@@ -71,7 +70,7 @@ export default defineComponent({
 
     const onClick = (e: Event) => {
       if (!disabled.value) {
-        emitEvent('click', e);
+        props.onClick?.(e);
       }
     };
 
diff --git a/src/date-time-picker/date-time-picker.vue b/src/date-time-picker/date-time-picker.vue
index 42959239..f6aed103 100644
--- a/src/date-time-picker/date-time-picker.vue
+++ b/src/date-time-picker/date-time-picker.vue
@@ -17,13 +17,12 @@ import { ref, computed, defineComponent, toRefs, watch, nextTick } from 'vue';
 import dayjs, { Dayjs, UnitType } from 'dayjs';
 import weekday from 'dayjs/plugin/weekday';
 import customParseFormat from 'dayjs/plugin/customParseFormat';
-import isString from 'lodash/isString';
 import isArray from 'lodash/isArray';
 
 import config from '../config';
 import DateTimePickerProps from './props';
 import { getMeaningColumn } from './shared';
-import { useEmitEvent, useVModel } from '../shared';
+import { useVModel } from '../shared';
 import { Picker as TPicker } from '../picker';
 import { PickerColumn, PickerColumnItem, PickerValue, PickerContext } from '../picker/type';
 
@@ -47,11 +46,15 @@ export default defineComponent({
   components: { TPicker },
   props: DateTimePickerProps,
   emits: ['change', 'cancel', 'confirm', 'pick', 'update:modelValue', 'update:value'],
-  setup(props: any, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props: any) {
     const className = computed(() => [`${name}`]);
-    const { value, modelValue } = toRefs(props);
-    const [innerValue, setDateTimePickerValue] = useVModel(value, modelValue, props.defaultValue, props.onChange);
+    const { value } = toRefs(props);
+    const [innerValue, setDateTimePickerValue] = useVModel(
+      value,
+      ref(props.modelValue),
+      props.defaultValue,
+      props.onChange,
+    );
     const title = computed(() => {
       return props.title || '选择时间';
     });
@@ -170,12 +173,12 @@ export default defineComponent({
     });
 
     const onConfirm = () => {
-      emitEvent('confirm', dayjs(curDate.value).format(props.format));
+      props.onConfirm(dayjs(curDate.value).format(props.format));
       setDateTimePickerValue(dayjs(curDate.value).format(props.format));
     };
 
     const onCancel = (context: { e: MouseEvent }) => {
-      emitEvent('cancel', { e: context.e });
+      props.onCancel?.({ e: context.e });
     };
 
     const onPick = (value: Array<PickerValue>, context: PickerContext) => {
@@ -184,7 +187,7 @@ export default defineComponent({
       const val = curDate.value.set(type as UnitType, parseInt(columns.value[column][index]?.value, 10));
 
       curDate.value = rationalize(val);
-      emitEvent('pick', rationalize(val).format(props.format));
+      props.onPick?.(rationalize(val).format(props.format));
     };
 
     watch(innerValue, (val) => {
diff --git a/src/divider/divider.en-US.md b/src/divider/divider.en-US.md
new file mode 100644
index 00000000..c6165b78
--- /dev/null
+++ b/src/divider/divider.en-US.md
@@ -0,0 +1,13 @@
+:: BASE_DOC ::
+
+## API
+
+### Divider Props
+
+name | type | default | description | required
+-- | -- | -- | -- | --
+align | String | center | options：left/right/center | N
+content | String / Slot / Function | - | Typescript：`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+dashed | Boolean | false | \- | N
+default | String / Slot / Function | - | Typescript：`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+layout | String | horizontal | options：horizontal/vertical | N
diff --git a/src/divider/divider.md b/src/divider/divider.md
index d56f7b96..f86cd75a 100644
--- a/src/divider/divider.md
+++ b/src/divider/divider.md
@@ -6,7 +6,7 @@
 名称 | 类型 | 默认值 | 说明 | 必传
 -- | -- | -- | -- | --
 align | String | center | 文本位置（仅在水平分割线有效）。可选项：left/right/center | N
-content | String / Slot / Function | - | 子元素。TS 类型：`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+content | String / Slot / Function | - | 子元素。TS 类型：`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 dashed | Boolean | false | 是否虚线（仅在水平分割线有效） | N
-default | String / Slot / Function | - | 子元素，同 content。TS 类型：`string | TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+default | String / Slot / Function | - | 子元素，同 content。TS 类型：`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 layout | String | horizontal | 分隔线类型有两种：水平和垂直。可选项：horizontal/vertical | N
diff --git a/src/divider/divider.vue b/src/divider/divider.vue
index feac06db..0124517b 100644
--- a/src/divider/divider.vue
+++ b/src/divider/divider.vue
@@ -1,14 +1,12 @@
 <template>
-  <div role="separator" :class="{ [`${name}--vertical-center`]: layout === 'vertical' }">
-    <div :class="dividerClass" :style="`border-color:${lineColor}`">
-      <div :class="`${name}__content`">
-        <t-node :content="dividerContent" />
-      </div>
+  <div :class="rootClass" role="separator">
+    <div :class="`${name}__content`">
+      <t-node :content="dividerContent" />
     </div>
   </div>
 </template>
 <script lang="ts">
-import { computed, toRefs, defineComponent, getCurrentInstance } from 'vue';
+import { computed, defineProps, getCurrentInstance } from 'vue';
 import { renderContent, TNode } from '../shared';
 import DividerProps from './props';
 import config from '../config';
@@ -16,28 +14,21 @@ import config from '../config';
 const { prefix } = config;
 const name = `${prefix}-divider`;
 
-export default defineComponent({
+export default {
   name,
-  components: { TNode },
-  props: DividerProps,
-  setup(props) {
-    const internalInstance = getCurrentInstance();
-    const dividerContent = computed(() => renderContent(internalInstance, 'default', 'content'));
-    const dividerClass = computed(() => [
-      `${name}`,
-      `${name}--${props.layout}`,
-      `${name}--${props.align}`,
-      {
-        [`${name}--dashed`]: props.dashed,
-      },
-    ]);
+};
+</script>
 
-    return {
-      name,
-      ...toRefs(props),
-      dividerContent,
-      dividerClass,
-    };
+<script lang="ts" setup>
+const props = defineProps(DividerProps);
+const internalInstance = getCurrentInstance();
+const dividerContent = computed(() => renderContent(internalInstance, 'default', 'content'));
+const rootClass = computed(() => [
+  `${name}`,
+  `${name}--${props.layout}`,
+  `${name}--${props.align}`,
+  {
+    [`${name}--dashed`]: props.dashed,
   },
-});
+]);
 </script>
diff --git a/src/divider/props.ts b/src/divider/props.ts
index f0190b1b..d7e7afbc 100644
--- a/src/divider/props.ts
+++ b/src/divider/props.ts
@@ -36,9 +36,4 @@ export default {
       return ['horizontal', 'vertical'].includes(val);
     },
   },
-  /** 分隔线颜色 */
-  lineColor: {
-    type: String,
-    default: '',
-  },
 };
diff --git a/src/divider/type.ts b/src/divider/type.ts
index 1e0cef0d..ca82f78d 100644
--- a/src/divider/type.ts
+++ b/src/divider/type.ts
@@ -30,9 +30,4 @@ export interface TdDividerProps {
    * @default horizontal
    */
   layout?: 'horizontal' | 'vertical';
-  /**
-   * 分隔线颜色
-   * @default ''
-   */
-  lineColor?: string;
 }
diff --git a/src/drawer/drawer.vue b/src/drawer/drawer.vue
index 818275ab..78b01cfa 100644
--- a/src/drawer/drawer.vue
+++ b/src/drawer/drawer.vue
@@ -41,7 +41,7 @@
 import { ref, watch, toRefs, computed, defineComponent, getCurrentInstance } from 'vue';
 import TPopup from '../popup';
 import config from '../config';
-import { useEmitEvent, renderTNode, TNode } from '../shared';
+import { renderTNode, TNode } from '../shared';
 import DrawerProps from './props';
 import { DrawerItem } from './type';
 
@@ -55,7 +55,6 @@ export default defineComponent({
   emits: ['update:visible', 'itemClick', 'overlayClick'],
   setup(props, context) {
     const currentInstance = getCurrentInstance();
-    const emitEvent = useEmitEvent(props, context.emit);
     const { visible, items, placement, showOverlay } = toRefs(props);
     const open = ref(visible.value || false);
 
@@ -63,7 +62,7 @@ export default defineComponent({
     const footerNode = computed(() => renderTNode(currentInstance, 'footer'));
 
     watch(open, () => {
-      emitEvent('update:visible', open.value);
+      context.emit('update:visible', open.value);
     });
 
     watch(visible, () => {
@@ -71,17 +70,17 @@ export default defineComponent({
     });
 
     const onItemClick = (index: number, item: DrawerItem, context: { e: MouseEvent }) => {
-      emitEvent('itemClick', index, item, context);
+      props.onItemClick?.(index, item, context);
     };
 
     const onVisibleChange = (visible: boolean) => {
       if (showOverlay.value) {
-        emitEvent('overlayClick', { visible });
+        props.onOverlayClick?.({ visible });
       }
     };
 
     const onClose = () => {
-      emitEvent('close', { trigger: 'overlay' });
+      props.onClose?.('overlay');
     };
 
     return {
diff --git a/src/dropdown-menu/dropdown-item-props.ts b/src/dropdown-menu/dropdown-item-props.ts
index 73441bca..6dfb57d1 100644
--- a/src/dropdown-menu/dropdown-item-props.ts
+++ b/src/dropdown-menu/dropdown-item-props.ts
@@ -31,11 +31,6 @@ export default {
     type: [String, Number] as PropType<TdDropdownItemProps['optionsColumns']>,
     default: 1,
   },
-  /** 已废弃。选项排列；不再支持 tree 布局，可与 treeSelect 配合使用 */
-  optionsLayout: {
-    type: String,
-    default: 'columns',
-  },
   /** 选中值 */
   value: {
     type: [String, Number] as PropType<TdDropdownItemProps['value']>,
diff --git a/src/dropdown-menu/dropdown-item.vue b/src/dropdown-menu/dropdown-item.vue
index 3c149eed..8be0d23b 100644
--- a/src/dropdown-menu/dropdown-item.vue
+++ b/src/dropdown-menu/dropdown-item.vue
@@ -79,10 +79,10 @@ import config from '../config';
 import TButton from '../button';
 import TPopup from '../popup';
 import TCheckbox, { CheckboxGroup as TCheckboxGroup } from '../checkbox';
-import { useVModel, useEmitEvent, uniqueFactory } from '../shared';
+import { useVModel, uniqueFactory } from '../shared';
 import DropdownItemProps from './dropdown-item-props';
 import { DropdownMenuState, DropdownMenuControl } from './context';
-import { TdDropdownMenuProps, DropdownOption, DropdownValue } from './type';
+import { TdDropdownMenuProps, DropdownValue } from './type';
 
 const { prefix } = config;
 const name = `${prefix}-dropdown-item`;
@@ -93,8 +93,7 @@ export default defineComponent({
   components: { TRadio, TButton, TPopup, TCheckbox, TRadioGroup, TCheckboxGroup },
   props: DropdownItemProps,
   emits: ['change', 'open', 'opened', 'close', 'closed', 'update:value', 'update:modelValue'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     // 受控 value 属性
     const { value, modelValue } = toRefs(props);
     const [passInValue, setValue] = useVModel(value, modelValue, props.defaultValue);
@@ -158,7 +157,7 @@ export default defineComponent({
         top: `${bottom}px`,
       };
       const { duration } = menuProps;
-      emitEvent(val ? 'open' : 'close');
+      props[`on${val ? 'Open' : 'Close'}`]?.();
       // 动画状态控制
       if (val) {
         state.wrapperVisible = true;
@@ -172,7 +171,7 @@ export default defineComponent({
         }, Number(duration));
       }
       setTimeout(() => {
-        emitEvent(val ? 'opened' : 'closed');
+        props[`on${val ? 'Opened' : 'Closed'}`]?.();
       }, Number(duration));
     };
 
diff --git a/src/dropdown-menu/dropdown-menu.en-US.md b/src/dropdown-menu/dropdown-menu.en-US.md
index ed751ef1..ffab0e3c 100644
--- a/src/dropdown-menu/dropdown-menu.en-US.md
+++ b/src/dropdown-menu/dropdown-menu.en-US.md
@@ -20,7 +20,6 @@ label | String | - | \- | N
 multiple | Boolean | false | \- | N
 options | Array | [] | Typescript：`Array<DropdownOption>` `interface DropdownOption { label: string; disabled: boolean; value: DropdownValue; }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dropdown-menu/type.ts) | N
 optionsColumns | String / Number | 1 | \- | N
-optionsLayout | String | columns | `deprecated` | N
 value | String / Number | undefined | `v-model` and `v-model:value` is supported。Typescript：`DropdownValue ` `type DropdownValue = string \| number;`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dropdown-menu/type.ts) | N
 defaultValue | String / Number | undefined | uncontrolled property。Typescript：`DropdownValue ` `type DropdownValue = string \| number;`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dropdown-menu/type.ts) | N
 onChange | Function |  | Typescript：`(value: DropdownValue) => void`<br/> | N
diff --git a/src/dropdown-menu/dropdown-menu.md b/src/dropdown-menu/dropdown-menu.md
index 9e380640..94742228 100644
--- a/src/dropdown-menu/dropdown-menu.md
+++ b/src/dropdown-menu/dropdown-menu.md
@@ -20,7 +20,6 @@ label | String | - | 标题 | N
 multiple | Boolean | false | 是否多选 | N
 options | Array | [] | 选项数据。TS 类型：`Array<DropdownOption>` `interface DropdownOption { label: string; disabled: boolean; value: DropdownValue; }`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dropdown-menu/type.ts) | N
 optionsColumns | String / Number | 1 | 选项分栏（1-3） | N
-optionsLayout | String | columns | 已废弃。选项排列；不再支持 tree 布局，可与 treeSelect 配合使用 | N
 value | String / Number | undefined | 选中值。支持语法糖 `v-model` 或 `v-model:value`。TS 类型：`DropdownValue ` `type DropdownValue = string \| number;`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dropdown-menu/type.ts) | N
 defaultValue | String / Number | undefined | 选中值。非受控属性。TS 类型：`DropdownValue ` `type DropdownValue = string \| number;`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/dropdown-menu/type.ts) | N
 onChange | Function |  | TS 类型：`(value: DropdownValue) => void`<br/>值改变时触发 | N
diff --git a/src/dropdown-menu/dropdown-menu.vue b/src/dropdown-menu/dropdown-menu.vue
index 907f0c6e..aec95ea3 100644
--- a/src/dropdown-menu/dropdown-menu.vue
+++ b/src/dropdown-menu/dropdown-menu.vue
@@ -13,6 +13,8 @@
 <script lang="ts">
 import { defineComponent, computed, toRefs, ref, reactive, watch, provide } from 'vue';
 import { CaretDownSmallIcon } from 'tdesign-icons-vue-next';
+import camelCase from 'lodash/camelCase';
+
 import config from '../config';
 import {
   context as menuContext,
@@ -21,7 +23,7 @@ import {
   DropdownMenuExpandState,
   TriggerSource,
 } from './context';
-import { useEmitEvent, useExpose } from '../shared';
+import { useExpose } from '../shared';
 import { findRelativeRect, findRelativeContainer } from './dom-utils';
 import DropdownMenuProps from './props';
 
@@ -33,8 +35,7 @@ export default defineComponent({
   components: { CaretDownSmallIcon },
   props: DropdownMenuProps,
   emits: ['menuOpened', 'menuClosed'],
-  setup(props, { slots, emit }) {
-    const emitEvent = useEmitEvent(props, emit);
+  setup(props, { slots }) {
     // 菜单状态
     const state = reactive<DropdownMenuState>({
       activeId: null,
@@ -97,10 +98,6 @@ export default defineComponent({
       },
     ]);
 
-    const emitEvents = (emit: string, trigger?: TriggerSource) => {
-      emitEvent(emit, { trigger });
-    };
-
     // 展开对应项目的菜单
     const expandMenu = (item: any, idx: number) => {
       const { disabled } = item;
@@ -110,10 +107,10 @@ export default defineComponent({
       if (state.activeId === idx) {
         // 再次点击时收起
         collapseMenu();
-        emitEvents('menuClosed', 'menu');
+        props.onMenuClosed?.({ trigger: 'menu' });
         return;
       }
-      emitEvents('menuOpened');
+      props.onMenuOpened?.('menuOpened');
       state.activeId = idx;
 
       // 获取菜单定位
@@ -138,7 +135,13 @@ export default defineComponent({
       const container = findRelativeContainer(bar) || document.body;
       menuContext.recordMenuExpanded(container, control, DropdownMenuExpandState.collapsed);
     };
-    const control: DropdownMenuControl = { expandMenu, collapseMenu, emitEvents };
+    const control: DropdownMenuControl = {
+      expandMenu,
+      collapseMenu,
+      emitEvents(emit: string, trigger?: TriggerSource) {
+        props[`on${camelCase(emit)}`]?.(trigger);
+      },
+    };
     // 提供子组件访问
     provide('dropdownMenuControl', control);
     useExpose({
diff --git a/src/dropdown-menu/type.ts b/src/dropdown-menu/type.ts
index ff904768..97d019df 100644
--- a/src/dropdown-menu/type.ts
+++ b/src/dropdown-menu/type.ts
@@ -59,12 +59,6 @@ export interface TdDropdownItemProps {
    * @default 1
    */
   optionsColumns?: string | number;
-  /**
-   * 选项排列；不再支持 tree 布局，可与 treeSelect 配合使用
-   * @default columns
-   * @deprecated
-   */
-  optionsLayout?: string;
   /**
    * 选中值
    */
diff --git a/src/fab/fab.vue b/src/fab/fab.vue
index cb69a439..66df0f34 100644
--- a/src/fab/fab.vue
+++ b/src/fab/fab.vue
@@ -16,7 +16,7 @@
 <script lang="ts">
 import { computed, defineProps, defineEmits, getCurrentInstance } from 'vue';
 
-import { renderTNode, useEmitEvent } from '../shared';
+import { renderTNode } from '../shared';
 import fabProps from './props';
 import config from '../config';
 import TButton from '../button';
@@ -35,11 +35,10 @@ const emit = defineEmits(['click']);
 
 const internalInstance = getCurrentInstance();
 const iconTNode = computed(() => renderTNode(internalInstance, 'icon'));
-const emitEvent = useEmitEvent(props, emit);
 
 const classes = computed(() => ({
   [`${name}`]: true,
 }));
 
-const onClick = (e: MouseEvent) => emitEvent('click', { e });
+const onClick = (e: MouseEvent) => props.onClick?.({ e });
 </script>
diff --git a/src/image-viewer/image-viewer.vue b/src/image-viewer/image-viewer.vue
index 2b889956..f26e7f8b 100644
--- a/src/image-viewer/image-viewer.vue
+++ b/src/image-viewer/image-viewer.vue
@@ -41,7 +41,7 @@ import { CloseIcon, DeleteIcon } from 'tdesign-icons-vue-next';
 
 import config from '../config';
 import ImagediverProps from './props';
-import { renderTNode, TNode, useEmitEvent, useDefault, useTouch } from '../shared';
+import { renderTNode, TNode, useDefault, useTouch } from '../shared';
 import { TdImageViewerProps } from './type';
 
 // inner components
@@ -64,19 +64,18 @@ export default defineComponent({
     TImage,
   },
   props: ImagediverProps,
-  emits: ['close', 'index-change', 'update:visible', 'update:modelValue', 'change'],
-  setup(props, context) {
+  emits: ['close', 'index-change', 'update:visible', 'update:modelValue', 'delete'],
+  setup(props, { emit }) {
     const internalInstance = getCurrentInstance();
     const state = reactive({
       zooming: false,
       scale: 1,
       swiperStyle: [] as string[],
     });
-    const emitEvent = useEmitEvent(props, context.emit);
-    const [visible, setVisible] = useDefault(props, context.emit, 'visible', 'change');
+    const [visible, setVisible] = useDefault(props, emit, 'visible', 'change');
     const [currentIndex, setIndex] = useDefault<TdImageViewerProps['index'], TdImageViewerProps>(
       props,
-      context.emit,
+      emit,
       'index',
       'index-change',
     );
@@ -109,11 +108,11 @@ export default defineComponent({
 
     const handleClose = (e: Event, trigger: string) => {
       setVisible(false);
-      emitEvent('close', { trigger, e });
+      emit('close', { trigger, e });
     };
 
     const handleDelete = () => {
-      emitEvent('delete', currentIndex);
+      emit('delete', currentIndex.value ?? 0);
     };
 
     const onSwiperChange = (index: number, context: any) => {
diff --git a/src/image/image.vue b/src/image/image.vue
index d2a8e26d..a22e2e2a 100644
--- a/src/image/image.vue
+++ b/src/image/image.vue
@@ -26,7 +26,7 @@ import { useIntersectionObserver } from '@vueuse/core';
 import { CloseIcon } from 'tdesign-icons-vue-next';
 
 import Loading from '../loading';
-import { useEmitEvent, renderTNode, TNode } from '../shared';
+import { renderTNode, TNode } from '../shared';
 import ImageProps from './props';
 import config from '../config';
 
@@ -38,8 +38,6 @@ export default defineComponent({
   components: { TNode },
   props: ImageProps,
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
-
     // 默认loading和error状态展示，slot支持Node和Function
     const internalInstance = getCurrentInstance();
 
@@ -93,7 +91,7 @@ export default defineComponent({
 
     // 图片加载完成回调
     const handleImgLoadCompleted = (e: Event) => {
-      emitEvent('load', e);
+      props.onLoad?.({ e });
       isLoading.value = false;
     };
 
@@ -102,7 +100,7 @@ export default defineComponent({
       if (realSrc.value === '') {
         return;
       }
-      emitEvent('error', e);
+      props.onError?.({ e });
       isLoading.value = false;
       isError.value = true;
     };
diff --git a/src/indexes/indexes.vue b/src/indexes/indexes.vue
index d3568c3e..992be622 100644
--- a/src/indexes/indexes.vue
+++ b/src/indexes/indexes.vue
@@ -39,7 +39,6 @@ import {
 import _ from 'lodash';
 import config from '../config';
 import IndexesProps from './props';
-import { useEmitEvent } from '../shared';
 
 const { prefix } = config;
 
@@ -66,8 +65,7 @@ export default defineComponent({
   name: componentName,
   props: IndexesProps,
   emits: ['select', 'change'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     let timeOut: number;
     const indexesRoot = ref<HTMLElement>();
     const parentRect = ref();
@@ -148,7 +146,7 @@ export default defineComponent({
     };
 
     const handleSidebarItemClick = (index: string | number) => {
-      emitEvent('select', index);
+      props.onSelect?.(index);
       setActiveSidebarAndTip(index);
       scrollToByIndex(index);
     };
@@ -214,7 +212,7 @@ export default defineComponent({
       () => state.activeSidebar,
       (val, oldVal) => {
         if (val !== oldVal) {
-          emitEvent('change', state.activeSidebar);
+          props.onChange?.(state.activeSidebar);
         }
       },
     );
diff --git a/src/input/input.en-US.md b/src/input/input.en-US.md
index 1d4cf818..20b6fdac 100644
--- a/src/input/input.en-US.md
+++ b/src/input/input.en-US.md
@@ -10,13 +10,12 @@ align | String | left | options：left/center/right | N
 allowInputOverMax | Boolean | false | \- | N
 autocomplete | String | undefined | \- | N
 autofocus | Boolean | false | \- | N
+borderless | Boolean | false | \- | N
 clearable | Boolean | false | \- | N
 disabled | Boolean | - | \- | N
-errorMessage | String | - | `deprecated` | N
 format | Function | - | Typescript：`InputFormatType` `type InputFormatType = (value: InputValue) => string`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/input/type.ts) | N
 label | String / Slot / Function | - | Typescript：`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 layout | String | horizontal | options：vertical/horizontal | N
-borderless | Boolean | false | \- | N
 maxcharacter | Number | - | \- | N
 maxlength | Number | - | \- | N
 name | String | - | \- | N
@@ -35,6 +34,7 @@ onBlur | Function |  | Typescript：`(value: InputValue, context: { e: FocusEven
 onChange | Function |  | Typescript：`(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' }) => void`<br/> | N
 onClear | Function |  | Typescript：`(context: { e: MouseEvent }) => void`<br/> | N
 onFocus | Function |  | Typescript：`(value: InputValue, context: { e: FocusEvent }) => void`<br/> | N
+onValidate | Function |  | Typescript：`(context: { error?: 'exceed-maximum' \| 'below-minimum' }) => void`<br/>trigger on text length being over max length or max character | N
 
 ### Input Events
 
@@ -44,3 +44,4 @@ blur | `(value: InputValue, context: { e: FocusEvent })` | \-
 change | `(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' })` | \-
 clear | `(context: { e: MouseEvent })` | \-
 focus | `(value: InputValue, context: { e: FocusEvent })` | \-
+validate | `(context: { error?: 'exceed-maximum' \| 'below-minimum' })` | trigger on text length being over max length or max character
diff --git a/src/input/input.md b/src/input/input.md
index beaf52f6..d977e022 100644
--- a/src/input/input.md
+++ b/src/input/input.md
@@ -9,13 +9,12 @@ align | String | left | 文本内容位置，居左/居中/居右。可选项：
 allowInputOverMax | Boolean | false | 超出 `maxlength` 或 `maxcharacter` 之后是否允许继续输入 | N
 autocomplete | String | undefined | 是否开启自动填充功能，HTML5 原生属性，[点击查看详情](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) | N
 autofocus | Boolean | false | 自动聚焦 | N
+borderless | Boolean | false | 是否开启无边框模式 | N
 clearable | Boolean | false | 是否可清空 | N
 disabled | Boolean | - | 是否禁用输入框 | N
-errorMessage | String | - | 已废弃。错误提示文本，值为空不显示（废弃属性，如果需要，请更为使用 status 和 tips） | N
 format | Function | - | 指定输入框展示值的格式。TS 类型：`InputFormatType` `type InputFormatType = (value: InputValue) => string`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/input/type.ts) | N
 label | String / Slot / Function | - | 左侧文本。TS 类型：`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 layout | String | horizontal | 标题输入框布局方式。可选项：vertical/horizontal | N
-borderless | Boolean | false | 是否开启无边框模式 | N
 maxcharacter | Number | - | 用户最多可以输入的字符个数，一个中文汉字表示两个字符长度。`maxcharacter` 和 `maxlength` 二选一使用 | N
 maxlength | Number | - | 用户最多可以输入的文本长度，一个中文等于一个计数长度。值为空，则表示不限制输入长度。`maxcharacter` 和 `maxlength` 二选一使用 | N
 name | String | - | 名称 | N
@@ -34,6 +33,7 @@ onBlur | Function |  | TS 类型：`(value: InputValue, context: { e: FocusEvent
 onChange | Function |  | TS 类型：`(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' }) => void`<br/>输入框值发生变化时触发。`trigger=initial` 表示传入的数据不符合预期，组件自动处理后触发 change 告知父组件。如：初始值长度超过 `maxlength` 限制 | N
 onClear | Function |  | TS 类型：`(context: { e: MouseEvent }) => void`<br/>清空按钮点击时触发 | N
 onFocus | Function |  | TS 类型：`(value: InputValue, context: { e: FocusEvent }) => void`<br/>获得焦点时触发 | N
+onValidate | Function |  | TS 类型：`(context: { error?: 'exceed-maximum' \| 'below-minimum' }) => void`<br/>字数超出限制时触发 | N
 
 ### Input Events
 
@@ -43,3 +43,4 @@ blur | `(value: InputValue, context: { e: FocusEvent })` | 失去焦点时触发
 change | `(value: InputValue, context?: { e?: InputEvent \| MouseEvent \| CompositionEvent; trigger: 'input' \| 'initial' \| 'clear' })` | 输入框值发生变化时触发。`trigger=initial` 表示传入的数据不符合预期，组件自动处理后触发 change 告知父组件。如：初始值长度超过 `maxlength` 限制
 clear | `(context: { e: MouseEvent })` | 清空按钮点击时触发
 focus | `(value: InputValue, context: { e: FocusEvent })` | 获得焦点时触发
+validate | `(context: { error?: 'exceed-maximum' \| 'below-minimum' })` | 字数超出限制时触发
diff --git a/src/input/input.vue b/src/input/input.vue
index 14fcb750..bc69a983 100644
--- a/src/input/input.vue
+++ b/src/input/input.vue
@@ -56,7 +56,7 @@ import { useFocus } from '@vueuse/core';
 import config from '../config';
 import InputProps from './props';
 import { InputValue, TdInputProps } from './type';
-import { useEmitEvent, getCharacterLength, renderTNode, TNode, useDefault, extendAPI } from '../shared';
+import { getCharacterLength, renderTNode, TNode, useDefault, extendAPI } from '../shared';
 import { useFormDisabled } from '../form/hooks';
 
 const { prefix } = config;
@@ -77,7 +77,6 @@ export default defineComponent({
   },
   emits: ['update:value', 'update:modelValue', 'click-icon', 'focus', 'blur', 'change', 'clear'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const disabled = useFormDisabled();
 
     const inputRef = ref();
@@ -161,13 +160,13 @@ export default defineComponent({
     const handleClear = (e: MouseEvent) => {
       innerValue.value = '';
       focused.value = true;
-      emitEvent('clear', { e });
+      props.onClear?.({ e });
     };
     const handleFocus = (e: FocusEvent) => {
-      emitEvent('focus', innerValue.value, { e });
+      props.onFocus?.(innerValue.value, { e });
     };
     const handleBlur = (e: FocusEvent) => {
-      emitEvent('blur', innerValue.value, { e });
+      props.onBlur?.(innerValue.value, { e });
     };
 
     const handleCompositionend = (e: InputEvent | CompositionEvent) => {
diff --git a/src/input/props.ts b/src/input/props.ts
index aeb65c5d..80375a15 100644
--- a/src/input/props.ts
+++ b/src/input/props.ts
@@ -26,15 +26,12 @@ export default {
   },
   /** 自动聚焦 */
   autofocus: Boolean,
+  /** 是否开启无边框模式 */
+  borderless: Boolean,
   /** 是否可清空 */
   clearable: Boolean,
   /** 是否禁用输入框 */
   disabled: Boolean,
-  /** 已废弃。错误提示文本，值为空不显示（废弃属性，如果需要，请更为使用 status 和 tips） */
-  errorMessage: {
-    type: String,
-    default: '',
-  },
   /** 指定输入框展示值的格式 */
   format: {
     type: Function as PropType<TdInputProps['format']>,
@@ -52,11 +49,6 @@ export default {
       return ['vertical', 'horizontal'].includes(val);
     },
   },
-  /** 是否开启无边框模式 */
-  borderless: {
-    type: Boolean,
-    value: false,
-  },
   /** 用户最多可以输入的字符个数，一个中文汉字表示两个字符长度。`maxcharacter` 和 `maxlength` 二选一使用 */
   maxcharacter: {
     type: Number,
diff --git a/src/input/type.ts b/src/input/type.ts
index d83ff548..e90f65c4 100644
--- a/src/input/type.ts
+++ b/src/input/type.ts
@@ -26,6 +26,11 @@ export interface TdInputProps {
    * @default false
    */
   autofocus?: boolean;
+  /**
+   * 是否开启无边框模式
+   * @default false
+   */
+  borderless?: boolean;
   /**
    * 是否可清空
    * @default false
@@ -35,12 +40,6 @@ export interface TdInputProps {
    * 是否禁用输入框
    */
   disabled?: boolean;
-  /**
-   * 错误提示文本，值为空不显示（废弃属性，如果需要，请更为使用 status 和 tips）
-   * @default ''
-   * @deprecated
-   */
-  errorMessage?: string;
   /**
    * 指定输入框展示值的格式
    */
@@ -54,11 +53,6 @@ export interface TdInputProps {
    * @default horizontal
    */
   layout?: 'vertical' | 'horizontal';
-  /**
-   * 是否开启无边框模式
-   * @default true
-   */
-  borderless?: boolean;
   /**
    * 用户最多可以输入的字符个数，一个中文汉字表示两个字符长度。`maxcharacter` 和 `maxlength` 二选一使用
    */
diff --git a/src/link/link.vue b/src/link/link.vue
index be8ce71b..74d2ac9b 100644
--- a/src/link/link.vue
+++ b/src/link/link.vue
@@ -22,7 +22,7 @@
 import { defineComponent, computed, getCurrentInstance } from 'vue';
 import config from '../config';
 import LinkProps from './props';
-import { useEmitEvent, renderContent, renderTNode, TNode } from '../shared';
+import { renderContent, renderTNode, TNode } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-link`;
@@ -33,8 +33,8 @@ export default defineComponent({
     TNode,
   },
   props: LinkProps,
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
+    const baseClass = name;
     const _this = getCurrentInstance();
     const linkContent = computed(() => renderContent(_this, 'default', 'content'));
     const prefixContent = computed(() => renderTNode(_this, 'prefixIcon'));
@@ -42,7 +42,7 @@ export default defineComponent({
 
     const handleClick = (e: MouseEvent) => {
       if (props.disabled) return;
-      emitEvent('click', e);
+      props.onClick?.(e);
     };
 
     const linkClass = computed(() => [
diff --git a/src/list/list.vue b/src/list/list.vue
index d2f9876b..33c712c6 100644
--- a/src/list/list.vue
+++ b/src/list/list.vue
@@ -20,7 +20,7 @@ import { useWindowSize, useEventListener } from '@vueuse/core';
 import TLoading from '../loading';
 import config from '../config';
 import ListProps from './props';
-import { renderTNode, TNode, useScrollParent, useEmitEvent } from '../shared';
+import { renderTNode, TNode, useScrollParent } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-list`;
@@ -38,7 +38,6 @@ export default defineComponent({
   props: ListProps,
   emits: ['load-more', 'scroll'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const root = ref<HTMLElement>();
     const empty = ref<HTMLElement>();
     const scrollParent = useScrollParent(root);
@@ -50,7 +49,7 @@ export default defineComponent({
 
     const onLoadMore = (e: MouseEvent) => {
       if (props.asyncLoading === 'load-more') {
-        emitEvent('load-more');
+        props.onLoadMore?.();
       }
     };
 
@@ -62,7 +61,7 @@ export default defineComponent({
         (e.target as HTMLElement).scrollTop || document.documentElement.scrollTop || document.body.scrollTop;
       const offsetHeight = (e.target as HTMLElement).offsetHeight || height.value;
 
-      emitEvent('scroll', scrollHeight - (scrollTop + offsetHeight), scrollTop);
+      props.onScroll?.(scrollHeight - (scrollTop + offsetHeight), scrollTop);
     };
 
     useEventListener(scrollParent, 'scroll', handleScroll);
diff --git a/src/message/message.en-US.md b/src/message/message.en-US.md
index fc3e3dc1..efcee543 100644
--- a/src/message/message.en-US.md
+++ b/src/message/message.en-US.md
@@ -17,24 +17,14 @@ theme | String | info | options：info/success/warning/error。Typescript：`Mes
 visible | Boolean | false | `v-model` and `v-model:visible` is supported | N
 defaultVisible | Boolean | false | uncontrolled property | N
 zIndex | Number | - | \- | N
-onChange | Function |  | Typescript：`(visible: boolean) => void`<br/>`deprecated` | N
-onClose | Function |  | Typescript：`(context: { trigger: 'close-click' \| 'duration-end', e?: MouseEvent }) => void`<br/>`deprecated`。close message event。Typescript：`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 onCloseBtnClick | Function |  | Typescript：`(context: { e: MouseEvent }) => void`<br/> | N
-onClosed | Function |  | Typescript：`() => void`<br/>`deprecated` | N
 onDurationEnd | Function |  | Typescript：`() => void`<br/> | N
-onLinkClick | Function |  | 【developing】Typescript：`(context: { e: MouseEvent }) => void`<br/> | N
-onOpen | Function |  | Typescript：`() => void`<br/>`deprecated` | N
-onOpened | Function |  | Typescript：`() => void`<br/>`deprecated` | N
+onLinkClick | Function |  | Typescript：`(context: { e: MouseEvent }) => void`<br/> | N
 
 ### Message Events
 
 name | params | description
 -- | -- | --
-change | `(visible: boolean)` | `deprecated`
-close | `(context: { trigger: 'close-click' \| 'duration-end', e?: MouseEvent })` | `deprecated`。close message event。Typescript：`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)
 close-btn-click | `(context: { e: MouseEvent })` | \-
-closed | \- | `deprecated`
 duration-end | \- | \-
-link-click | 【developing】`(context: { e: MouseEvent })` | \-
-open | \- | `deprecated`
-opened | \- | `deprecated`
+link-click | `(context: { e: MouseEvent })` | \-
diff --git a/src/message/message.md b/src/message/message.md
index a5f3c938..f4206113 100644
--- a/src/message/message.md
+++ b/src/message/message.md
@@ -17,24 +17,14 @@ theme | String | info | 消息组件风格。可选项：info/success/warning/er
 visible | Boolean | false | 是否显示，隐藏时默认销毁组件。支持语法糖 `v-model` 或 `v-model:visible` | N
 defaultVisible | Boolean | false | 是否显示，隐藏时默认销毁组件。非受控属性 | N
 zIndex | Number | - | 元素层级，样式默认为 5000 | N
-onChange | Function |  | TS 类型：`(visible: boolean) => void`<br/>已废弃。可见性变化时触发 | N
-onClose | Function |  | TS 类型：`(context: { trigger: 'close-click' \| 'duration-end', e?: MouseEvent }) => void`<br/>已废弃。关闭消息时触发。TS 类型：`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 onCloseBtnClick | Function |  | TS 类型：`(context: { e: MouseEvent }) => void`<br/>当关闭按钮存在时，用户点击关闭按钮触发 | N
-onClosed | Function |  | TS 类型：`() => void`<br/>已废弃。关闭消息并且动画结束后触发 | N
 onDurationEnd | Function |  | TS 类型：`() => void`<br/>计时结束后触发 | N
-onLinkClick | Function |  | 【开发中】TS 类型：`(context: { e: MouseEvent }) => void`<br/>当link链接存在时，点击链接文本时触发 | N
-onOpen | Function |  | TS 类型：`() => void`<br/>已废弃。展示Message时触发 | N
-onOpened | Function |  | TS 类型：`() => void`<br/>已废弃。展示Message时并且动画结束后触发 | N
+onLinkClick | Function |  | TS 类型：`(context: { e: MouseEvent }) => void`<br/>当link链接存在时，点击链接文本时触发 | N
 
 ### Message Events
 
 名称 | 参数 | 描述
 -- | -- | --
-change | `(visible: boolean)` | 已废弃。可见性变化时触发
-close | `(context: { trigger: 'close-click' \| 'duration-end', e?: MouseEvent })` | 已废弃。关闭消息时触发。TS 类型：`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)
 close-btn-click | `(context: { e: MouseEvent })` | 当关闭按钮存在时，用户点击关闭按钮触发
-closed | \- | 已废弃。关闭消息并且动画结束后触发
 duration-end | \- | 计时结束后触发
-link-click | `(context: { e: MouseEvent })` | 【开发中】当link链接存在时，点击链接文本时触发
-open | \- | 已废弃。展示Message时触发
-opened | \- | 已废弃。展示Message时并且动画结束后触发
+link-click | `(context: { e: MouseEvent })` | 当link链接存在时，点击链接文本时触发
diff --git a/src/message/message.vue b/src/message/message.vue
index 4e358ab6..89bd8f45 100644
--- a/src/message/message.vue
+++ b/src/message/message.vue
@@ -35,9 +35,9 @@ import {
 import { CheckCircleFilledIcon, CloseIcon, InfoCircleFilledIcon } from 'tdesign-icons-vue-next';
 import { isFunction } from 'lodash';
 import messageProps from './props';
-import { DrawMarquee } from './type';
+import { DrawMarquee, TdMessageProps } from './type';
 import config from '../config';
-import { renderContent, renderTNode, TNode, useEmitEvent, useVModel } from '../shared';
+import { renderContent, renderTNode, TNode, useVModel } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-message`;
@@ -53,8 +53,7 @@ export default defineComponent({
   components: { TNode },
   props: messageProps,
   emits: ['durationEnd', 'closeBtnClick'],
-  setup(props: any, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props, context) {
     const internalInstance = getCurrentInstance();
 
     // 初始化动画相关数据
@@ -84,13 +83,13 @@ export default defineComponent({
       [`${name}__text-nowrap`]: props.marquee,
     }));
 
-    const changeNumToStr = (arr: []) => {
+    const changeNumToStr = (arr: TdMessageProps['offset'] = []) => {
       return arr.map(function (item) {
         return typeof item === 'number' ? `${item}px` : item;
       });
     };
 
-    const getMessageStylesOffset = (offset: []) => {
+    const getMessageStylesOffset = (offset: TdMessageProps['offset']) => {
       const arr = changeNumToStr(offset);
       return {
         top: arr[0],
@@ -153,11 +152,11 @@ export default defineComponent({
       state.scroll = {
         marquee: true,
         // 负数统一当作循环播放
-        loop: Math.max(props.marquee?.loop, -1) || loop,
+        loop: Math.max(props.marquee === true || props.marquee?.loop == null ? loop : props.marquee?.loop, -1),
         // 速度必须为正数
-        speed: Math.max(props.marquee?.speed, 1) || speed,
+        speed: Math.max(props.marquee === true || props.marquee?.speed == null ? speed : props.marquee?.speed, 1),
         // 延迟不可为负数
-        delay: Math.max(props.marquee?.delay, 0) || delay,
+        delay: Math.max(props.marquee === true || props.marquee?.delay == null ? delay : props.marquee?.delay, 0),
       };
       state.offset = 0;
 
@@ -198,14 +197,14 @@ export default defineComponent({
     };
 
     const onCloseBtnClick = () => {
-      emitEvent('closeBtnClick');
+      props.onCloseBtnClick?.();
       setVisible(false);
     };
 
     const handleDuration = () => {
       if (props.duration > 0) {
         setTimeout(() => {
-          emitEvent('durationEnd');
+          props.onDurationEnd?.();
           onCloseBtnClick();
         }, props.duration);
       }
diff --git a/src/message/props.ts b/src/message/props.ts
index 2f7aad8d..09980610 100644
--- a/src/message/props.ts
+++ b/src/message/props.ts
@@ -73,20 +73,10 @@ export default {
   zIndex: {
     type: Number,
   },
-  /** 已废弃。可见性变化时触发 */
-  onChange: Function as PropType<TdMessageProps['onChange']>,
-  /** 已废弃。关闭消息时触发 */
-  onClose: Function as PropType<TdMessageProps['onClose']>,
   /** 当关闭按钮存在时，用户点击关闭按钮触发 */
   onCloseBtnClick: Function as PropType<TdMessageProps['onCloseBtnClick']>,
-  /** 已废弃。关闭消息并且动画结束后触发 */
-  onClosed: Function as PropType<TdMessageProps['onClosed']>,
   /** 计时结束后触发 */
   onDurationEnd: Function as PropType<TdMessageProps['onDurationEnd']>,
   /** 当link链接存在时，点击链接文本时触发 */
   onLinkClick: Function as PropType<TdMessageProps['onLinkClick']>,
-  /** 已废弃。展示Message时触发 */
-  onOpen: Function as PropType<TdMessageProps['onOpen']>,
-  /** 已废弃。展示Message时并且动画结束后触发 */
-  onOpened: Function as PropType<TdMessageProps['onOpened']>,
 };
diff --git a/src/message/type.ts b/src/message/type.ts
index 0369c900..43204084 100644
--- a/src/message/type.ts
+++ b/src/message/type.ts
@@ -67,25 +67,10 @@ export interface TdMessageProps {
    * 元素层级，样式默认为 5000
    */
   zIndex?: number;
-  /**
-   * 可见性变化时触发
-   * @deprecated
-   */
-  onChange?: (visible: boolean) => void;
-  /**
-   * 关闭消息时触发
-   * @deprecated
-   */
-  onClose?: (context: { trigger: 'close-click' | 'duration-end'; e?: MouseEvent }) => void;
   /**
    * 当关闭按钮存在时，用户点击关闭按钮触发
    */
   onCloseBtnClick?: (context: { e: MouseEvent }) => void;
-  /**
-   * 关闭消息并且动画结束后触发
-   * @deprecated
-   */
-  onClosed?: () => void;
   /**
    * 计时结束后触发
    */
@@ -94,16 +79,6 @@ export interface TdMessageProps {
    * 当link链接存在时，点击链接文本时触发
    */
   onLinkClick?: (context: { e: MouseEvent }) => void;
-  /**
-   * 展示Message时触发
-   * @deprecated
-   */
-  onOpen?: () => void;
-  /**
-   * 展示Message时并且动画结束后触发
-   * @deprecated
-   */
-  onOpened?: () => void;
 }
 
 export type MessageAlignType = 'left' | 'center';
diff --git a/src/navbar/navbar.vue b/src/navbar/navbar.vue
index 92e5673b..9e9189e4 100644
--- a/src/navbar/navbar.vue
+++ b/src/navbar/navbar.vue
@@ -24,7 +24,7 @@
 import { computed, defineComponent, getCurrentInstance, ref, toRefs } from 'vue';
 import { ChevronLeftIcon as TChevronLeftIcon } from 'tdesign-icons-vue-next';
 import config from '../config';
-import { renderTNode, TNode, useEmitEvent } from '../shared';
+import { renderTNode, TNode } from '../shared';
 import NavbarProps from './props';
 
 const { prefix } = config;
@@ -70,14 +70,12 @@ export default defineComponent({
     const rightContent = computed(() => renderTNode(internalInstance, 'right'));
     const capsuleContent = computed(() => renderTNode(internalInstance, 'capsule'));
 
-    const emitEvent = useEmitEvent(props, context.emit);
-
     const handleLeftClick = () => {
-      emitEvent('left-click');
+      props.onLeftClick?.();
     };
 
     const handleRightClick = () => {
-      emitEvent('right-click');
+      props.onRightClick?.();
     };
 
     return {
diff --git a/src/notice-bar/notice-bar.vue b/src/notice-bar/notice-bar.vue
index d80a025b..87d4c5ac 100644
--- a/src/notice-bar/notice-bar.vue
+++ b/src/notice-bar/notice-bar.vue
@@ -65,7 +65,7 @@ import { Swiper as TSwiper, SwiperItem as TSwiperItem } from '../swiper';
 import NoticeBarProps from './props';
 import { NoticeBarTrigger, DrawMarquee } from './type';
 import config from '../config';
-import { useEmitEvent, renderTNode, TNode, useVModel } from '../shared';
+import { renderTNode, TNode, useVModel } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-notice-bar`;
@@ -81,7 +81,6 @@ export default defineComponent({
   props: NoticeBarProps,
   emits: ['click'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const internalInstance = getCurrentInstance();
     // 初始化数据
     const state = reactive({
@@ -123,7 +122,7 @@ export default defineComponent({
     const showContent = computed(() => renderTNode(internalInstance, 'content'));
     // click
     function handleClick(trigger: NoticeBarTrigger) {
-      emitEvent('click', trigger);
+      props.onClick?.(trigger);
     }
     // 动画 i
     const animateStyle = computed(() => ({
diff --git a/src/picker/picker-item.vue b/src/picker/picker-item.vue
index 053babe8..c36e6cd1 100644
--- a/src/picker/picker-item.vue
+++ b/src/picker/picker-item.vue
@@ -11,7 +11,6 @@ import { ref, computed, onMounted, toRefs, defineComponent, PropType, watch } fr
 import config from '../config';
 import Picker from './picker.class';
 import { PickerColumnItem, PickerValue } from './type';
-import { useEmitEvent } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-picker-item`;
@@ -30,10 +29,13 @@ export default defineComponent({
       type: Function,
       default: undefined,
     },
+    onPick: {
+      type: Function,
+      default: undefined,
+    },
   },
   emits: ['pick'],
-  setup(props: any, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props, context) {
     let picker: Picker | null = null;
     const el = document.createElement('ul');
     const root = ref(el);
@@ -84,7 +86,7 @@ export default defineComponent({
         onChange: (index: number) => {
           const curItem = props.options[index];
           const changeValue = { value: curItem.value, index };
-          emitEvent('pick', changeValue);
+          props.onPick?.(changeValue);
         },
       });
     });
diff --git a/src/picker/picker.vue b/src/picker/picker.vue
index 8ec256e7..f6c542a8 100644
--- a/src/picker/picker.vue
+++ b/src/picker/picker.vue
@@ -23,14 +23,14 @@
 </template>
 
 <script lang="ts">
-import { computed, nextTick, defineComponent, toRefs, onMounted, ref, getCurrentInstance, watch } from 'vue';
+import { computed, defineComponent, toRefs, onMounted, ref, getCurrentInstance, watch } from 'vue';
 import isString from 'lodash/isString';
 import isBoolean from 'lodash/isBoolean';
 
 import config from '../config';
 import PickerProps from './props';
 import { PickerValue, PickerColumn, PickerColumnItem } from './type';
-import { useEmitEvent, useVModel, useChildSlots, useExpose, TNode, renderTNode } from '../shared';
+import { useVModel, useChildSlots, TNode, renderTNode } from '../shared';
 import PickerItem from './picker-item.vue';
 
 const { prefix } = config;
@@ -45,9 +45,8 @@ export default defineComponent({
   components: { PickerItem, TNode },
   props: PickerProps,
   emits: ['change', 'cancel', 'pick', 'update:modelValue', 'update:value'],
-  setup(props: any, context) {
+  setup(props: any) {
     const internalInstance = getCurrentInstance();
-    const emitEvent = useEmitEvent(props, context.emit);
     const { value, modelValue } = toRefs(props);
     const [pickerValue = ref([]), setPickerValue] = useVModel(value, modelValue, props.defaultValue, props.onChange);
     const getDefaultText = (prop: string | boolean, defaultText: string): string => {
@@ -80,13 +79,13 @@ export default defineComponent({
       const label = target.map((item: PickerColumnItem) => item.label);
       const value = target.map((item: PickerColumnItem) => item.value);
       setPickerValue(value);
-      emitEvent('confirm', value, { index: curIndexArray, label, e });
+      props.onConfirm?.(value, { index: curIndexArray, label, e });
     };
     const handleCancel = (e: MouseEvent) => {
       pickerItemInstanceArray.value.forEach((item: any, index: number) => {
         item.exposed?.setIndex(curIndexArray[index]);
       });
-      emitEvent('cancel', { e });
+      props.onCancel?.({ e });
     };
     const handlePick = (context: any, column: number) => {
       const { index } = context;
@@ -94,7 +93,7 @@ export default defineComponent({
       curIndexArray[column] = index;
       curValueArray.value[column] = realColumns.value[column][index]?.value;
 
-      emitEvent('pick', curValueArray.value, { index, column });
+      props.onPick?.(curValueArray.value, { index, column });
     };
 
     watch(pickerValue, () => {
diff --git a/src/popup/popup.vue b/src/popup/popup.vue
index 617d2ae3..8f9b3a18 100644
--- a/src/popup/popup.vue
+++ b/src/popup/popup.vue
@@ -20,7 +20,7 @@ import popupProps from './props';
 import TOverlay from '../overlay';
 import config from '../config';
 import { TdPopupProps } from './type';
-import { useDefault, useEmitEvent, TNode, renderTNode } from '../shared';
+import { useDefault, TNode, renderTNode } from '../shared';
 import { getAttach } from '../shared/dom';
 
 const { prefix } = config;
@@ -35,7 +35,6 @@ export default defineComponent({
   emits: ['open', 'close', 'opened', 'closed', 'visible-change', 'update:visible', 'update:modelValue'],
   setup(props, context) {
     const currentInstance = getCurrentInstance();
-    const emitEvent = useEmitEvent(props, context.emit);
     const [currentVisible, setVisible] = useDefault<TdPopupProps['visible'], TdPopupProps>(
       props,
       context.emit,
@@ -89,7 +88,7 @@ export default defineComponent({
     );
 
     const handleCloseClick = (e: MouseEvent) => {
-      emitEvent('close', { e });
+      props.onClose?.({ e });
       setVisible(false);
     };
 
@@ -98,22 +97,22 @@ export default defineComponent({
       if (!props.closeOnOverlayClick) {
         return;
       }
-      emitEvent('close', { e });
+      props.onClose?.({ e });
       setVisible(false);
     };
 
     const afterLeave = () => {
       wrapperVisbile.value = false;
-      emitEvent('closed');
+      props.onClosed?.();
     };
-    const afterEnter = () => emitEvent('opened');
+    const afterEnter = () => props.onOpened?.();
     const to = computed(() => getAttach(props.attach ?? 'body'));
 
     watch(
       () => currentVisible.value,
       (val) => {
         if (val) {
-          emitEvent('open');
+          props.onOpen?.();
           setVisible(true);
         }
       },
diff --git a/src/pull-down-refresh/pull-down-refresh.vue b/src/pull-down-refresh/pull-down-refresh.vue
index daa5cbbf..ead8fcd8 100644
--- a/src/pull-down-refresh/pull-down-refresh.vue
+++ b/src/pull-down-refresh/pull-down-refresh.vue
@@ -27,7 +27,8 @@ import debounce from 'lodash/debounce';
 import isArray from 'lodash/isArray';
 
 import PullDownRefreshProps from './props';
-import { useEmitEvent, useVModel, convertUnit, reconvertUnit } from '../shared';
+import { useVModel, convertUnit, reconvertUnit } from '../shared';
+
 import config from '../config';
 import TLoading from '../loading';
 import { useTouch, isReachTop, easeDistance } from './useTouch';
@@ -41,8 +42,7 @@ export default defineComponent({
   components: { TLoading },
   props: PullDownRefreshProps,
   emits: ['refresh', 'timeout', 'scrolltolower', 'update:value', 'update:modelValue'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     let timer: any = null;
 
     // 滑动距离
@@ -137,10 +137,10 @@ export default defineComponent({
       if (status.value === 'loosing') {
         distance.value = loadingBarHeight.value;
         setLoading(true);
-        emitEvent('refresh');
+        props.onRefresh?.();
         timer = setTimeout(() => {
           if (loading.value) {
-            emitEvent('timeout');
+            props.onTimeout?.();
             setLoading(false);
           }
         }, props.refreshTimeout);
@@ -155,7 +155,7 @@ export default defineComponent({
         const { clientHeight, scrollHeight } = document.documentElement; // 可视区域/屏幕高度， 页面高度
         const distance = 20; // 距离视窗 20 时，开始触发
         if (scrollTop + clientHeight >= scrollHeight - distance) {
-          emitEvent('scrolltolower');
+          props.onScrolltolower?.();
         }
       },
       300,
diff --git a/src/radio/props.ts b/src/radio/props.ts
index 268db252..e6e83ad1 100644
--- a/src/radio/props.ts
+++ b/src/radio/props.ts
@@ -8,15 +8,6 @@ import { TdRadioProps } from './type';
 import { PropType } from 'vue';
 
 export default {
-  /** 已废弃。复选框和内容相对位置 */
-  align: {
-    type: String as PropType<TdRadioProps['align']>,
-    default: 'left' as TdRadioProps['align'],
-    validator(val: TdRadioProps['align']): boolean {
-      if (!val) return true;
-      return ['left', 'right'].includes(val);
-    },
-  },
   /** 是否允许取消选中 */
   allowUncheck: Boolean,
   /** 是否为块级元素	 */
@@ -82,7 +73,7 @@ export default {
   /** 复选框和内容相对位置 */
   placement: {
     type: String as PropType<TdRadioProps['placement']>,
-    default: undefined,
+    default: 'left' as TdRadioProps['placement'],
     validator(val: TdRadioProps['placement']): boolean {
       if (!val) return true;
       return ['left', 'right'].includes(val);
diff --git a/src/radio/radio-group-props.ts b/src/radio/radio-group-props.ts
index ef8bc3a5..a1b8e389 100644
--- a/src/radio/radio-group-props.ts
+++ b/src/radio/radio-group-props.ts
@@ -14,6 +14,11 @@ export default {
   borderless: Boolean,
   /** 是否禁用全部子单选框。默认为 false。RadioGroup.disabled 优先级低于 Radio.disabled */
   disabled: Boolean,
+  /** 自定义选中图标和非选中图标。示例：[选中态图标地址，非选中态图标地址]。使用 String 时，值为 circle 表示填充型图标、值为 line 表示描边型图标、值为 dot 表示圆点图标 */
+  icon: {
+    type: [String, Array] as PropType<TdRadioGroupProps['icon']>,
+    default: 'circle',
+  },
   /** 用来定义 value / label 在 `options` 中对应的字段别名 */
   keys: {
     type: Object as PropType<TdRadioGroupProps['keys']>,
@@ -23,11 +28,6 @@ export default {
     type: String,
     default: '',
   },
-  /** 自定义选中图标和非选中图标。示例：[选中态图标地址，非选中态图标地址]。使用 String 时，值为 circle 表示填充型图标、值为 line 表示描边型图标、值为 dot 表示圆点图标 */
-  icon: {
-    type: [String, Array] as PropType<TdRadioGroupProps['icon']>,
-    default: 'circle',
-  },
   /** 单选组件按钮形式。RadioOption 数据类型为 string 或 number 时，表示 label 和 value 值相同 */
   options: {
     type: Array as PropType<TdRadioGroupProps['options']>,
diff --git a/src/radio/radio.en-US.md b/src/radio/radio.en-US.md
index 282b9635..3595bf87 100644
--- a/src/radio/radio.en-US.md
+++ b/src/radio/radio.en-US.md
@@ -5,7 +5,6 @@
 
 name | type | default | description | required
 -- | -- | -- | -- | --
-align | String | left | options：left/right | N
 allowUncheck | Boolean | false | \- | N
 block | Boolean | true | \- | N
 borderless | Boolean | undefined | \- | N
@@ -20,7 +19,7 @@ label | String / Slot / Function | - | Typescript：`string \| TNode`。[see mor
 maxContentRow | Number | 5 | \- | N
 maxLabelRow | Number | 3 | \- | N
 name | String | - | \- | N
-placement | String | undefined | options：left/right | N
+placement | String | left | options：left/right | N
 value | String / Number / Boolean | undefined | Typescript：`string \| number \| boolean` | N
 onChange | Function |  | Typescript：`(checked: boolean, context: { e: Event }) => void`<br/> | N
 
@@ -37,9 +36,10 @@ name | type | default | description | required
 allowUncheck | Boolean | false | \- | N
 borderless | Boolean | false | \- | N
 disabled | Boolean | - | \- | N
+icon | String / Array | 'circle' | Typescript：`'circle' \| 'line' \| 'dot' \| Array<TNode>`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 keys | Object | - | Typescript：`KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 name | String | - | \- | N
-options | Array | - | Typescript：`Array<RadioOption>` `type RadioOption = string \| number \| RadioOptionObj` `interface RadioOptionObj { label?: string \| TNode; value?: string \| number; disabled?: boolean }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
+options | Array | - | Typescript：`Array<RadioOption>` `type RadioOption = string \| number \| RadioOptionObj` `interface RadioOptionObj { label?: string \| TNode; value?: string \| number \| boolean; disabled?: boolean }`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
 placement | String | left | options：left/right | N
 value | String / Number / Boolean | - | `v-model` and `v-model:value` is supported。Typescript：`T` `type RadioValue = string \| number \| boolean`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
 defaultValue | String / Number / Boolean | - | uncontrolled property。Typescript：`T` `type RadioValue = string \| number \| boolean`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
diff --git a/src/radio/radio.md b/src/radio/radio.md
index 48eb7231..9e38714f 100644
--- a/src/radio/radio.md
+++ b/src/radio/radio.md
@@ -5,7 +5,6 @@
 
 名称 | 类型 | 默认值 | 说明 | 必传
 -- | -- | -- | -- | --
-align | String | left | 已废弃。复选框和内容相对位置。可选项：left/right | N
 allowUncheck | Boolean | false | 是否允许取消选中 | N
 block | Boolean | true | 是否为块级元素	 | N
 borderless | Boolean | undefined | 是否开启无边框模式 | N
@@ -37,9 +36,10 @@ change | `(checked: boolean, context: { e: Event })` | 选中状态变化时触
 allowUncheck | Boolean | false | 是否允许取消选中 | N
 borderless | Boolean | false | 是否开启无边框模式；优先级低于 Radio | N
 disabled | Boolean | - | 是否禁用全部子单选框。默认为 false。RadioGroup.disabled 优先级低于 Radio.disabled | N
+icon | String / Array | 'circle' | 自定义选中图标和非选中图标。示例：[选中态图标地址，非选中态图标地址]。使用 String 时，值为 circle 表示填充型图标、值为 line 表示描边型图标、值为 dot 表示圆点图标。TS 类型：`'circle' \| 'line' \| 'dot' \| Array<TNode>`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 keys | Object | - | 用来定义 value / label 在 `options` 中对应的字段别名。TS 类型：`KeysType`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 name | String | - | HTML 元素原生属性 | N
-options | Array | - | 单选组件按钮形式。RadioOption 数据类型为 string 或 number 时，表示 label 和 value 值相同。TS 类型：`Array<RadioOption>` `type RadioOption = string \| number \| RadioOptionObj` `interface RadioOptionObj { label?: string \| TNode; value?: string \| number; disabled?: boolean }`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
+options | Array | - | 单选组件按钮形式。RadioOption 数据类型为 string 或 number 时，表示 label 和 value 值相同。TS 类型：`Array<RadioOption>` `type RadioOption = string \| number \| RadioOptionObj` `interface RadioOptionObj { label?: string \| TNode; value?: string \| number \| boolean; disabled?: boolean }`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
 placement | String | left | 复选框和内容相对位置。可选项：left/right | N
 value | String / Number / Boolean | - | 选中的值。支持语法糖 `v-model` 或 `v-model:value`。TS 类型：`T` `type RadioValue = string \| number \| boolean`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
 defaultValue | String / Number / Boolean | - | 选中的值。非受控属性。TS 类型：`T` `type RadioValue = string \| number \| boolean`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/radio/type.ts) | N
diff --git a/src/radio/type.ts b/src/radio/type.ts
index a04dc097..7d1dbb16 100644
--- a/src/radio/type.ts
+++ b/src/radio/type.ts
@@ -7,11 +7,6 @@
 import { TNode, KeysType } from '../common';
 
 export interface TdRadioProps {
-  /**
-   * 已废弃。复选框和内容相对位置
-   * @default left
-   */
-  align?: 'left' | 'right';
   /**
    * 是否允许取消选中
    * @default false
@@ -156,7 +151,7 @@ export type RadioOption = string | number | RadioOptionObj;
 
 export interface RadioOptionObj {
   label?: string | TNode;
-  value?: string | number;
+  value?: string | number | boolean;
   disabled?: boolean;
 }
 
diff --git a/src/rate/props.ts b/src/rate/props.ts
index 68b93694..eaffefa5 100644
--- a/src/rate/props.ts
+++ b/src/rate/props.ts
@@ -10,8 +10,6 @@ import { PropType } from 'vue';
 export default {
   /** 是否允许半选 */
   allowHalf: Boolean,
-  /** 已废弃。是否允许取消选择 */
-  clearable: Boolean,
   /** 评分图标的颜色，样式中默认为 #ED7B2F。一个值表示设置选中高亮的五角星颜色，示例：[选中颜色]。数组则表示分别设置 选中高亮的五角星颜色 和 未选中暗灰的五角星颜色，[选中颜色，未选中颜色]。示例：['#ED7B2F', '#E3E6EB']。 */
   color: {
     type: [String, Array] as PropType<TdRateProps['color']>,
@@ -31,7 +29,7 @@ export default {
   },
   /** 自定义评分图标，[选中图标，未选中图标] */
   icon: {
-    type: [Function, Array] as PropType<TdRateProps['icon']>,
+    type: [Array, Function] as PropType<TdRateProps['icon']>,
   },
   /** 是否显示对应的辅助文字 */
   showText: Boolean,
diff --git a/src/rate/rate.en-US.md b/src/rate/rate.en-US.md
index a8788a95..e01094f1 100644
--- a/src/rate/rate.en-US.md
+++ b/src/rate/rate.en-US.md
@@ -1,18 +1,16 @@
 :: BASE_DOC ::
 
 ## API
-
 ### Rate Props
 
 name | type | default | description | required
 -- | -- | -- | -- | --
 allowHalf | Boolean | false | \- | N
-clearable | Boolean | false | `deprecated` | N
 color | String / Array | '#ED7B2F' | Typescript：`string \| Array<string>` | N
 count | Number | 5 | \- | N
 disabled | Boolean | - | \- | N
 gap | Number | 8 | \- | N
-icon | Array / Slot / Function | - | Typescript：`string \| string[]` | N
+icon | Array / Slot / Function | - | Typescript：`Array<TNode \| Function>`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 showText | Boolean | false | \- | N
 size | String | 24px | \- | N
 texts | Array | [] | Typescript：`Array<string>` | N
diff --git a/src/rate/rate.md b/src/rate/rate.md
index 75fe99fb..3e16bf39 100644
--- a/src/rate/rate.md
+++ b/src/rate/rate.md
@@ -6,12 +6,11 @@
 名称 | 类型 | 默认值 | 说明 | 必传
 -- | -- | -- | -- | --
 allowHalf | Boolean | false | 是否允许半选 | N
-clearable | Boolean | false | 已废弃。是否允许取消选择 | N
 color | String / Array | '#ED7B2F' | 评分图标的颜色，样式中默认为 #ED7B2F。一个值表示设置选中高亮的五角星颜色，示例：[选中颜色]。数组则表示分别设置 选中高亮的五角星颜色 和 未选中暗灰的五角星颜色，[选中颜色，未选中颜色]。示例：['#ED7B2F', '#E3E6EB']。。TS 类型：`string \| Array<string>` | N
 count | Number | 5 | 评分的数量 | N
 disabled | Boolean | - | 是否禁用评分 | N
 gap | Number | 8 | 评分图标的间距 | N
-icon | Array / Slot / Function | - | 自定义评分图标，[选中图标，未选中图标]。TS 类型：`string \| string[]` | N
+icon | Array / Slot / Function | - | 自定义评分图标，[选中图标，未选中图标]。TS 类型：`Array<TNode \| Function>`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
 showText | Boolean | false | 是否显示对应的辅助文字 | N
 size | String | 24px | 评分图标的大小 | N
 texts | Array | [] | 评分等级对应的辅助文字。组件内置默认值为：['极差', '失望', '一般', '满意', '惊喜']。自定义值示例：['1分', '2分', '3分', '4分', '5分']。TS 类型：`Array<string>` | N
diff --git a/src/rate/type.ts b/src/rate/type.ts
index 24aa9a1c..18783431 100644
--- a/src/rate/type.ts
+++ b/src/rate/type.ts
@@ -1,23 +1,17 @@
 /* eslint-disable */
 
-import { TNode } from '@/common';
-
 /**
  * 该文件为脚本自动生成文件，请勿随意修改。如需修改请联系 PMC
  * */
 
+import { TNode } from '../common';
+
 export interface TdRateProps {
   /**
    * 是否允许半选
    * @default false
    */
   allowHalf?: boolean;
-  /**
-   * 是否允许取消选择
-   * @default false
-   * @deprecated
-   */
-  clearable?: boolean;
   /**
    * 评分图标的颜色，样式中默认为 #ED7B2F。一个值表示设置选中高亮的五角星颜色，示例：[选中颜色]。数组则表示分别设置 选中高亮的五角星颜色 和 未选中暗灰的五角星颜色，[选中颜色，未选中颜色]。示例：['#ED7B2F', '#E3E6EB']。
    * @default '#ED7B2F'
diff --git a/src/search/demos/mobile.vue b/src/search/demos/mobile.vue
index 5bb11b58..ab498c80 100644
--- a/src/search/demos/mobile.vue
+++ b/src/search/demos/mobile.vue
@@ -5,7 +5,7 @@
     <tdesign-demo-block title="01 组件类型" summary="基础搜索框">
       <baseDemo />
     </tdesign-demo-block>
-    <tdesign-demo-block summary="获取焦点后显示取消按钮">
+    <tdesign-demo-block summary="输入值后显示取消按钮">
       <actionDemo />
     </tdesign-demo-block>
     <tdesign-demo-block title="02 组件样式" summary="搜索框形状">
diff --git a/src/search/search.vue b/src/search/search.vue
index 4d57fb34..af7ed3ce 100644
--- a/src/search/search.vue
+++ b/src/search/search.vue
@@ -24,7 +24,7 @@
       </div>
     </div>
     <slot name="action">
-      <div v-if="action" v-show="focused" :class="`${name}__search-action`" @click="onActionClick">
+      <div v-if="action && searchValue" :class="`${name}__search-action`" @click="onActionClick">
         {{ action }}
       </div>
     </slot>
@@ -37,7 +37,7 @@ import { ref, computed, defineComponent, toRefs, getCurrentInstance, h, nextTick
 import isFunction from 'lodash/isFunction';
 import { useFocus } from '@vueuse/core';
 import config from '../config';
-import { renderTNode, TNode, useEmitEvent } from '../shared';
+import { renderTNode, TNode } from '../shared';
 import searchProps from './props';
 import { useDefault } from '../shared/useDefault';
 
@@ -50,7 +50,6 @@ export default defineComponent({
   props: searchProps,
   emits: ['update:value', 'update:modelValue', 'action-click', 'focus', 'blur', 'change', 'clear', 'submit'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const searchInput = ref();
     const { focused } = useFocus(searchInput, { initialValue: props.focus });
     const [searchValue] = useDefault(props, context.emit, 'value', 'change');
@@ -102,15 +101,15 @@ export default defineComponent({
     const handleClear = (e: MouseEvent) => {
       searchValue.value = '';
       focused.value = true;
-      emitEvent('clear', { e });
+      props.onClear?.({ e });
     };
 
     const handleFocus = (e: FocusEvent) => {
-      emitEvent('focus', { value: searchValue.value, e });
+      props.onFocus?.({ value: searchValue.value, e });
     };
 
     const handleBlur = (e: FocusEvent) => {
-      emitEvent('blur', { value: searchValue.value, e });
+      props.onBlur?.({ value: searchValue.value, e });
     };
 
     const handleCompositionend = (e: InputEvent | CompositionEvent) => {
@@ -118,14 +117,14 @@ export default defineComponent({
     };
 
     const onActionClick = (e: MouseEvent) => {
-      emitEvent('action-click', { e: MouseEvent });
+      props.onActionClick?.({ e });
     };
 
     const handleSearch = (e: any) => {
       // 如果按的是 enter 键, 13是 enter
       if (e.keyCode === 13) {
         e.preventDefault(); // 禁止默认（换行）事件
-        emitEvent('submit', { value: searchValue.value, e });
+        props.onSubmit?.({ value: searchValue.value, e });
       }
     };
 
diff --git a/src/shared/index.ts b/src/shared/index.ts
index 94251f85..0131e94c 100644
--- a/src/shared/index.ts
+++ b/src/shared/index.ts
@@ -9,7 +9,6 @@ export { default as TNode } from './render-tnode';
 export * from './useToggle';
 export * from './useCountDown';
 export * from './useDefault';
-export * from './useEmitEvent';
 export * from './useChildSlots';
 export * from './useVModel';
 export * from './useTouch';
diff --git a/src/shared/useEmitEvent/index.ts b/src/shared/useEmitEvent/index.ts
deleted file mode 100644
index 8ee8a2a7..00000000
--- a/src/shared/useEmitEvent/index.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-import camelCase from 'lodash/camelCase';
-
-export type EmitEventName = { event: string; method: string } | string;
-
-/**
- * 组件对外传递事件
- * @param props 组件props
- * @param emit 事件名(注意使用中划线)
- * @param args 事件参数
- * @returns {emitEvent}
- * @example useEmitEvent<IProps>(props, emit);
- */
-export function useEmitEvent<P extends Record<string, any>>(props: P, emit: (...args: any[]) => void) {
-  return function emitEvent<T extends any[] = any[]>(eventName: string, ...args: T) {
-    let emitEventMethodName = '';
-    if (typeof eventName === 'string') {
-      emitEventMethodName = camelCase(`on-${eventName}`);
-    }
-
-    if (typeof props[emitEventMethodName] === 'function') {
-      props[emitEventMethodName](...args);
-    } else {
-      emit(eventName, ...args);
-    }
-  };
-}
diff --git a/src/side-bar/side-bar.vue b/src/side-bar/side-bar.vue
index c3e7b19f..75addb8c 100644
--- a/src/side-bar/side-bar.vue
+++ b/src/side-bar/side-bar.vue
@@ -10,7 +10,7 @@ import { defineComponent, ref, Ref, ComponentInternalInstance, provide, toRefs }
 import config from '../config';
 import SideBarProps from './props';
 import { TdSideBarProps } from './type';
-import { useEmitEvent, useDefault } from '../shared';
+import { useDefault } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-side-bar`;
@@ -20,7 +20,6 @@ export default defineComponent({
   props: SideBarProps,
   emits: ['update:value', 'update:modelValue', 'change'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const [currentValue, setCurrentValue] = useDefault<TdSideBarProps['value'], TdSideBarProps>(
       props,
       context.emit,
@@ -38,9 +37,9 @@ export default defineComponent({
       children.value = children.value.filter((item) => item !== child);
     };
 
-    const onClickItem = (cur: TdSideBarProps['value'], label: string) => {
+    const onClickItem = (cur: string | number, label: string) => {
       setCurrentValue(cur);
-      emitEvent('click', cur, label);
+      props.onClick?.(cur, label);
     };
 
     provide('sideBarProvide', {
diff --git a/src/stepper/props.ts b/src/stepper/props.ts
index 3af21942..4d2d3887 100644
--- a/src/stepper/props.ts
+++ b/src/stepper/props.ts
@@ -67,6 +67,8 @@ export default {
   onBlur: Function as PropType<TdStepperProps['onBlur']>,
   /** 数值发生变更时触发 */
   onChange: Function as PropType<TdStepperProps['onChange']>,
+  /** 输入框聚焦时触发 */
+  onFocus: Function as PropType<TdStepperProps['onFocus']>,
   /** 数值超出限制时触发 */
   onOverlimit: Function as PropType<TdStepperProps['onOverlimit']>,
 };
diff --git a/src/stepper/stepper.en-US.md b/src/stepper/stepper.en-US.md
index 408c6c2a..20fd3427 100644
--- a/src/stepper/stepper.en-US.md
+++ b/src/stepper/stepper.en-US.md
@@ -17,6 +17,7 @@ value | String / Number | 0 | `v-model` and `v-model:value` is supported | N
 defaultValue | String / Number | 0 | uncontrolled property | N
 onBlur | Function |  | Typescript：`(value: string \| number) => void`<br/> | N
 onChange | Function |  | Typescript：`(value: string \| number) => void`<br/> | N
+onFocus | Function |  | Typescript：`(value: string \| number) => void`<br/> | N
 onOverlimit | Function |  | Typescript：`(type: 'minus' \| 'plus') => void`<br/> | N
 
 ### Stepper Events
@@ -25,4 +26,5 @@ name | params | description
 -- | -- | --
 blur | `(value: string \| number)` | \-
 change | `(value: string \| number)` | \-
+focus | `(value: string \| number)` | \-
 overlimit | `(type: 'minus' \| 'plus')` | \-
diff --git a/src/stepper/stepper.md b/src/stepper/stepper.md
index 4184b724..9f9163c9 100644
--- a/src/stepper/stepper.md
+++ b/src/stepper/stepper.md
@@ -17,6 +17,7 @@ value | String / Number | 0 | 值。支持语法糖 `v-model` 或 `v-model:value
 defaultValue | String / Number | 0 | 值。非受控属性 | N
 onBlur | Function |  | TS 类型：`(value: string \| number) => void`<br/>输入框失去焦点时触发 | N
 onChange | Function |  | TS 类型：`(value: string \| number) => void`<br/>数值发生变更时触发 | N
+onFocus | Function |  | TS 类型：`(value: string \| number) => void`<br/>输入框聚焦时触发 | N
 onOverlimit | Function |  | TS 类型：`(type: 'minus' \| 'plus') => void`<br/>数值超出限制时触发 | N
 
 ### Stepper Events
@@ -25,4 +26,5 @@ onOverlimit | Function |  | TS 类型：`(type: 'minus' \| 'plus') => void`<br/>
 -- | -- | --
 blur | `(value: string \| number)` | 输入框失去焦点时触发
 change | `(value: string \| number)` | 数值发生变更时触发
+focus | `(value: string \| number)` | 输入框聚焦时触发
 overlimit | `(type: 'minus' \| 'plus')` | 数值超出限制时触发
diff --git a/src/stepper/stepper.vue b/src/stepper/stepper.vue
index 2ea1b0f6..d255a780 100644
--- a/src/stepper/stepper.vue
+++ b/src/stepper/stepper.vue
@@ -5,7 +5,7 @@
         `${name}__minus`,
         `${name}__minus--${theme}`,
         `${name}__icon--${size}`,
-        `${disabled || stepperValue <= min ? name + '--' + theme + '-disabled' : ''}`,
+        `${disabled || Number(stepperValue) <= min ? name + '--' + theme + '-disabled' : ''}`,
       ]"
       @click="minusValue"
     >
@@ -33,7 +33,7 @@
         `${name}__plus`,
         `${name}__plus--${theme}`,
         `${name}__icon--${size}`,
-        `${disabled || stepperValue >= max ? name + '--' + theme + '-disabled' : ''}`,
+        `${disabled || Number(stepperValue) >= max ? name + '--' + theme + '-disabled' : ''}`,
       ]"
       @click="plusValue"
     >
@@ -47,7 +47,7 @@ import { toRefs, computed, defineComponent } from 'vue';
 import { AddIcon, RemoveIcon } from 'tdesign-icons-vue-next';
 import config from '../config';
 import StepperProps from './props';
-import { useDefault, useEmitEvent } from '../shared';
+import { useDefault } from '../shared';
 import { TdStepperProps } from './type';
 import { useFormDisabled } from '../form/hooks';
 
@@ -62,44 +62,60 @@ export default defineComponent({
   props: StepperProps,
   emits: ['update:value', 'update:modelValue', 'blur', 'change', 'overlimit'],
   setup(props, context) {
-    const [stepperValue] = useDefault<number, TdStepperProps>(props, context.emit, 'value', 'change');
-    const emitEvent = useEmitEvent(props, context.emit);
+    const [stepperValue] = useDefault<TdStepperProps['value'], TdStepperProps>(props, context.emit, 'value', 'change');
     const disabled = useFormDisabled();
     const { min, max, step, inputWidth } = toRefs(props);
     const inputStyle = computed(() => (inputWidth ? { width: `${inputWidth.value}px` } : ''));
 
     const isDisabled = (type: 'minus' | 'plus') => {
       if (disabled.value) return true;
-      if (type === 'minus' && stepperValue.value <= min.value) {
+      if (type === 'minus' && Number(stepperValue.value) <= min.value) {
         return true;
       }
-      if (type === 'plus' && stepperValue.value >= max.value) {
+      if (type === 'plus' && Number(stepperValue.value) >= max.value) {
         return true;
       }
       return false;
     };
 
-    const formatValue = (value: number) =>
-      Math.max(Math.min(max.value, value, Number.MAX_SAFE_INTEGER), min.value, Number.MIN_SAFE_INTEGER);
+    const getLen = (num: number) => {
+      const numStr = num.toString();
+      return numStr.indexOf('.') === -1 ? 0 : numStr.split('.')[1].length;
+    };
+
+    /**
+     * 精确加法
+     */
+    const add = (a: number, b: number) => {
+      const maxLen = Math.max(getLen(a), getLen(b));
+      const base = 10 ** maxLen;
+      return Math.round(a * base + b * base) / base;
+    };
+
+    const formatValue = (value: number) => {
+      return Math.max(Math.min(max.value, value, Number.MAX_SAFE_INTEGER), min.value, Number.MIN_SAFE_INTEGER).toFixed(
+        getLen(step.value),
+      );
+    };
 
-    const updateValue = (value: number) => {
-      stepperValue.value = formatValue(value);
+    const updateValue = (value: TdStepperProps['value']) => {
+      stepperValue.value = value;
     };
 
     const plusValue = () => {
       if (isDisabled('plus')) {
-        emitEvent('overlimit', 'plus');
+        props.onOverlimit?.('plus');
         return;
       }
-      updateValue(Number(stepperValue.value) + step.value);
+      updateValue(formatValue(add(Number(stepperValue.value), step.value)));
     };
 
     const minusValue = () => {
       if (isDisabled('minus')) {
-        emitEvent('overlimit', 'minus');
+        props.onOverlimit?.('minus');
         return;
       }
-      updateValue(Number(stepperValue.value) - step.value);
+      updateValue(formatValue(add(Number(stepperValue.value), -step.value)));
     };
 
     const handleInput = (e: Event) => {
@@ -108,16 +124,16 @@ export default defineComponent({
     };
 
     const handleChange = () => {
-      const formattedValue = formatValue(stepperValue.value);
+      const formattedValue = formatValue(Number(stepperValue.value));
       updateValue(formattedValue);
     };
 
     const handleFocus = () => {
-      emitEvent('focus', stepperValue.value);
+      props.onFocus?.(Number(stepperValue.value));
     };
 
     const handleBlur = () => {
-      emitEvent('blur', stepperValue.value);
+      props.onBlur?.(Number(stepperValue.value));
     };
 
     return {
diff --git a/src/stepper/type.ts b/src/stepper/type.ts
index 5af9d9ba..d4d8762d 100644
--- a/src/stepper/type.ts
+++ b/src/stepper/type.ts
@@ -69,6 +69,10 @@ export interface TdStepperProps {
    * 数值发生变更时触发
    */
   onChange?: (value: string | number) => void;
+  /**
+   * 输入框聚焦时触发
+   */
+  onFocus?: (value: string | number) => void;
   /**
    * 数值超出限制时触发
    */
diff --git a/src/sticky/sticky.vue b/src/sticky/sticky.vue
index 19c803c8..7fd55571 100644
--- a/src/sticky/sticky.vue
+++ b/src/sticky/sticky.vue
@@ -11,7 +11,7 @@ import { computed, getCurrentInstance, defineComponent, ref, watch } from 'vue';
 import { useElementBounding, templateRef } from '@vueuse/core';
 import props from './props';
 import config from '../config';
-import { renderContent, useEmitEvent, TNode } from '../shared';
+import { renderContent, TNode } from '../shared';
 
 const name = `${config.prefix}-sticky`;
 
@@ -19,10 +19,9 @@ export default defineComponent({
   name,
   components: { TNode },
   props,
-  setup(props, context) {
+  setup(props) {
     const boxClasses = name;
     const stickyContent = computed(() => renderContent(getCurrentInstance(), 'default', ''));
-    const emitEvent = useEmitEvent(props, context.emit);
 
     // box 用于占位和记录边界
     // content 用于实际定位
@@ -66,7 +65,7 @@ export default defineComponent({
         styleStr += `position:fixed;top:${offsetTop}px;`;
         isFixed = true;
       }
-      emitEvent('scroll', { scrollTop: contentTop.value, isFixed });
+      props.onScroll?.({ scrollTop: contentTop.value, isFixed });
       return styleStr;
     });
 
diff --git a/src/swipe-cell/swipe-cell.vue b/src/swipe-cell/swipe-cell.vue
index f026edeb..d7de7a6d 100644
--- a/src/swipe-cell/swipe-cell.vue
+++ b/src/swipe-cell/swipe-cell.vue
@@ -91,7 +91,7 @@ import { useSwipe } from './useSwipe';
 import props from './props';
 import config from '../config';
 import { SwipeActionItem } from './type';
-import { renderContent, renderTNode, TNode, useEmitEvent, useClickAway } from '../shared';
+import { renderContent, renderTNode, TNode, useClickAway } from '../shared';
 import { useSureConfirm } from './useSureConfirm';
 
 const { prefix } = config;
@@ -113,7 +113,6 @@ export default defineComponent({
   props,
   emits: ['click', 'change'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const internalInstance = getCurrentInstance();
     const swipeContent = computed(() => renderContent(internalInstance, 'default', 'content'));
     const swipeLeftMenu = computed(() =>
@@ -356,12 +355,12 @@ export default defineComponent({
       if (direction === 'toLeft') {
         initData.pos = -initData.rightWidth;
         if (initData.rightWidth && !isOpen) {
-          emitEvent('change', 'right');
+          props.onChange?.('right');
         }
       } else {
         initData.pos = initData.leftWidth;
         if (initData.leftWidth && !isOpen) {
-          emitEvent('change', 'left');
+          props.onChange?.('left');
         }
       }
     };
@@ -373,7 +372,7 @@ export default defineComponent({
       sureMarginRight.value = 0;
       initData.status = 'close';
       initData.pos = 0;
-      emitEvent('change', undefined);
+      props.onChange?.(undefined);
     };
     const handleClickBtn = ({ action, source }: { action: SwipeActionItem; source: String }) => {
       const clickFn = () => {
@@ -384,7 +383,7 @@ export default defineComponent({
           action.onClick();
           return;
         }
-        emitEvent('click', { action, source });
+        props.onClick?.({ action, source });
       };
       if (action.sure) {
         showSure(action.sure, clickFn);
diff --git a/src/swiper/swiper.vue b/src/swiper/swiper.vue
index bdc80fd4..a7b6f3fe 100644
--- a/src/swiper/swiper.vue
+++ b/src/swiper/swiper.vue
@@ -58,7 +58,7 @@ import { isNumber } from 'lodash';
 
 import config from '../config';
 import SwiperProps from './props';
-import { renderTNode, TNode, useEmitEvent, useVModel } from '../shared';
+import { renderTNode, TNode, useVModel } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-swiper`;
@@ -81,7 +81,6 @@ const items = ref<any>([]);
 const props = defineProps(SwiperProps);
 const emit = defineEmits(['change', 'update:current', 'update:modelValue']);
 
-const emitEvent = useEmitEvent(props, emit);
 const [current, setCurrent] = useVModel(
   ref(props.current),
   ref(props.modelValue),
@@ -112,7 +111,7 @@ const enableNavigation = computed(() => {
 let autoplayTimer: any = null;
 
 const onItemClick = () => {
-  emitEvent('click', current.value);
+  props.onClick?.(current.value ?? 0);
 };
 
 const move = (step: number) => {
diff --git a/src/switch/props.ts b/src/switch/props.ts
index c88f8b1d..ade77dae 100644
--- a/src/switch/props.ts
+++ b/src/switch/props.ts
@@ -8,10 +8,6 @@ import { TdSwitchProps } from './type';
 import { PropType } from 'vue';
 
 export default {
-  /** 已废弃。已废弃。自定义颜色，[打开时的颜色，关闭时的颜色]。组件默认颜色为 ['#0052d9', 'rgba(0, 0, 0, .26']。示例：[blue, gray] */
-  colors: {
-    type: Array as PropType<TdSwitchProps['colors']>,
-  },
   /** 用于自定义开关的值，[打开时的值，关闭时的值]。默认为 [true, false]。示例：[1, 0]、['open', 'close'] */
   customValue: {
     type: Array as PropType<TdSwitchProps['customValue']>,
diff --git a/src/switch/switch.en-US.md b/src/switch/switch.en-US.md
index 8a22fcb8..ca94f820 100644
--- a/src/switch/switch.en-US.md
+++ b/src/switch/switch.en-US.md
@@ -5,7 +5,6 @@
 
 name | type | default | description | required
 -- | -- | -- | -- | --
-colors | Array | - | `deprecated`。Typescript：`string[]` | N
 customValue | Array | - | Typescript：`Array<SwitchValue>` | N
 disabled | Boolean | - | \- | N
 icon | Array | [] | Typescript：`TNode[]`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
@@ -14,10 +13,10 @@ loading | Boolean | false | \- | N
 size | String | medium | options：small/medium/large | N
 value | String / Number / Boolean | - | `v-model` and `v-model:value` is supported。Typescript：`T` `type SwitchValue = string \| number \| boolean`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/switch/type.ts) | N
 defaultValue | String / Number / Boolean | - | uncontrolled property。Typescript：`T` `type SwitchValue = string \| number \| boolean`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/switch/type.ts) | N
-onChange | Function |  | Typescript：`(value: T) => void`<br/> | N
+onChange | Function |  | Typescript：`(value: T, context: { e: MouseEvent }) => void`<br/> | N
 
 ### Switch Events
 
 name | params | description
 -- | -- | --
-change | `(value: T)` | \-
+change | `(value: T, context: { e: MouseEvent })` | \-
diff --git a/src/switch/switch.md b/src/switch/switch.md
index 1acd903a..099315cf 100644
--- a/src/switch/switch.md
+++ b/src/switch/switch.md
@@ -5,7 +5,6 @@
 
 名称 | 类型 | 默认值 | 说明 | 必传
 -- | -- | -- | -- | --
-colors | Array | - | 已废弃。已废弃。自定义颜色，[打开时的颜色，关闭时的颜色]。组件默认颜色为 ['#0052d9', 'rgba(0, 0, 0, .26']。示例：[blue, gray]。TS 类型：`string[]` | N
 customValue | Array | - | 用于自定义开关的值，[打开时的值，关闭时的值]。默认为 [true, false]。示例：[1, 0]、['open', 'close']。TS 类型：`Array<SwitchValue>` | N
 disabled | Boolean | - | 是否禁用组件，默认为 false | N
 icon | Array | [] | 开关的图标；[打开时的图标，关闭时的图标]。TS 类型：`TNode[]`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
@@ -14,10 +13,10 @@ loading | Boolean | false | 是否处于加载中状态 | N
 size | String | medium | 开关尺寸。可选项：small/medium/large | N
 value | String / Number / Boolean | - | 开关值。支持语法糖 `v-model` 或 `v-model:value`。TS 类型：`T` `type SwitchValue = string \| number \| boolean`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/switch/type.ts) | N
 defaultValue | String / Number / Boolean | - | 开关值。非受控属性。TS 类型：`T` `type SwitchValue = string \| number \| boolean`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/switch/type.ts) | N
-onChange | Function |  | TS 类型：`(value: T) => void`<br/>数据发生变化时触发 | N
+onChange | Function |  | TS 类型：`(value: T, context: { e: MouseEvent }) => void`<br/>数据发生变化时触发 | N
 
 ### Switch Events
 
 名称 | 参数 | 描述
 -- | -- | --
-change | `(value: T)` | 数据发生变化时触发
+change | `(value: T, context: { e: MouseEvent })` | 数据发生变化时触发
diff --git a/src/switch/type.ts b/src/switch/type.ts
index f48c2a45..e83495c8 100644
--- a/src/switch/type.ts
+++ b/src/switch/type.ts
@@ -7,11 +7,6 @@
 import { TNode } from '../common';
 
 export interface TdSwitchProps<T = SwitchValue> {
-  /**
-   * 已废弃。自定义颜色，[打开时的颜色，关闭时的颜色]。组件默认颜色为 ['#0052d9', 'rgba(0, 0, 0, .26']。示例：[blue, gray]
-   * @deprecated
-   */
-  colors?: string[];
   /**
    * 用于自定义开关的值，[打开时的值，关闭时的值]。默认为 [true, false]。示例：[1, 0]、['open', 'close']
    */
@@ -55,7 +50,7 @@ export interface TdSwitchProps<T = SwitchValue> {
   /**
    * 数据发生变化时触发
    */
-  onChange?: (value: T) => void;
+  onChange?: (value: T, context: { e: MouseEvent }) => void;
 }
 
 export type SwitchValue = string | number | boolean;
diff --git a/src/table/base-table.vue b/src/table/base-table.vue
index 3eec3bde..9ce3d50e 100644
--- a/src/table/base-table.vue
+++ b/src/table/base-table.vue
@@ -88,7 +88,7 @@ import TdBaseTableProps from './base-table-props';
 import config from '../config';
 import useClassName from './hooks/useClassName';
 import useStyle, { formatCSSUnit } from './hooks/useStyle';
-import { renderTNode, TNode, useEmitEvent } from '../shared';
+import { renderTNode, TNode } from '../shared';
 import { TableRowData } from './type';
 import TLoading from '../loading';
 
@@ -100,9 +100,7 @@ export default defineComponent({
   components: { TNode },
   props: TdBaseTableProps,
   emits: ['cell-click', 'row-click', 'scroll'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
-
+  setup(props) {
     const { classPrefix, tableLayoutClasses, tableHeaderClasses, tableBaseClass, tdAlignClasses, tdEllipsisClass } =
       useClassName();
 
@@ -124,16 +122,14 @@ export default defineComponent({
     const ellipsisClasses = computed(() => [`${classPrefix}-table__ellipsis`, `${classPrefix}-text-ellipsis`]);
 
     const handleRowClick = (row: TableRowData, rowIndex: number, e: MouseEvent) => {
-      const p = { row, index: rowIndex, e };
-      emitEvent('rowClick', { context: p });
+      props.onRowClick?.({ row, index: rowIndex, e });
     };
 
     const handleCellClick = (row: TableRowData, col: any, rowIndex: number, colIndex: number, e: MouseEvent) => {
-      const p = { row, col, rowIndex, colIndex, e };
       if (col.stopPropagation) {
         e.stopPropagation();
       }
-      emitEvent('cellClick', { context: p });
+      props.onCellClick?.({ row, col, rowIndex, colIndex, e });
     };
 
     const dynamicBaseTableClasses = computed(() => [tableClasses.value]);
@@ -148,7 +144,7 @@ export default defineComponent({
     );
 
     const onInnerVirtualScroll = (e: WheelEvent) => {
-      emitEvent('scroll', { params: e });
+      props.onScroll?.({ params: e });
     };
 
     return {
diff --git a/src/tabs/props.ts b/src/tabs/props.ts
index afe410ce..8c2568f9 100644
--- a/src/tabs/props.ts
+++ b/src/tabs/props.ts
@@ -16,15 +16,6 @@ export default {
   list: {
     type: Array as PropType<TdTabsProps['list']>,
   },
-  /** 已废弃。选项卡位置 */
-  placement: {
-    type: String as PropType<TdTabsProps['placement']>,
-    default: 'top' as TdTabsProps['placement'],
-    validator(val: TdTabsProps['placement']): boolean {
-      if (!val) return true;
-      return ['left', 'top'].includes(val);
-    },
-  },
   /** 是否展示底部激活线条 */
   showBottomLine: {
     type: Boolean,
diff --git a/src/tabs/tabs.en-US.md b/src/tabs/tabs.en-US.md
index ddba604d..0427cf9b 100644
--- a/src/tabs/tabs.en-US.md
+++ b/src/tabs/tabs.en-US.md
@@ -1,14 +1,12 @@
 :: BASE_DOC ::
 
 ## API
-
 ### Tabs Props
 
 name | type | default | description | required
 -- | -- | -- | -- | --
 animation | Object | - | Typescript：`TabAnimation` `type TabAnimation = { duration: number } & Record<string, any>`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/tabs/type.ts) | N
 list | Array | - | Typescript：`Array<TdTabPanelProps>` | N
-placement | String | top | options：left/top | N
 showBottomLine | Boolean | true | \- | N
 size | String | medium | options：medium/large | N
 spaceEvenly | Boolean | true | \- | N
diff --git a/src/tabs/tabs.md b/src/tabs/tabs.md
index 63f647ec..1a4cc9da 100644
--- a/src/tabs/tabs.md
+++ b/src/tabs/tabs.md
@@ -7,7 +7,6 @@
 -- | -- | -- | -- | --
 animation | Object | - | 动画效果设置。其中 duration 表示动画时长。TS 类型：`TabAnimation` `type TabAnimation = { duration: number } & Record<string, any>`。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/tabs/type.ts) | N
 list | Array | - | 选项卡列表。TS 类型：`Array<TdTabPanelProps>` | N
-placement | String | top | 已废弃。选项卡位置。可选项：left/top | N
 showBottomLine | Boolean | true | 是否展示底部激活线条 | N
 size | String | medium | 组件尺寸。可选项：medium/large | N
 spaceEvenly | Boolean | true | 选项卡头部空间是否均分 | N
diff --git a/src/tabs/tabs.vue b/src/tabs/tabs.vue
index f21dd738..0042c268 100644
--- a/src/tabs/tabs.vue
+++ b/src/tabs/tabs.vue
@@ -62,7 +62,7 @@ import {
 import config from '../config';
 import TabsProps from './props';
 import TabNavItem from './tab-nav-item.vue';
-import { useVModel, useEmitEvent } from '../shared';
+import { useVModel } from '../shared';
 import CLASSNAMES from '../shared/constants';
 import TSticky from '../sticky';
 
@@ -75,7 +75,6 @@ export default defineComponent({
   props: TabsProps,
   emits: ['update:value', 'update:modelValue'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const placement = ref('top');
     const theme = computed(() => props.theme);
     const spaceEvenly = computed(() => props.spaceEvenly);
@@ -177,7 +176,7 @@ export default defineComponent({
         return false;
       }
       setCurrentValue(value, typeof label === 'function' ? label() : label);
-      emitEvent('click', value, typeof label === 'function' ? label() : label);
+      props.onClick?.(value, typeof label === 'function' ? label() : label);
       nextTick(() => {
         moveToActiveTab();
       });
@@ -186,7 +185,7 @@ export default defineComponent({
     const handlerScroll = (context: { scrollTop: number; isFixed: boolean }) => {
       const { scrollTop, isFixed } = context;
       if (props.stickyProps) {
-        emitEvent('scroll', scrollTop, isFixed);
+        props.onScroll?.(scrollTop, isFixed);
       }
     };
 
diff --git a/src/tabs/type.ts b/src/tabs/type.ts
index eb336384..a56fe2d5 100644
--- a/src/tabs/type.ts
+++ b/src/tabs/type.ts
@@ -16,11 +16,6 @@ export interface TdTabsProps {
    * 选项卡列表
    */
   list?: Array<TdTabPanelProps>;
-  /**
-   * 已废弃。选项卡位置
-   * @default top
-   */
-  placement?: 'left' | 'top';
   /**
    * 是否展示底部激活线条
    * @default true
diff --git a/src/tag/check-tag-props.ts b/src/tag/check-tag-props.ts
index db6bd245..6b6e0a5c 100644
--- a/src/tag/check-tag-props.ts
+++ b/src/tag/check-tag-props.ts
@@ -19,8 +19,6 @@ export default {
   },
   /** 标签选中的状态，默认风格（theme=default）才有选中态，非受控属性 */
   defaultChecked: Boolean,
-  /** 已废弃。标签是否可关闭 */
-  closable: Boolean,
   /** 组件子元素；传入数组时：[选中内容，非选中内容] */
   content: {
     type: [String, Number, Array, Function] as PropType<TdCheckTagProps['content']>,
diff --git a/src/tag/check-tag.vue b/src/tag/check-tag.vue
index ce31728c..8ceaaefd 100644
--- a/src/tag/check-tag.vue
+++ b/src/tag/check-tag.vue
@@ -9,18 +9,14 @@
       </template>
       <t-node v-else :content="tagContent"></t-node>
     </span>
-    <span v-if="closable && !disabled" :class="`${baseClass}__icon-close`" @click="onClickClose">
-      <close-icon />
-    </span>
   </span>
 </template>
 
 <script lang="ts">
-import { CloseIcon } from 'tdesign-icons-vue-next';
 import { defineComponent, computed, toRefs, getCurrentInstance } from 'vue';
 import config from '../config';
 import CheckTagProps from './check-tag-props';
-import { useEmitEvent, renderContent, renderTNode, TNode, useVModel } from '../shared';
+import { renderContent, renderTNode, TNode, useVModel } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-check-tag`;
@@ -28,13 +24,11 @@ const name = `${prefix}-check-tag`;
 const CheckTag = defineComponent({
   name,
   components: {
-    CloseIcon,
     TNode,
   },
   props: CheckTagProps,
   emits: ['change', 'click', 'update:checked', 'update:modelValue'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     const internalInstance = getCurrentInstance();
     const tagContent = computed(() => renderContent(internalInstance, 'default', 'content'));
     const iconContent = computed(() => renderTNode(internalInstance, 'icon'));
@@ -64,21 +58,14 @@ const CheckTag = defineComponent({
       `${baseClass}--${props.size}`,
       `${baseClass}--${props.variant}`,
       {
-        [`${prefix}-is-closable ${baseClass}--closable`]: props.closable,
         [`${prefix}-is-disabled ${baseClass}--disabled`]: props.disabled,
         [`${prefix}-is-checked ${baseClass}--checked`]: !props.disabled && innerChecked.value,
       },
     ]);
 
-    const onClickClose = (e: MouseEvent): void => {
-      if (!props.disabled) {
-        emitEvent('close', { e });
-      }
-    };
-
     const handleClick = (e: MouseEvent) => {
       if (!props.disabled) {
-        emitEvent('click', { e });
+        props.onClick?.({ e });
         setInnerChecked(!innerChecked.value);
       }
     };
@@ -87,7 +74,6 @@ const CheckTag = defineComponent({
       contentIsArray,
       baseClass,
       classes,
-      onClickClose,
       handleClick,
       iconContent,
       tagContent,
diff --git a/src/tag/tag.vue b/src/tag/tag.vue
index 06801740..64ae1b18 100644
--- a/src/tag/tag.vue
+++ b/src/tag/tag.vue
@@ -17,7 +17,7 @@ import { CloseIcon } from 'tdesign-icons-vue-next';
 import { defineComponent, computed, getCurrentInstance } from 'vue';
 import config from '../config';
 import TagProps from './props';
-import { useEmitEvent, renderContent, renderTNode, TNode } from '../shared';
+import { renderContent, renderTNode, TNode } from '../shared';
 
 const { prefix } = config;
 const name = `${prefix}-tag`;
@@ -30,8 +30,7 @@ const Tag = defineComponent({
   },
   props: TagProps,
   emits: ['close', 'click'],
-  setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
+  setup(props) {
     const internalInstance = getCurrentInstance();
     const tagContent = computed(() => renderContent(internalInstance, 'default', 'content'));
     const iconContent = computed(() => renderTNode(internalInstance, 'icon'));
@@ -57,13 +56,16 @@ const Tag = defineComponent({
 
     const onClickClose = (e: MouseEvent): void => {
       if (!props.disabled) {
-        emitEvent('close', { e });
+        console.log(1);
+        console.log(props.onClose);
+
+        props.onClose?.({ e });
       }
     };
 
     const handleClick = (e: MouseEvent): void => {
       if (!props.disabled) {
-        emitEvent('click', { e });
+        props.onClick?.({ e });
       }
     };
 
diff --git a/src/tag/type.ts b/src/tag/type.ts
index 184a61c9..16c78a29 100644
--- a/src/tag/type.ts
+++ b/src/tag/type.ts
@@ -76,12 +76,6 @@ export interface TdCheckTagProps {
    * 标签选中的状态，默认风格（theme=default）才有选中态
    */
   modelValue?: boolean;
-  /**
-   * 标签是否可关闭
-   * @default false
-   * @deprecated
-   */
-  closable?: boolean;
   /**
    * 组件子元素；传入数组时：[选中内容，非选中内容]
    */
diff --git a/src/textarea/textarea.vue b/src/textarea/textarea.vue
index 07ada656..cdcbb70a 100644
--- a/src/textarea/textarea.vue
+++ b/src/textarea/textarea.vue
@@ -27,7 +27,7 @@
 
 <script lang="ts">
 import { computed, ref, onMounted, defineComponent, getCurrentInstance, toRefs, nextTick, watch } from 'vue';
-import { useEmitEvent, renderTNode, TNode, getCharacterLength, useVModel } from '../shared';
+import { renderTNode, TNode, getCharacterLength, useVModel } from '../shared';
 import config from '../config';
 import TextareaProps from './props';
 import { TdTextareaProps, TextareaValue } from './type';
@@ -45,7 +45,6 @@ export default defineComponent({
   props: TextareaProps,
   emits: ['update:value', 'update:modelValue', 'focus', 'blur', 'change'],
   setup(props, context) {
-    const emitEvent = useEmitEvent(props, context.emit);
     const disabled = useFormDisabled();
     const textareaRef = ref<null | HTMLElement>(null);
     const textareaStyle = ref();
@@ -124,10 +123,10 @@ export default defineComponent({
     };
 
     const handleFocus = (e: FocusEvent) => {
-      emitEvent('focus', innerValue.value, { e });
+      props.onFocus?.(innerValue.value, { e });
     };
     const handleBlur = (e: FocusEvent) => {
-      emitEvent('blur', innerValue.value, { e });
+      props.onBlur?.(innerValue.value, { e });
     };
 
     onMounted(() => {
diff --git a/src/upload/upload.vue b/src/upload/upload.vue
index 9b2905ea..592d5547 100644
--- a/src/upload/upload.vue
+++ b/src/upload/upload.vue
@@ -57,7 +57,7 @@ import isFunction from 'lodash/isFunction';
 import TImage from '../image';
 import TImageViewer from '../image-viewer';
 import xhr from '../_common/js/upload/xhr';
-import { useDefault, useEmitEvent, renderTNode, TNode } from '../shared';
+import { useDefault, renderTNode, TNode } from '../shared';
 import { TdUploadProps, UploadFile, RequestMethodResponse, SizeLimitObj } from './type';
 import { SuccessContext, InnerProgressContext } from './interface';
 import UploadProps from './props';
@@ -93,12 +93,11 @@ export default defineComponent({
     'select-change',
     'validate',
   ],
-  setup(props, context) {
+  setup(props, { emit }) {
     const disabled = useFormDisabled();
-    const emitEvent = useEmitEvent(props, context.emit);
     const [innerFiles, setInnerFiles] = useDefault<TdUploadProps['files'], TdUploadProps>(
       props,
-      context.emit,
+      emit,
       'files',
       'change',
     );
@@ -129,7 +128,7 @@ export default defineComponent({
 
     const handlePreview = (e: MouseEvent, file: UploadFile) => {
       showViewer.value = true;
-      emitEvent('preview', {
+      emit('preview', {
         e,
         file,
       });
@@ -144,7 +143,7 @@ export default defineComponent({
       if (disabled.value || !input || !input.files) return;
 
       const formatFiles = formatFileToUploadFile(input.files);
-      emitEvent('select-change', [...formatFiles]);
+      emit?.('select-change', [...formatFiles], { currentSelectedFiles: uploadedFiles.value });
       uploadFiles(formatFiles);
       input.value = '';
     };
@@ -176,7 +175,7 @@ export default defineComponent({
         if (props.sizeLimit) {
           const isOverSizeLimit = handleSizeLimit(file.size || 0);
           if (isOverSizeLimit) {
-            emitEvent('validate', { type: 'FILE_OVER_SIZE_LIMIT', files: [file] });
+            props.onValidate?.({ type: 'FILE_OVER_SIZE_LIMIT', files: [file] });
           }
           resolve(!handleSizeLimit(file.size || 0));
         }
@@ -231,7 +230,7 @@ export default defineComponent({
           } else {
             const isDuplicated = toUploadFiles.value.some((file) => file.name === uploadFile.name);
             if (isDuplicated) {
-              emitEvent('validate', {
+              props.onValidate?.({
                 type: 'FILTER_FILE_SAME_NAME',
                 files: [uploadFile],
               });
@@ -273,14 +272,14 @@ export default defineComponent({
         file,
         type,
       };
-      emitEvent('progress', progressCtx);
+      props.onProgress?.(progressCtx);
     };
 
     const handleRemove = (e: MouseEvent, file: UploadFile, index: number) => {
       const files = uploadedFiles.value.concat();
       files.splice(index, 1);
       setInnerFiles(files, { e, trigger: 'remove', index, file });
-      emitEvent('remove', { e, index, file });
+      emit('remove', { e, index, file });
       images.value.splice(index, 1);
     };
 
@@ -381,7 +380,7 @@ export default defineComponent({
       const newFile = { ...file, response: res };
       const files = uploadedFiles.value.concat(newFile as UploadFile);
       setInnerFiles(files, { e: event, response: res, trigger: 'upload-success' });
-      emitEvent('success', {
+      emit('success', {
         file,
         fileList: files,
         e: event,
@@ -410,7 +409,7 @@ export default defineComponent({
         const files = uploadedFiles.value.concat(file);
         setInnerFiles(files, { e: event, response: res, trigger: 'upload-fail' });
       }
-      emitEvent('fail', { e: event, file });
+      props.onFail?.({ e: event, file });
     };
 
     return {
@@ -427,7 +426,6 @@ export default defineComponent({
       uploadedFiles,
       defaultContent,
       addContent,
-      emitEvent,
       setInnerFiles,
       triggerUpload,
       handleChange,


Test patch:
diff --git a/src/back-top/__test__/index.test.jsx b/src/back-top/__test__/index.test.jsx
index 602945df..582a0214 100644
--- a/src/back-top/__test__/index.test.jsx
+++ b/src/back-top/__test__/index.test.jsx
@@ -81,9 +81,10 @@ describe('BackTop', () => {
 
   describe('events', () => {
     it(': to-top', async () => {
-      const wrapper = mount(BackTop);
+      const onToTop = vi.fn();
+      const wrapper = mount(<BackTop onToTop={onToTop} />);
       wrapper.find('.t-back-top').trigger('click');
-      expect(wrapper.emitted('to-top')).toHaveLength(1);
+      expect(onToTop).toBeCalledTimes(1);
     });
   });
 });
diff --git a/src/cascader/__test__/__snapshots__/demo.test.jsx.snap b/src/cascader/__test__/__snapshots__/demo.test.jsx.snap
index 2a236c62..fc387684 100644
--- a/src/cascader/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/cascader/__test__/__snapshots__/demo.test.jsx.snap
@@ -147,7 +147,7 @@ exports[`Cascader > Cascader baseVue demo works fine 1`] = `
                 >
                   
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -156,7 +156,7 @@ exports[`Cascader > Cascader baseVue demo works fine 1`] = `
                       value="110000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -181,7 +181,7 @@ exports[`Cascader > Cascader baseVue demo works fine 1`] = `
                     <!--v-if-->
                   </div>
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -190,7 +190,7 @@ exports[`Cascader > Cascader baseVue demo works fine 1`] = `
                       value="120000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -377,7 +377,7 @@ exports[`Cascader > Cascader keysVue demo works fine 1`] = `
                 >
                   
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -386,7 +386,7 @@ exports[`Cascader > Cascader keysVue demo works fine 1`] = `
                       value="110000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -411,7 +411,7 @@ exports[`Cascader > Cascader keysVue demo works fine 1`] = `
                     <!--v-if-->
                   </div>
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -420,7 +420,7 @@ exports[`Cascader > Cascader keysVue demo works fine 1`] = `
                       value="120000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -638,7 +638,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                     >
                       
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -647,7 +647,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="110000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -672,7 +672,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                         <!--v-if-->
                       </div>
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -681,7 +681,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="120000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -851,7 +851,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                     >
                       
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -860,7 +860,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="110000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -885,7 +885,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                         <!--v-if-->
                       </div>
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -894,7 +894,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="120000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1098,7 +1098,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                     >
                       
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -1107,7 +1107,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="110000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1132,7 +1132,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                         <!--v-if-->
                       </div>
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -1141,7 +1141,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="120000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1341,7 +1341,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                     >
                       
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -1350,7 +1350,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="110000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1375,7 +1375,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                         <!--v-if-->
                       </div>
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -1384,7 +1384,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="120000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1588,7 +1588,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                     >
                       
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -1597,7 +1597,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="110000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1622,7 +1622,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                         <!--v-if-->
                       </div>
                       <div
-                        class="t-radio t-radio--right t-radio--block"
+                        class="t-radio t-radio--left t-radio--block"
                       >
                         <input
                           class="t-radio__original"
@@ -1631,7 +1631,7 @@ exports[`Cascader > Cascader mobileVue demo works fine 1`] = `
                           value="120000"
                         />
                         <div
-                          class="t-radio__icon t-radio__icon--right"
+                          class="t-radio__icon t-radio__icon--left"
                         >
                           <!--v-if-->
                           <!--v-if-->
@@ -1791,7 +1791,7 @@ exports[`Cascader > Cascader themeTabVue demo works fine 1`] = `
                 >
                   
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -1800,7 +1800,7 @@ exports[`Cascader > Cascader themeTabVue demo works fine 1`] = `
                       value="110000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -1825,7 +1825,7 @@ exports[`Cascader > Cascader themeTabVue demo works fine 1`] = `
                     <!--v-if-->
                   </div>
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -1834,7 +1834,7 @@ exports[`Cascader > Cascader themeTabVue demo works fine 1`] = `
                       value="120000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -2025,7 +2025,7 @@ exports[`Cascader > Cascader withTitleVue demo works fine 1`] = `
                 >
                   
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -2034,7 +2034,7 @@ exports[`Cascader > Cascader withTitleVue demo works fine 1`] = `
                       value="110000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -2059,7 +2059,7 @@ exports[`Cascader > Cascader withTitleVue demo works fine 1`] = `
                     <!--v-if-->
                   </div>
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -2068,7 +2068,7 @@ exports[`Cascader > Cascader withTitleVue demo works fine 1`] = `
                       value="120000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -2255,7 +2255,7 @@ exports[`Cascader > Cascader withValueVue demo works fine 1`] = `
                 >
                   
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -2264,7 +2264,7 @@ exports[`Cascader > Cascader withValueVue demo works fine 1`] = `
                       value="110000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
@@ -2289,7 +2289,7 @@ exports[`Cascader > Cascader withValueVue demo works fine 1`] = `
                     <!--v-if-->
                   </div>
                   <div
-                    class="t-radio t-radio--right t-radio--block"
+                    class="t-radio t-radio--left t-radio--block"
                   >
                     <input
                       class="t-radio__original"
@@ -2298,7 +2298,7 @@ exports[`Cascader > Cascader withValueVue demo works fine 1`] = `
                       value="120000"
                     />
                     <div
-                      class="t-radio__icon t-radio__icon--right"
+                      class="t-radio__icon t-radio__icon--left"
                     >
                       <!--v-if-->
                       <!--v-if-->
diff --git a/src/cascader/__test__/__snapshots__/index.test.jsx.snap b/src/cascader/__test__/__snapshots__/index.test.jsx.snap
index 7fcc8f81..7de0dc2d 100644
--- a/src/cascader/__test__/__snapshots__/index.test.jsx.snap
+++ b/src/cascader/__test__/__snapshots__/index.test.jsx.snap
@@ -98,7 +98,7 @@ exports[`cascader > events > : pick 1`] = `
               >
                 
                 <div
-                  class="t-radio t-radio--right t-radio--block"
+                  class="t-radio t-radio--left t-radio--block"
                 >
                   <input
                     class="t-radio__original"
@@ -107,7 +107,7 @@ exports[`cascader > events > : pick 1`] = `
                     value="110000"
                   />
                   <div
-                    class="t-radio__icon t-radio__icon--right"
+                    class="t-radio__icon t-radio__icon--left"
                   >
                     <!--v-if-->
                     <!--v-if-->
@@ -132,7 +132,7 @@ exports[`cascader > events > : pick 1`] = `
                   <!--v-if-->
                 </div>
                 <div
-                  class="t-radio t-radio--right t-radio--block"
+                  class="t-radio t-radio--left t-radio--block"
                 >
                   <input
                     class="t-radio__original"
@@ -141,7 +141,7 @@ exports[`cascader > events > : pick 1`] = `
                     value="120000"
                   />
                   <div
-                    class="t-radio__icon t-radio__icon--right"
+                    class="t-radio__icon t-radio__icon--left"
                   >
                     <!--v-if-->
                     <!--v-if-->
diff --git a/src/divider/__test__/__snapshots__/demo.test.jsx.snap b/src/divider/__test__/__snapshots__/demo.test.jsx.snap
index db5b3ceb..fdbc65a5 100644
--- a/src/divider/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/divider/__test__/__snapshots__/demo.test.jsx.snap
@@ -11,18 +11,14 @@ exports[`Divider > Divider baseVue demo works fine 1`] = `
     水平分割线
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--center"
     data-v-589c0842=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--center"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        <!---->
-      </div>
+      <!---->
     </div>
   </div>
   <div
@@ -32,48 +28,36 @@ exports[`Divider > Divider baseVue demo works fine 1`] = `
     带文字水平分割线
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--left"
     data-v-589c0842=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--left"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        文字信息
-      </div>
+      文字信息
     </div>
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--center"
     data-v-589c0842=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--center"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        文字信息
-      </div>
+      文字信息
     </div>
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--right"
     data-v-589c0842=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--right"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        文字信息
-      </div>
+      文字信息
     </div>
   </div>
   <div
@@ -93,18 +77,14 @@ exports[`Divider > Divider baseVue demo works fine 1`] = `
       文字信息
     </span>
     <div
-      class="t-divider--vertical-center"
+      class="t-divider t-divider--vertical t-divider--center"
       data-v-589c0842=""
       role="separator"
     >
       <div
-        class="t-divider t-divider--vertical t-divider--center"
+        class="t-divider__content"
       >
-        <div
-          class="t-divider__content"
-        >
-          <!---->
-        </div>
+        <!---->
       </div>
     </div>
     <span
@@ -113,18 +93,14 @@ exports[`Divider > Divider baseVue demo works fine 1`] = `
       文字信息
     </span>
     <div
-      class="t-divider--vertical-center"
+      class="t-divider t-divider--vertical t-divider--center"
       data-v-589c0842=""
       role="separator"
     >
       <div
-        class="t-divider t-divider--vertical t-divider--center"
+        class="t-divider__content"
       >
-        <div
-          class="t-divider__content"
-        >
-          <!---->
-        </div>
+        <!---->
       </div>
     </div>
     <span
@@ -182,18 +158,14 @@ exports[`Divider > Divider mobileVue demo works fine 1`] = `
           水平分割线
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--center"
           data-v-589c0842=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--center"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              <!---->
-            </div>
+            <!---->
           </div>
         </div>
         <div
@@ -203,48 +175,36 @@ exports[`Divider > Divider mobileVue demo works fine 1`] = `
           带文字水平分割线
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--left"
           data-v-589c0842=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--left"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              文字信息
-            </div>
+            文字信息
           </div>
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--center"
           data-v-589c0842=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--center"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              文字信息
-            </div>
+            文字信息
           </div>
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--right"
           data-v-589c0842=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--right"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              文字信息
-            </div>
+            文字信息
           </div>
         </div>
         <div
@@ -264,18 +224,14 @@ exports[`Divider > Divider mobileVue demo works fine 1`] = `
             文字信息
           </span>
           <div
-            class="t-divider--vertical-center"
+            class="t-divider t-divider--vertical t-divider--center"
             data-v-589c0842=""
             role="separator"
           >
             <div
-              class="t-divider t-divider--vertical t-divider--center"
+              class="t-divider__content"
             >
-              <div
-                class="t-divider__content"
-              >
-                <!---->
-              </div>
+              <!---->
             </div>
           </div>
           <span
@@ -284,18 +240,14 @@ exports[`Divider > Divider mobileVue demo works fine 1`] = `
             文字信息
           </span>
           <div
-            class="t-divider--vertical-center"
+            class="t-divider t-divider--vertical t-divider--center"
             data-v-589c0842=""
             role="separator"
           >
             <div
-              class="t-divider t-divider--vertical t-divider--center"
+              class="t-divider__content"
             >
-              <div
-                class="t-divider__content"
-              >
-                <!---->
-              </div>
+              <!---->
             </div>
           </div>
           <span
@@ -337,63 +289,47 @@ exports[`Divider > Divider mobileVue demo works fine 1`] = `
           虚线样式
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
           data-v-127b67ab=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              <!---->
-            </div>
+            <!---->
           </div>
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--left t-divider--dashed"
           data-v-127b67ab=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--left t-divider--dashed"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              文字信息
-            </div>
+            文字信息
           </div>
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
           data-v-127b67ab=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              文字信息
-            </div>
+            文字信息
           </div>
         </div>
         <div
-          class=""
+          class="t-divider t-divider--horizontal t-divider--right t-divider--dashed"
           data-v-127b67ab=""
           role="separator"
         >
           <div
-            class="t-divider t-divider--horizontal t-divider--right t-divider--dashed"
+            class="t-divider__content"
           >
-            <div
-              class="t-divider__content"
-            >
-              文字信息
-            </div>
+            文字信息
           </div>
         </div>
       </div>
@@ -414,63 +350,47 @@ exports[`Divider > Divider themeVue demo works fine 1`] = `
     虚线样式
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
     data-v-127b67ab=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        <!---->
-      </div>
+      <!---->
     </div>
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--left t-divider--dashed"
     data-v-127b67ab=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--left t-divider--dashed"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        文字信息
-      </div>
+      文字信息
     </div>
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
     data-v-127b67ab=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--center t-divider--dashed"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        文字信息
-      </div>
+      文字信息
     </div>
   </div>
   <div
-    class=""
+    class="t-divider t-divider--horizontal t-divider--right t-divider--dashed"
     data-v-127b67ab=""
     role="separator"
   >
     <div
-      class="t-divider t-divider--horizontal t-divider--right t-divider--dashed"
+      class="t-divider__content"
     >
-      <div
-        class="t-divider__content"
-      >
-        文字信息
-      </div>
+      文字信息
     </div>
   </div>
 </div>
diff --git a/src/divider/__test__/index.test.jsx b/src/divider/__test__/index.test.jsx
index ced96773..722a03a1 100644
--- a/src/divider/__test__/index.test.jsx
+++ b/src/divider/__test__/index.test.jsx
@@ -64,12 +64,8 @@ describe('divider.vue', async () => {
       // layout = 'vertical'
       expect($divider.find(`.${name}`).classes()).toContain(`${name}--${layout}`);
     });
-
-    it(': lineColor', async () => {
-      const wrapper = mount(() => <Divider lineColor="red" />);
-      expect(getComputedStyle(wrapper.element.firstChild, null).borderColor).toBe('red');
-    });
   });
+
   describe('slot', async () => {
     it(': content', async () => {
       const wrapper = mount({
diff --git a/src/drawer/__test__/index.test.jsx b/src/drawer/__test__/index.test.jsx
index bc7387de..4ea6f006 100644
--- a/src/drawer/__test__/index.test.jsx
+++ b/src/drawer/__test__/index.test.jsx
@@ -37,10 +37,7 @@ describe('drawer', () => {
     });
 
     it(': closeOnOverlayClick', async () => {
-      let triggerOrigin = {};
-      const onClose = ({ trigger }) => {
-        triggerOrigin = trigger;
-      };
+      const onClose = vi.fn();
       const closeOverlayClick = ref(true);
 
       const visible = ref(true);
@@ -54,7 +51,7 @@ describe('drawer', () => {
       expect($overlay.exists()).toBeTruthy();
 
       $overlay.find('.t-overlay').trigger('click');
-      expect(triggerOrigin).toBe('overlay');
+      expect(onClose).toBeCalledWith('overlay');
     });
 
     it(': items', async () => {
diff --git a/src/form/__test__/__snapshots__/demo.test.jsx.snap b/src/form/__test__/__snapshots__/demo.test.jsx.snap
index c0ea42c8..35799f92 100644
--- a/src/form/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/form/__test__/__snapshots__/demo.test.jsx.snap
@@ -924,7 +924,7 @@ exports[`Form > Form horizontalVue demo works fine 1`] = `
                         >
                           
                           <div
-                            class="t-radio t-radio--right t-radio--block"
+                            class="t-radio t-radio--left t-radio--block"
                           >
                             <input
                               class="t-radio__original"
@@ -933,7 +933,7 @@ exports[`Form > Form horizontalVue demo works fine 1`] = `
                               value="110000"
                             />
                             <div
-                              class="t-radio__icon t-radio__icon--right"
+                              class="t-radio__icon t-radio__icon--left"
                             >
                               <!--v-if-->
                               <!--v-if-->
@@ -958,7 +958,7 @@ exports[`Form > Form horizontalVue demo works fine 1`] = `
                             <!--v-if-->
                           </div>
                           <div
-                            class="t-radio t-radio--right t-radio--block"
+                            class="t-radio t-radio--left t-radio--block"
                           >
                             <input
                               class="t-radio__original"
@@ -967,7 +967,7 @@ exports[`Form > Form horizontalVue demo works fine 1`] = `
                               value="120000"
                             />
                             <div
-                              class="t-radio__icon t-radio__icon--right"
+                              class="t-radio__icon t-radio__icon--left"
                             >
                               <!--v-if-->
                               <!--v-if-->
@@ -2675,7 +2675,7 @@ exports[`Form > Form mobileVue demo works fine 1`] = `
                               >
                                 
                                 <div
-                                  class="t-radio t-radio--right t-radio--block"
+                                  class="t-radio t-radio--left t-radio--block"
                                 >
                                   <input
                                     class="t-radio__original"
@@ -2684,7 +2684,7 @@ exports[`Form > Form mobileVue demo works fine 1`] = `
                                     value="110000"
                                   />
                                   <div
-                                    class="t-radio__icon t-radio__icon--right"
+                                    class="t-radio__icon t-radio__icon--left"
                                   >
                                     <!--v-if-->
                                     <!--v-if-->
@@ -2709,7 +2709,7 @@ exports[`Form > Form mobileVue demo works fine 1`] = `
                                   <!--v-if-->
                                 </div>
                                 <div
-                                  class="t-radio t-radio--right t-radio--block"
+                                  class="t-radio t-radio--left t-radio--block"
                                 >
                                   <input
                                     class="t-radio__original"
@@ -2718,7 +2718,7 @@ exports[`Form > Form mobileVue demo works fine 1`] = `
                                     value="120000"
                                   />
                                   <div
-                                    class="t-radio__icon t-radio__icon--right"
+                                    class="t-radio__icon t-radio__icon--left"
                                   >
                                     <!--v-if-->
                                     <!--v-if-->
@@ -4286,7 +4286,7 @@ exports[`Form > Form verticalVue demo works fine 1`] = `
                         >
                           
                           <div
-                            class="t-radio t-radio--right t-radio--block"
+                            class="t-radio t-radio--left t-radio--block"
                           >
                             <input
                               class="t-radio__original"
@@ -4295,7 +4295,7 @@ exports[`Form > Form verticalVue demo works fine 1`] = `
                               value="110000"
                             />
                             <div
-                              class="t-radio__icon t-radio__icon--right"
+                              class="t-radio__icon t-radio__icon--left"
                             >
                               <!--v-if-->
                               <!--v-if-->
@@ -4320,7 +4320,7 @@ exports[`Form > Form verticalVue demo works fine 1`] = `
                             <!--v-if-->
                           </div>
                           <div
-                            class="t-radio t-radio--right t-radio--block"
+                            class="t-radio t-radio--left t-radio--block"
                           >
                             <input
                               class="t-radio__original"
@@ -4329,7 +4329,7 @@ exports[`Form > Form verticalVue demo works fine 1`] = `
                               value="120000"
                             />
                             <div
-                              class="t-radio__icon t-radio__icon--right"
+                              class="t-radio__icon t-radio__icon--left"
                             >
                               <!--v-if-->
                               <!--v-if-->
diff --git a/src/navbar/__test__/__snapshots__/demo.test.jsx.snap b/src/navbar/__test__/__snapshots__/demo.test.jsx.snap
index 33d85a16..51096c2a 100644
--- a/src/navbar/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/navbar/__test__/__snapshots__/demo.test.jsx.snap
@@ -141,18 +141,14 @@ exports[`Navbar > Navbar baseVue demo works fine 1`] = `
               />
             </svg>
             <div
-              class="t-divider--vertical-center"
+              class="t-divider t-divider--vertical t-divider--center"
               data-v-7388e034=""
               role="separator"
             >
               <div
-                class="t-divider t-divider--vertical t-divider--center"
+                class="t-divider__content"
               >
-                <div
-                  class="t-divider__content"
-                >
-                  <!---->
-                </div>
+                <!---->
               </div>
             </div>
             <svg
@@ -655,18 +651,14 @@ exports[`Navbar > Navbar mobileVue demo works fine 1`] = `
                   />
                 </svg>
                 <div
-                  class="t-divider--vertical-center"
+                  class="t-divider t-divider--vertical t-divider--center"
                   data-v-7388e034=""
                   role="separator"
                 >
                   <div
-                    class="t-divider t-divider--vertical t-divider--center"
+                    class="t-divider__content"
                   >
-                    <div
-                      class="t-divider__content"
-                    >
-                      <!---->
-                    </div>
+                    <!---->
                   </div>
                 </div>
                 <svg
diff --git a/src/search/__test__/__snapshots__/demo.test.jsx.snap b/src/search/__test__/__snapshots__/demo.test.jsx.snap
index e11b50c5..5bb58745 100644
--- a/src/search/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/search/__test__/__snapshots__/demo.test.jsx.snap
@@ -35,12 +35,7 @@ exports[`Search > Search actionVue demo works fine 1`] = `
       <!--v-if-->
     </div>
     
-    <div
-      class="t-search__search-action"
-      style="display: none;"
-    >
-      取消
-    </div>
+    <!--v-if-->
     
   </div>
 </div>
@@ -171,7 +166,7 @@ exports[`Search > Search mobileVue demo works fine 1`] = `
       <p
         class="tdesign-mobile-demo-block__summary tdesign-mobile-demo-block_subtitle"
       >
-        获取焦点后显示取消按钮
+        输入值后显示取消按钮
       </p>
     </div>
     <div
@@ -212,12 +207,7 @@ exports[`Search > Search mobileVue demo works fine 1`] = `
             <!--v-if-->
           </div>
           
-          <div
-            class="t-search__search-action"
-            style="display: none;"
-          >
-            取消
-          </div>
+          <!--v-if-->
           
         </div>
       </div>
diff --git a/src/search/__test__/index.test.jsx b/src/search/__test__/index.test.jsx
index 1a6f3d8a..cb473240 100644
--- a/src/search/__test__/index.test.jsx
+++ b/src/search/__test__/index.test.jsx
@@ -129,8 +129,8 @@ describe('search', () => {
       const onActionClick = vi.fn();
       const wrapper = mount(Search, {
         props: {
+          value: 'test',
           action: '取消',
-          focus: true,
           onActionClick,
         },
       });
diff --git a/src/stepper/__test__/index.test.jsx b/src/stepper/__test__/index.test.jsx
index 6f66c03a..5b0bb735 100644
--- a/src/stepper/__test__/index.test.jsx
+++ b/src/stepper/__test__/index.test.jsx
@@ -77,13 +77,13 @@ describe('stepper', () => {
       await nextTick();
       // value <= min，change 触发， value = min
       expect(onChange).toBeCalledTimes(1);
-      expect(value.value).toBe(1);
+      expect(Number(value.value)).toBe(1);
       expect(minusIcon.classes()).toContain('t-stepper--normal-disabled');
 
       await plusIcon.trigger('click');
       await nextTick();
       expect(onChange).toBeCalledTimes(2);
-      expect(value.value).toBe(3);
+      expect(Number(value.value)).toBe(3);
 
       // 此时 value = max, plusIcon 为禁用态
       expect(plusIcon.classes()).toContain('t-stepper--normal-disabled');
@@ -121,14 +121,14 @@ describe('stepper', () => {
       await plusIcon.trigger('click');
       await nextTick();
       expect(onChange).toBeCalledTimes(1);
-      expect(value.value).toBe(5);
+      expect(Number(value.value)).toBe(5);
 
       // 此时 value = max，不触发 change，但触发 overlimit
       await plusIcon.trigger('click');
       await nextTick();
       expect(onChange).toBeCalledTimes(1);
       expect(onOverlimit).toBeCalledTimes(1);
-      expect(value.value).toBe(5);
+      expect(Number(value.value)).toBe(5);
 
       /**
        * minusIcon
@@ -136,7 +136,7 @@ describe('stepper', () => {
       // 此时 value = 5，
       await minusIcon.trigger('click');
       await nextTick();
-      expect(value.value).toBe(0);
+      expect(Number(value.value)).toBe(0);
       expect(onChange).toBeCalledTimes(2);
 
       //此时 value = min， 不触发 change，但触发 overlimit
diff --git a/src/tag/__test__/__snapshots__/demo.test.jsx.snap b/src/tag/__test__/__snapshots__/demo.test.jsx.snap
index 6c7dc6ca..bd7232d0 100644
--- a/src/tag/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/tag/__test__/__snapshots__/demo.test.jsx.snap
@@ -37,7 +37,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             未选中态
             
           </span>
-          <!--v-if-->
         </span>
         <span
           aria-disabled="false"
@@ -56,7 +55,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             已选中态
             
           </span>
-          <!--v-if-->
         </span>
       </div>
       <div
@@ -84,7 +82,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             未选中态
             
           </span>
-          <!--v-if-->
         </span>
         <span
           aria-disabled="false"
@@ -103,7 +100,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             已选中态
             
           </span>
-          <!--v-if-->
         </span>
       </div>
       <div
@@ -131,7 +127,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             未选中态
             
           </span>
-          <!--v-if-->
         </span>
         <span
           aria-disabled="false"
@@ -150,7 +145,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             已选中态
             
           </span>
-          <!--v-if-->
         </span>
       </div>
       <div
@@ -178,7 +172,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             未选中态
             
           </span>
-          <!--v-if-->
         </span>
         <span
           aria-disabled="false"
@@ -197,7 +190,6 @@ exports[`Tag > Tag checkableVue demo works fine 1`] = `
             已选中态
             
           </span>
-          <!--v-if-->
         </span>
       </div>
       
@@ -645,7 +637,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   未选中态
                   
                 </span>
-                <!--v-if-->
               </span>
               <span
                 aria-disabled="false"
@@ -664,7 +655,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   已选中态
                   
                 </span>
-                <!--v-if-->
               </span>
             </div>
             <div
@@ -692,7 +682,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   未选中态
                   
                 </span>
-                <!--v-if-->
               </span>
               <span
                 aria-disabled="false"
@@ -711,7 +700,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   已选中态
                   
                 </span>
-                <!--v-if-->
               </span>
             </div>
             <div
@@ -739,7 +727,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   未选中态
                   
                 </span>
-                <!--v-if-->
               </span>
               <span
                 aria-disabled="false"
@@ -758,7 +745,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   已选中态
                   
                 </span>
-                <!--v-if-->
               </span>
             </div>
             <div
@@ -786,7 +772,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   未选中态
                   
                 </span>
-                <!--v-if-->
               </span>
               <span
                 aria-disabled="false"
@@ -805,7 +790,6 @@ exports[`Tag > Tag mobileVue demo works fine 1`] = `
                   已选中态
                   
                 </span>
-                <!--v-if-->
               </span>
             </div>
             
diff --git a/src/tag/__test__/index.test.jsx b/src/tag/__test__/index.test.jsx
index 9341817c..10dd44d1 100644
--- a/src/tag/__test__/index.test.jsx
+++ b/src/tag/__test__/index.test.jsx
@@ -86,14 +86,15 @@ describe('Tag.vue', async () => {
   });
 
   it('close render', async () => {
-    const wrapper = mount(() => <Tag closable>{TEXT}</Tag>);
+    const onClose = vi.fn();
+    const wrapper = mount(() => <Tag closable onClose={onClose}>{TEXT}</Tag>);
     const com = wrapper.findComponent(Tag);
     expect(wrapper.classes()).toContain('t-tag--closable');
     const closeBtn = com.find('.t-tag__icon-close');
     expect(closeBtn.exists()).toBeTruthy();
 
     await closeBtn.trigger('click');
-    expect(com.emitted('close')).toBeTruthy();
+    expect(onClose).toBeCalledWith({ e: expect.any(MouseEvent)});
   });
 });
 
@@ -113,16 +114,6 @@ describe('CheckTag render', async () => {
     expect(wrapper.classes()).toContain('t-tag--checked');
   });
 
-  it('close render', async () => {
-    const wrapper = mount(() => <TCheckTag closable>{TEXT}</TCheckTag>);
-    const com = wrapper.findComponent(TCheckTag);
-    expect(wrapper.classes()).toContain('t-tag--closable');
-    const closeBtn = com.find('.t-tag__icon-close');
-    expect(closeBtn.exists()).toBeTruthy();
-    await closeBtn.trigger('click');
-    expect(com.emitted('close')).toBeTruthy();
-  });
-
   it('checked render', async () => {
     const wrapper = mount(() => <TCheckTag modelValue={true}>{TEXT}</TCheckTag>);
     expect(wrapper.classes()).toContain('t-tag--checked');