Repository: Tencent/tdesign-mobile-vue
Pull request #455 (2022-11-02 12:34:48)

test(Overlay): improve unit test coverage
<!--
首先，感谢你的贡献！😄
请阅读并遵循 [TDesign 贡献指南](https://github.com/Tencent/tdesign/blob/main/docs/contributing.md)，填写以下 pull request 的信息。
PR 在维护者审核通过后会合并，谢谢！
-->

### 🤔 这个 PR 的性质是？

- [x] 测试用例


### 📝 更新日志

<!--
从用户角度描述具体变化，以及可能的 breaking change 和其他风险。
-->

- test(Overlay): 提升组件单元测试覆盖率


github-actions[bot]: **Size Change:** 0 B 

**Total Size:** 378 kB



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

| Filename | Size |
| :--- | :---: |
| `./dist/tdesign.css` | 24.5 kB |
| `./dist/tdesign.js` | 225 kB |
| `./dist/tdesign.min.css` | 22.7 kB |
| `./dist/tdesign.min.js` | 106 kB |

</details>


github-actions[bot]: <a href="https://preview-pr455-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/3377627180) -->
<!-- AUTO_PREVIEW_HOOK -->

Patch:
diff --git a/src/calendar/calendar.vue b/src/calendar/calendar.vue
index 493e5358..000fe823 100644
--- a/src/calendar/calendar.vue
+++ b/src/calendar/calendar.vue
@@ -45,7 +45,7 @@
         </div>
         <div class="t-calendar__footer">
           <slot name="confirmBtn">
-            <template v-if="confirmBtn">
+            <template v-if="confirmBtn && typeof confirmBtn === 'object'">
               <t-button block theme="primary" v-bind="confirmBtn" @click="handleConfirm" />
             </template>
           </slot>
@@ -58,7 +58,8 @@
 <script lang="ts">
 import { defineEmits, defineProps, computed, watch, ref, toRaw } from 'vue';
 import { CloseIcon } from 'tdesign-icons-vue-next';
-
+import TPopup from '../popup';
+import TButton from '../button';
 import config from '../config';
 import calendarProps from './props';
 import { TDate, TDateType } from './type';
@@ -68,6 +69,10 @@ const name = `${prefix}-calendar`;
 
 export default {
   name,
+  components: {
+    TPopup,
+    TButton,
+  },
 };
 </script>
 
@@ -226,7 +231,7 @@ const months = computed(() => {
 });
 
 const confirmBtn = computed(() => {
-  if (props.confirmBtn === 'string' || props.confirmBtn === '') return { content: props.confirmBtn || '确认' };
+  if (typeof props.confirmBtn === 'string' || props.confirmBtn === '') return { content: props.confirmBtn || '确认' };
   return props.confirmBtn;
 });
 
diff --git a/src/cascader/demos/base.vue b/src/cascader/demos/base.vue
index 6838a655..e2aa0596 100644
--- a/src/cascader/demos/base.vue
+++ b/src/cascader/demos/base.vue
@@ -1,14 +1,7 @@
 <template>
   <t-input :value="address" readonly label="地址" placeholder="选择地址" @click="show = true" />
   <t-popup v-model="show" placement="bottom">
-    <t-cascader
-      v-model="address"
-      title="选择地址"
-      :options="options"
-      @change="onChange"
-      @cancel="show = false"
-      @confirm="onConfirm"
-    />
+    <t-cascader v-model="address" title="选择地址" :options="options" @change="onChange" @cancel="show = false" />
   </t-popup>
 </template>
 
@@ -23,10 +16,6 @@ const onChange = (value: string, options: TdCascaderItems) => {
   show.value = false;
 };
 
-const onConfirm = () => {
-  console.log('confirm');
-};
-
 const options: TdCascaderItems = [
   {
     label: '广东省',
diff --git a/src/cascader/demos/status.vue b/src/cascader/demos/status.vue
index 633220ba..172fd135 100644
--- a/src/cascader/demos/status.vue
+++ b/src/cascader/demos/status.vue
@@ -1,18 +1,11 @@
 <template>
-  <t-input :value="address.address4" label="地址" placeholder="选择地址" readonly @click="show.show4 = true" />
-  <t-popup v-model="show.show4" placement="bottom">
-    <t-cascader
-      v-model="address.address4"
-      :options="provinces"
-      @change="onChange"
-      @cancel="show.show4 = false"
-      @confirm="onConfirm"
-    >
-    </t-cascader>
+  <t-input :value="address" label="地址" placeholder="选择地址" readonly @click="show = true" />
+  <t-popup v-model="show" placement="bottom">
+    <t-cascader v-model="address" :options="provinces" @change="onChange" @cancel="show = false" />
   </t-popup>
 </template>
 <script lang="ts" setup>
-import { reactive } from 'vue';
+import { ref } from 'vue';
 import { TdCascaderItems } from '../type';
 
 const provinces: TdCascaderItems = [
@@ -27,29 +20,11 @@ const provinces: TdCascaderItems = [
   },
 ];
 
-const show = reactive({
-  show1: false,
-  show2: false,
-  show3: false,
-  show4: false,
-});
+const show = ref(false);
+const address = ref('');
 
-const address = reactive({
-  address1: '',
-  address2: '',
-  address3: '',
-  address4: '',
-  address5: '',
-});
-
-const onChange = (value: TdCascaderItems, options: any) => {
-  show.show1 = false;
-  show.show2 = false;
-  show.show3 = false;
-  show.show4 = false;
-};
-
-const onConfirm = () => {
-  console.log('confirm');
+const onChange = (value: string, options: TdCascaderItems) => {
+  address.value = value;
+  show.value = false;
 };
 </script>
diff --git a/src/navbar/demos/base.vue b/src/navbar/demos/base.vue
index 64d4f81b..7c6d50cc 100644
--- a/src/navbar/demos/base.vue
+++ b/src/navbar/demos/base.vue
@@ -1,9 +1,9 @@
 <template>
-  <div>
-    <div class="tdesign-demo-block">
-      <t-navbar> 界面标题 </t-navbar>
-    </div>
-  </div>
+  <t-navbar title="标题" left-icon :fixed="false" @left-click="handleClick" />
 </template>
 
-<script lang="ts" setup></script>
+<script lang="ts" setup>
+const handleClick = () => {
+  console.log('left-click');
+};
+</script>
diff --git a/src/navbar/demos/event.vue b/src/navbar/demos/event.vue
deleted file mode 100644
index ab8fd6a7..00000000
--- a/src/navbar/demos/event.vue
+++ /dev/null
@@ -1,36 +0,0 @@
-<template>
-  <div>
-    <div class="tdesign-demo-block">
-      <t-navbar @click-text="onClickText" @click-right="onClickMore">
-        界面标题
-        <template #left>
-          <t-icon name="close" @click="onClickLeft"></t-icon>
-        </template>
-      </t-navbar>
-    </div>
-  </div>
-</template>
-
-<script lang="ts">
-import { defineComponent } from 'vue';
-
-export default defineComponent({
-  setup() {
-    return {};
-  },
-  methods: {
-    onClickMore() {
-      this.$toast('Click More');
-      console.log('on click more');
-    },
-    onClickText() {
-      this.$toast('Click Text');
-      console.log('on click Text');
-    },
-    onClickLeft() {
-      this.$toast('Click close');
-      console.log('on click left');
-    },
-  },
-});
-</script>
diff --git a/src/navbar/demos/icon.vue b/src/navbar/demos/icon.vue
deleted file mode 100644
index 1668e98a..00000000
--- a/src/navbar/demos/icon.vue
+++ /dev/null
@@ -1,16 +0,0 @@
-<template>
-  <div>
-    <div class="tdesign-demo-block">
-      <t-navbar>
-        导航栏 - 带icon
-        <template #right>
-          <sound-icon></sound-icon>
-        </template>
-      </t-navbar>
-    </div>
-  </div>
-</template>
-
-<script lang="ts" setup>
-import { SoundIcon } from 'tdesign-icons-vue-next';
-</script>
diff --git a/src/navbar/demos/left-icon.vue b/src/navbar/demos/left-icon.vue
new file mode 100644
index 00000000..c5c25836
--- /dev/null
+++ b/src/navbar/demos/left-icon.vue
@@ -0,0 +1,27 @@
+<template>
+  <t-navbar title="标题" :fixed="false">
+    <template #left-icon>
+      <t-icon size="24px" name="chevron-left" class="btn-left" @click="handleBack" />
+      <t-icon size="24px" name="close" @click="handleClose" />
+    </template>
+    <template #right-icon>
+      <t-icon size="24px" name="ellipsis" @click="handleShowMore" />
+    </template>
+  </t-navbar>
+</template>
+
+<script lang="ts" setup>
+import { Icon as TIcon } from 'tdesign-icons-vue-next';
+
+const handleBack = () => {
+  console.log('back-icon-click');
+};
+
+const handleClose = () => {
+  console.log('close-icon-click');
+};
+
+const handleShowMore = () => {
+  console.log('more-icon-click');
+};
+</script>
diff --git a/src/navbar/demos/mobile.vue b/src/navbar/demos/mobile.vue
index 7e4a49f7..f6420bb9 100644
--- a/src/navbar/demos/mobile.vue
+++ b/src/navbar/demos/mobile.vue
@@ -1,48 +1,25 @@
 <template>
-  <div class="t-navbar-base-demo">
-    <p class="divide-line"></p>
-    <t-navbar :right-show="false">界面标题</t-navbar>
-    <p class="divide-line"></p>
-    <t-navbar :right-show="false">
-      <template #left>
-        <t-icon size="24px" name="close" />
-      </template>
-      界面标题
-      <template #right>
-        <t-icon size="24px" name="app" />
-      </template>
-    </t-navbar>
-    <p class="divide-line"></p>
-    <t-navbar>
-      界面标题
-      <template #right>
-        <t-icon size="24px" name="app" />
-      </template>
-    </t-navbar>
+  <div class="tdesign-mobile-demo">
+    <h1 class="title">NavBar 导航栏</h1>
+    <p class="summary">用于不同页面之间切换或者跳转，位于内容区的上方，系统状态栏的下方。</p>
+    <tdesign-demo-block title="01 类型" summary="基础导航栏">
+      <base-demo />
+    </tdesign-demo-block>
+
+    <tdesign-demo-block summary="左侧双操作导航栏">
+      <left-icon-demo />
+    </tdesign-demo-block>
+
+    <tdesign-demo-block summary="右侧双操作导航栏">
+      <right-icon-demo />
+    </tdesign-demo-block>
   </div>
 </template>
 
-<script lang="ts">
-import { defineComponent } from 'vue';
-import { Icon as TIcon } from 'tdesign-icons-vue-next';
-import Toast from '../../toast/index';
-
-export default defineComponent({
-  components: { TIcon },
-  setup() {
-    return {};
-  },
-  methods: {
-    onClickMore() {
-      Toast('Click More');
-      console.log('on click more');
-    },
-    onClickLeft() {
-      Toast('Click close');
-      console.log('on click left');
-    },
-  },
-});
+<script lang="ts" setup>
+import baseDemo from './base.vue';
+import leftIconDemo from './left-icon.vue';
+import rightIconDemo from './right-icon.vue';
 </script>
 
 <style lang="less" scoped>
@@ -51,4 +28,8 @@ export default defineComponent({
   height: 24px;
   background: #f9f9f9;
 }
+
+.btn-left {
+  margin-right: 8px;
+}
 </style>
diff --git a/src/navbar/demos/right-icon.vue b/src/navbar/demos/right-icon.vue
new file mode 100644
index 00000000..d884e298
--- /dev/null
+++ b/src/navbar/demos/right-icon.vue
@@ -0,0 +1,24 @@
+<template>
+  <t-navbar title="标题" left-icon :fixed="false" @left-click="handleBack">
+    <template #right-icon>
+      <t-icon size="24px" name="notification" class="btn-left" @click="handleNotify" />
+      <t-icon size="24px" name="ellipsis" @click="handleShowMore" />
+    </template>
+  </t-navbar>
+</template>
+
+<script lang="ts" setup>
+import { Icon as TIcon } from 'tdesign-icons-vue-next';
+
+const handleBack = () => {
+  console.log('left-click');
+};
+
+const handleNotify = () => {
+  console.log('notification-icon-click');
+};
+
+const handleShowMore = () => {
+  console.log('more-icon-click');
+};
+</script>
diff --git a/src/navbar/navbar.en-US.md b/src/navbar/navbar.en-US.md
new file mode 100644
index 00000000..d49ce9db
--- /dev/null
+++ b/src/navbar/navbar.en-US.md
@@ -0,0 +1,25 @@
+:: BASE_DOC ::
+
+## API
+### Navbar Props
+
+name | type | default | description | required
+-- | -- | -- | -- | --
+animation | Boolean | true |【developing】 | N
+background | String | - | background | N
+fixed | Boolean | true | \- | N
+homeIcon | Boolean / Slot / Function | - | homeIcon。Typescript：`boolean \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+leftIcon | Boolean / Slot / Function | false | Typescript：`boolean \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+rightIcon | Slot / Function | - | Typescript：`TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+title | String / Slot / Function | - | page title。Typescript：`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+titleMaxLength | Number | - | \- | N
+visible | Boolean | true | \- | N
+onLeftClick | Function |  | Typescript：`(trigger: NavBarTrigger) => void`<br/>[see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/navbar/type.ts)。<br/>`type NavBarTrigger = 'home-icon' \| 'left-icon';`<br/> | N
+onRightClick | Function |  | Typescript：`() => void`<br/> | N
+
+### Navbar Events
+
+name | params | description
+-- | -- | --
+left-click | `(trigger: NavBarTrigger)` | [see more ts definition](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/navbar/type.ts)。<br/>`type NavBarTrigger = 'home-icon' \| 'left-icon';`<br/>
+right-click | \- | \-
diff --git a/src/navbar/navbar.md b/src/navbar/navbar.md
index 4c7b0968..b04106fe 100644
--- a/src/navbar/navbar.md
+++ b/src/navbar/navbar.md
@@ -1,25 +1,25 @@
 :: BASE_DOC ::
 
 ## API
+### Navbar Props
 
-| 属性      | 类型    | 默认值 | 必传 | 说明                        |
-| --------- | ------- | ------ | ---- | --------------------------- |
-| title     | String  | 导航栏 | N    | 标题文字                    |
-| leftArrow | Boolean | true   | N    | 是否展示左箭头              |
-| maxLen    | Number  | 6      | N    | 标题文字长度，多余展示`...` |
-| rightShow | Boolean  | true    | N    | 是否展示右侧更多按钮 |
+名称 | 类型 | 默认值 | 说明 | 必传
+-- | -- | -- | -- | --
+animation | Boolean | true | 【开发中】是否添加动画效果 | N
+background | String | - | 背景 | N
+fixed | Boolean | true | 是否固定在顶部 | N
+homeIcon | Boolean / Slot / Function | - | 首页图标。值为 true 表示显示默认返回图标，值为 false 表示不显示首页图标，值为其他表示自定义图标。TS 类型：`boolean \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+leftIcon | Boolean / Slot / Function | false | 左侧图标。值为 true 表示显示默认返回图标，值为 false 表示不显示左侧图标，值为其他表示自定义图标。TS 类型：`boolean \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+rightIcon | Slot / Function | - | 右侧图标，可自定义。TS 类型：`TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+title | String / Slot / Function | - | 页面标题。TS 类型：`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-mobile-vue/blob/develop/src/common.ts) | N
+titleMaxLength | Number | - | 标题文字最大长度，超出的范围使用 `...` 表示 | N
+visible | Boolean | true | 是否显示 | N
+onLeftClick | Function |  | TS 类型：`(trigger: NavBarTrigger) => void`<br/>点击左侧按钮时触发。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/navbar/type.ts)。<br/>`type NavBarTrigger = 'home-icon' \| 'left-icon';`<br/> | N
+onRightClick | Function |  | TS 类型：`() => void`<br/>点击右侧图标时触发 | N
 
-## Event
+### Navbar Events
 
-| 事件名      | 说明              | 回调参数 |
-| ----------- | ----------------- | -------- |
-| click-right | 点击右侧更多 icon | event    |
-| click-text  | 点击标题文字      | event    |
-
-## Slot
-
-| 插槽名 | 说明             |
-| ------ | ---------------- |
-| left   | 自定义左侧关闭按钮 |
-| right   | 自定义右侧icon |
-| -   | 默认title文案 |
+名称 | 参数 | 描述
+-- | -- | --
+left-click | `(trigger: NavBarTrigger)` | 点击左侧按钮时触发。[详细类型定义](https://github.com/Tencent/tdesign-mobile-vue/tree/develop/src/navbar/type.ts)。<br/>`type NavBarTrigger = 'home-icon' \| 'left-icon';`<br/>
+right-click | \- | 点击右侧图标时触发
diff --git a/src/navbar/navbar.vue b/src/navbar/navbar.vue
index daae45fe..b3885a95 100644
--- a/src/navbar/navbar.vue
+++ b/src/navbar/navbar.vue
@@ -1,82 +1,85 @@
 <template>
-  <div :class="name">
-    <div :class="`${name}__back`">
-      <span v-if="leftArrow" :class="`${name}__back--arrow`" @click="handleBack">
+  <div v-show="visible" :class="name" :style="`${navStyle}`">
+    <div v-if="homeContent || leftContent" :class="`${name}__back`">
+      <span v-if="leftIcon === true" :class="`${name}__back--arrow`" @click="handleLeftClick">
         <t-chevron-left-icon />
       </span>
-      <slot name="left"> </slot>
+      <span v-if="homeIcon === true" :class="`${name}__back--arrow`">
+        <t-home-icon />
+      </span>
+      <t-node :content="homeContent"></t-node>
+      <t-node :content="leftContent"></t-node>
     </div>
 
-    <div :class="`${name}__text`" @click="clickText">
-      <slot>{{ nTitleContent }}</slot>
+    <div :class="`${name}__text`">
+      <t-node :content="titleContent"></t-node>
     </div>
 
-    <div :class="`${name}__right`">
-      <slot name="right"> </slot>
-      <i v-if="rightShow" :class="`${name}__right--more`" @click="handleMore">
-        <svg
-          t="1614321969302"
-          class="icon"
-          viewBox="0 0 1024 1024"
-          version="1.1"
-          xmlns="http://www.w3.org/2000/svg"
-          p-id="2091"
-          width="24"
-          height="24"
-        >
-          <path
-            d="M512 449.749333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m-318.805333-1.109333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m638.677333 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"
-            fill="#444444"
-            p-id="2092"
-          ></path>
-        </svg>
-      </i>
+    <div :class="`${name}__right`" @click="handleRightClick">
+      <t-node :content="rightContent"></t-node>
     </div>
   </div>
 </template>
 <script lang="ts">
-import { computed, defineComponent, SetupContext } from 'vue';
-import { ChevronLeftIcon as TChevronLeftIcon } from 'tdesign-icons-vue-next';
+import { computed, defineComponent, getCurrentInstance, SetupContext, toRefs } from 'vue';
+import { ChevronLeftIcon as TChevronLeftIcon, HomeIcon as THomeIcon } from 'tdesign-icons-vue-next';
 import config from '../config';
-import { NavbarProps } from './navbar.interface';
+import { renderTNode, TNode, useEmitEvent } from '../shared';
+import NavbarProps from './props';
 
 const { prefix } = config;
 const name = `${prefix}-navbar`;
 
 export default defineComponent({
   name,
-  components: { TChevronLeftIcon },
+  components: { TChevronLeftIcon, TNode, THomeIcon },
   props: NavbarProps,
-  emits: ['click-right', 'click-text'],
+  emits: ['left-click', 'right-click'],
   setup(props, context: SetupContext) {
-    const nTitleContent = computed(() => {
-      const { title, maxLen } = props;
-      if (title && title.trim().length > maxLen) {
-        return `${title.slice(0, maxLen)}...`;
+    const internalInstance = getCurrentInstance();
+    const { title, titleMaxLength, fixed, background } = toRefs(props);
+
+    const navStyle = computed(
+      () => `position: ${fixed.value ? 'fixed' : 'relative'}; background: ${background.value || ''};`,
+    );
+
+    const titleContent = computed(() => {
+      if (titleMaxLength.value != null && title.value) {
+        if (titleMaxLength.value <= 0) {
+          console.warn('titleMaxLength must be greater than 0');
+        } else {
+          return title.value.length <= titleMaxLength.value
+            ? props.title
+            : `${(title.value as string).slice(0, titleMaxLength.value)}...`;
+        }
       }
-      return title;
+
+      return renderTNode(internalInstance, 'title');
     });
 
-    const handleBack = () => {
-      if (history.length > 1) {
-        history.back();
-      }
-    };
+    const leftContent = computed(() => renderTNode(internalInstance, 'left-icon'));
+    const rightContent = computed(() => renderTNode(internalInstance, 'right-icon'));
+    const homeContent = computed(() => renderTNode(internalInstance, 'home-icon'));
+
+    const emitEvent = useEmitEvent(props, context.emit);
 
-    const handleMore = (evt: MouseEvent) => {
-      context.emit('click-right', evt);
+    const handleLeftClick = () => {
+      emitEvent('left-click');
     };
 
-    const clickText = (evt: MouseEvent) => {
-      context.emit('click-text', evt);
+    const handleRightClick = () => {
+      emitEvent('right-click');
     };
 
     return {
       name,
-      nTitleContent,
-      handleBack,
-      handleMore,
-      clickText,
+      titleContent,
+      leftContent,
+      rightContent,
+      navStyle,
+      homeContent,
+      handleLeftClick,
+      handleRightClick,
     };
   },
 });
diff --git a/src/navbar/props.ts b/src/navbar/props.ts
new file mode 100644
index 00000000..346c2845
--- /dev/null
+++ b/src/navbar/props.ts
@@ -0,0 +1,56 @@
+/* eslint-disable */
+
+/**
+ * 该文件为脚本自动生成文件，请勿随意修改。如需修改请联系 PMC
+ * */
+
+import { TdNavbarProps } from './type';
+import { PropType } from 'vue';
+
+export default {
+  /** 是否添加动画效果 */
+  animation: {
+    type: Boolean,
+    default: true,
+  },
+  /** 背景 */
+  background: {
+    type: String,
+    default: '',
+  },
+  /** 是否固定在顶部 */
+  fixed: {
+    type: Boolean,
+    default: true,
+  },
+  /** 首页图标。值为 true 表示显示默认返回图标，值为 false 表示不显示首页图标，值为其他表示自定义图标 */
+  homeIcon: {
+    type: [Boolean, Function] as PropType<TdNavbarProps['homeIcon']>,
+  },
+  /** 左侧图标。值为 true 表示显示默认返回图标，值为 false 表示不显示左侧图标，值为其他表示自定义图标 */
+  leftIcon: {
+    type: [Boolean, Function] as PropType<TdNavbarProps['leftIcon']>,
+    default: false,
+  },
+  /** 右侧图标，可自定义 */
+  rightIcon: {
+    type: Function as PropType<TdNavbarProps['rightIcon']>,
+  },
+  /** 页面标题 */
+  title: {
+    type: [String, Function] as PropType<TdNavbarProps['title']>,
+  },
+  /** 标题文字最大长度，超出的范围使用 `...` 表示 */
+  titleMaxLength: {
+    type: Number,
+  },
+  /** 是否显示 */
+  visible: {
+    type: Boolean,
+    default: true,
+  },
+  /** 点击左侧按钮时触发 */
+  onLeftClick: Function as PropType<TdNavbarProps['onLeftClick']>,
+  /** 点击右侧图标时触发 */
+  onRightClick: Function as PropType<TdNavbarProps['onRightClick']>,
+};
diff --git a/src/navbar/type.ts b/src/navbar/type.ts
new file mode 100644
index 00000000..7a3fcb9a
--- /dev/null
+++ b/src/navbar/type.ts
@@ -0,0 +1,61 @@
+/* eslint-disable */
+
+/**
+ * 该文件为脚本自动生成文件，请勿随意修改。如需修改请联系 PMC
+ * */
+
+import { TNode } from '../common';
+
+export interface TdNavbarProps {
+  /**
+   * 是否添加动画效果
+   * @default true
+   */
+  animation?: boolean;
+  /**
+   * 背景
+   * @default ''
+   */
+  background?: string;
+  /**
+   * 是否固定在顶部
+   * @default true
+   */
+  fixed?: boolean;
+  /**
+   * 首页图标。值为 true 表示显示默认返回图标，值为 false 表示不显示首页图标，值为其他表示自定义图标
+   */
+  homeIcon?: boolean | TNode;
+  /**
+   * 左侧图标。值为 true 表示显示默认返回图标，值为 false 表示不显示左侧图标，值为其他表示自定义图标
+   * @default false
+   */
+  leftIcon?: boolean | TNode;
+  /**
+   * 右侧图标，可自定义
+   */
+  rightIcon?: TNode;
+  /**
+   * 页面标题
+   */
+  title?: string | TNode;
+  /**
+   * 标题文字最大长度，超出的范围使用 `...` 表示
+   */
+  titleMaxLength?: number;
+  /**
+   * 是否显示
+   * @default true
+   */
+  visible?: boolean;
+  /**
+   * 点击左侧按钮时触发
+   */
+  onLeftClick?: (trigger: NavBarTrigger) => void;
+  /**
+   * 点击右侧图标时触发
+   */
+  onRightClick?: () => void;
+}
+
+export type NavBarTrigger = 'home-icon' | 'left-icon';
diff --git a/src/slider/demos/base.vue b/src/slider/demos/base.vue
index aa14586e..f3ae9617 100644
--- a/src/slider/demos/base.vue
+++ b/src/slider/demos/base.vue
@@ -1,12 +1,11 @@
 <template>
-  <t-slider v-model="value" :label="label" :on-change="onChange" />
+  <t-slider v-model="value" :label="false" :on-change="onChange" />
 </template>
 
 <script lang="ts" setup>
 import { ref } from 'vue';
 
-const value = ref(60);
-const label = ref(false);
+const value = ref(0);
 const onChange = ($event: number | number[]) => {
   console.log(`change to ${$event}`);
 };
diff --git a/src/slider/demos/disable.vue b/src/slider/demos/disable.vue
index 29c442ea..f3a2fb2a 100644
--- a/src/slider/demos/disable.vue
+++ b/src/slider/demos/disable.vue
@@ -1,34 +1,3 @@
 <template>
-  <div class="section">
-    <t-slider :default-value="60" disabled />
-  </div>
-  <div class="section">
-    <t-slider :default-value="30" :min="30" disabled />
-  </div>
-  <div class="section">
-    <t-slider :default-value="50" :label="label" :marks="marks" disabled />
-  </div>
-  <div class="section">
-    <t-slider :default-value="[30, 70]" range disabled />
-  </div>
+  <t-slider :default-value="30" :min="30" disabled :label="false" />
 </template>
-
-<script lang="ts" setup>
-import { ref } from 'vue';
-
-const marks = ref({ 0: '小', 50: '中', 100: '大' });
-const label = ref(false);
-</script>
-
-<style lang="less" scoped>
-.section {
-  // display: flex;
-  align-items: center;
-  padding-top: 16px;
-  padding-bottom: 16px;
-
-  & + .section {
-    margin-top: 16px;
-  }
-}
-</style>
diff --git a/src/slider/demos/mark.vue b/src/slider/demos/mark.vue
index 04471ed0..01c3c53b 100644
--- a/src/slider/demos/mark.vue
+++ b/src/slider/demos/mark.vue
@@ -1,5 +1,5 @@
 <template>
-  <t-slider v-model="value" :label="label" :marks="marks" />
+  <t-slider v-model="value" :label="label" :marks="marks" :step="50" />
 </template>
 
 <script lang="ts" setup>
@@ -8,10 +8,4 @@ import { ref } from 'vue';
 const value = ref(50);
 const label = ref(false);
 const marks = ref({ 0: '小', 50: '中', 100: '大' });
-// const marks = ref([0, 50, 100]);
-// const marks = ref({
-//   0: (val: number) => `${val}%`,
-//   50: (val: number) => `${val}%`,
-//   100: (val: number) => `${val}%`,
-// });
 </script>
diff --git a/src/slider/demos/mobile.vue b/src/slider/demos/mobile.vue
index 2446a359..7a5c2d1a 100644
--- a/src/slider/demos/mobile.vue
+++ b/src/slider/demos/mobile.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="tdesign-mobile-demo">
     <h1 class="title">Slider 滑动选择器</h1>
-    <p class="summary">滑动滑块来选择一个数值，在具体场景中也可以增加来刻度和展示数值来方便用户使用</p>
+    <p class="summary">用于选择横轴上的数值、区间、档位</p>
     <tdesign-demo-block title="01 类型" summary="基础滑动选择器">
       <div class="slider-demo">
         <BaseDemo />
@@ -14,7 +14,7 @@
     </tdesign-demo-block>
     <tdesign-demo-block summary="起始非零滑动选择器">
       <div class="slider-demo">
-        <t-slider :default-value="30" :min="30"> </t-slider>
+        <UnZeroDemo />
       </div>
     </tdesign-demo-block>
     <tdesign-demo-block summary="带刻度滑动选择器">
@@ -32,6 +32,16 @@
         <DisableDemo />
       </div>
     </tdesign-demo-block>
+    <tdesign-demo-block title="03 规格" summary="无标题滑动选择器">
+      <div class="slider-demo">
+        <BaseDemo />
+      </div>
+    </tdesign-demo-block>
+    <tdesign-demo-block summary="有标题滑动选择器">
+      <div class="slider-demo">
+        <TitleDemo />
+      </div>
+    </tdesign-demo-block>
   </div>
 </template>
 
@@ -41,6 +51,8 @@ import MarkDemo from './mark.vue';
 import ValueDemo from './value.vue';
 import RangeDemo from './range.vue';
 import DisableDemo from './disable.vue';
+import UnZeroDemo from './unZero.vue';
+import TitleDemo from './title.vue';
 </script>
 
 <style scoped>
diff --git a/src/slider/demos/title.vue b/src/slider/demos/title.vue
new file mode 100644
index 00000000..2b88f959
--- /dev/null
+++ b/src/slider/demos/title.vue
@@ -0,0 +1,18 @@
+<template>
+  <div class="flex">
+    <span :style="{ width: '120px', 'margin-right': '10px' }"> 选择器标题 </span>
+    <t-slider v-model="value" :label="false" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref } from 'vue';
+
+const value = ref(0);
+</script>
+<style>
+.flex {
+  display: flex;
+  align-items: center;
+}
+</style>
diff --git a/src/slider/demos/unZero.vue b/src/slider/demos/unZero.vue
new file mode 100644
index 00000000..a6c8b4db
--- /dev/null
+++ b/src/slider/demos/unZero.vue
@@ -0,0 +1,3 @@
+<template>
+  <t-slider :default-value="30" :min="30" :max="200" :label="false"> </t-slider>
+</template>
diff --git a/src/slider/demos/value.vue b/src/slider/demos/value.vue
index e6ac6488..13caa023 100644
--- a/src/slider/demos/value.vue
+++ b/src/slider/demos/value.vue
@@ -5,5 +5,5 @@
 <script lang="ts" setup>
 import { ref } from 'vue';
 
-const value = ref(60);
+const value = ref(50);
 </script>


Test patch:
diff --git a/src/calendar/__test__/__snapshots__/index.test.jsx.snap b/src/calendar/__test__/__snapshots__/index.test.jsx.snap
new file mode 100644
index 00000000..616dc242
--- /dev/null
+++ b/src/calendar/__test__/__snapshots__/index.test.jsx.snap
@@ -0,0 +1,582 @@
+// Vitest Snapshot v1
+
+exports[`calendar > props > : format 1`] = `
+<div>
+  <!--teleport start-->
+  <div
+    class="t-popup"
+  >
+    <transition-stub
+      appear="false"
+      css="true"
+      name="fade"
+      persisted="false"
+    >
+      <transition-stub
+        appear="false"
+        css="true"
+        name="t-overlay"
+        persisted="true"
+      >
+        <div
+          class="t-overlay t-overlay--active"
+          style="z-index: 1000; transition-duration: 300ms;"
+        >
+          
+          
+        </div>
+      </transition-stub>
+    </transition-stub>
+    <transition-stub
+      appear="false"
+      css="true"
+      name="slide-bottom"
+      persisted="true"
+    >
+      <div
+        class="t-popup--content t-popup--content-bottom"
+      >
+        
+        <div
+          class="t-calendar"
+        >
+          <div
+            class="t-calendar__title"
+          >
+            
+            请选择日期
+            
+          </div>
+          <svg
+            class="t-icon t-icon-close t-calendar__close-btn"
+            fill="none"
+            height="1em"
+            viewBox="0 0 16 16"
+            width="1em"
+          >
+            <path
+              d="M8 8.92L11.08 12l.92-.92L8.92 8 12 4.92 11.08 4 8 7.08 4.92 4 4 4.92 7.08 8 4 11.08l.92.92L8 8.92z"
+              fill="currentColor"
+              fill-opacity="0.9"
+            />
+          </svg>
+          <div
+            class="t-calendar__days"
+          >
+            
+            <div
+              class="t-calendar__days-item"
+            >
+              日
+            </div>
+            <div
+              class="t-calendar__days-item"
+            >
+              一
+            </div>
+            <div
+              class="t-calendar__days-item"
+            >
+              二
+            </div>
+            <div
+              class="t-calendar__days-item"
+            >
+              三
+            </div>
+            <div
+              class="t-calendar__days-item"
+            >
+              四
+            </div>
+            <div
+              class="t-calendar__days-item"
+            >
+              五
+            </div>
+            <div
+              class="t-calendar__days-item"
+            >
+              六
+            </div>
+            
+          </div>
+          <div
+            class="t-calendar__months"
+          >
+            
+            
+            <div
+              class="t-calendar__month"
+            >
+              2022 年 1 月
+            </div>
+            <div
+              class="t-calendar__dates"
+            >
+              
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 294px;"
+              >
+                
+                <!--v-if-->
+                 1 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 2 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 3 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 4 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 5 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 6 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 7 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 8 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 9 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 10 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 11 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 12 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 13 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 14 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item t-calendar__dates-item--selected"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 15 
+                <div
+                  class="t-calendar__dates-item-suffix t-calendar__dates-item-suffix--selected"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 16 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 17 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 18 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 19 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 20 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 21 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 22 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 23 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 24 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 25 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 26 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 27 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 28 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 29 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 30 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              <div
+                class="t-calendar__dates-item"
+                style="margin-left: 0px;"
+              >
+                
+                <!--v-if-->
+                 31 
+                <div
+                  class="t-calendar__dates-item-suffix"
+                >
+                  ¥60
+                </div>
+                
+              </div>
+              
+            </div>
+            
+            
+          </div>
+          <div
+            class="t-calendar__footer"
+          >
+            
+            <button
+              aria-disabled="false"
+              class="t-button t-size-m t-button--base t-button--primary t-button--shape-rectangle t-is-block"
+              role="button"
+            >
+              <!---->
+              <span
+                class="t-button__text"
+              >
+                确认
+              </span>
+            </button>
+            
+          </div>
+        </div>
+        
+      </div>
+    </transition-stub>
+  </div>
+  <!--teleport end-->
+</div>
+`;
diff --git a/src/calendar/__test__/index.test.jsx b/src/calendar/__test__/index.test.jsx
new file mode 100644
index 00000000..85d63fe5
--- /dev/null
+++ b/src/calendar/__test__/index.test.jsx
@@ -0,0 +1,160 @@
+import { ref } from 'vue';
+import { mount } from '@vue/test-utils';
+import { describe, it, expect, vi } from 'vitest';
+import Button from '../../button/index';
+import Calendar from '../calendar.vue';
+
+const prefix = 't'
+const name = `${prefix}-calendar`;
+
+const raw = '日一二三四五六';
+const year = 2022;
+const month = 0;
+const day = 15;
+const minDate = new Date(year, month, 1);
+const maxDate = new Date(year, month, 31);
+const value = new Date(year, month, day);
+const confirmBtn = 'confirmBtn'
+
+const dayFormat = (date, character) => {
+  const D = new Date(date);
+  const year = D.getFullYear();
+  const month = D.getMonth() + 1;
+  const dates = D.getDate();
+  return [year, month, dates].join(character);
+};
+
+describe('calendar', () => {
+  describe('props', () => {
+    it(': visible', async () => {
+      const wrapper = mount(Calendar, {
+        props: {
+          visible: false
+        }
+      })
+      await wrapper.setProps({
+        visible: true
+      })
+    });
+
+    it(': title', async () => {
+      const title = '';
+      const wrapper = mount(<Calendar title={title} />)
+      const $title = wrapper.find(`.${name}__title`)
+      expect($title.text()).toEqual(title || '请选择日期')
+    });
+
+    it(': confirmBtn', async () => {
+      const wrapper = mount(<Calendar visible={true} value={value} confirmBtn={confirmBtn} />)
+      const $confirmBtn = wrapper.find(`.${name}__footer`)
+      expect($confirmBtn.text()).toEqual(confirmBtn)
+    });
+
+    it(': firstDayOfWeek', async () => {
+      const firstDayOfWeek = 1;
+      const wrapper = mount(<Calendar visible={true} value={value} firstDayOfWeek={firstDayOfWeek} />)
+      const $firstDay = wrapper.find(`.${name}__days-item`)
+      expect($firstDay.text()).toEqual(raw.split('')[firstDayOfWeek]);
+    });
+
+    it(': type', async () => {
+      // type = 'range'
+      const onSelect = vi.fn();
+      const time = ref('');
+      const character = '-';
+      const onConfirm = vi.fn((e) => {
+        time.value = dayFormat(e, character);
+      });
+      const wrapper = mount(<Calendar visible={true} value={value} type='range' minDate={minDate} maxDate={maxDate} onConfirm={onConfirm} onSelect={onSelect}/>)
+      const $dates = wrapper.findAll(`.t-calendar__dates-item`);
+      expect($dates).toHaveLength(31);
+
+      // 默认日期是2022/1/15， 模拟点选第2项，和第8项
+      const selectFirstIndex = 2;
+      const selectLastIndex = 8;
+      await $dates[selectFirstIndex].trigger('click');
+      await $dates[selectLastIndex].trigger('click');
+      expect(onSelect).toHaveBeenCalledTimes(2);
+
+      // confirm
+      const $button = wrapper.findComponent(Button);
+      await $button.trigger('click')
+
+      // TODO：区间选择器时，返回的应该是数组，但测试环境下只有单条 Date 对象数据
+      expect(time.value).toEqual([year, month + 1, selectLastIndex + 1].join(character));
+      // TODO: && type = 'multiple'
+
+      // 覆盖 type = '' 情况
+      const wrapper1 = mount(<Calendar visible={true} value={value} type='' minDate={minDate} maxDate={maxDate} onConfirm={onConfirm} onSelect={onSelect} />)
+
+    });
+
+    it(': format', async () => {
+      const suffix = '¥60'; // 每一个日期后均自定义 suffix = '¥60'
+      const format = (day) => {
+        day.suffix = suffix;
+        return day;
+      };
+      const wrapper = mount(<Calendar visible={true} value={value} format={format} minDate={minDate} maxDate={maxDate} />)
+      expect(wrapper.element).toMatchSnapshot();
+      const $dates = wrapper.findAll(`.${name}__dates-item-suffix`);
+      $dates.forEach((item) => {
+        expect(item.text()).toEqual(suffix)
+      })
+    });
+
+  });
+
+  describe('slots', () => {
+    it(': confirmBtn', async () => {
+      const wrapper = mount(<Calendar visible={true} value={value} />, {
+        slots: {
+          confirmBtn,
+        }
+      })
+      const $confirmBtn = wrapper.find(`.${name}__footer`)
+      expect($confirmBtn.text()).toEqual(confirmBtn);
+    });
+  });
+
+  describe('events', () => {
+    it(': onConfirm', async () => {
+      const time = ref('');
+      const character = '-';
+      const onConfirm = vi.fn((e) => {
+        time.value = dayFormat(e, character);
+      });
+      const wrapper = mount(<Calendar visible={true} value={value} onConfirm={onConfirm} />)
+      const $button = wrapper.findComponent(Button);
+      await $button.trigger('click')
+      expect(onConfirm).toHaveBeenCalledTimes(1);
+      expect(time.value).toEqual([year, month + 1, day].join(character));
+    });
+
+    it(': onSelect', async () => {
+      const time = ref('');
+      const character = '-';
+      const onConfirm = vi.fn((e) => {
+        time.value = dayFormat(e, character);
+      });
+
+      const selectTime = ref('');
+      const onSelect = vi.fn((e) => {
+        selectTime.value = dayFormat(e, character);
+      });
+      const wrapper = mount(<Calendar visible={true} value={value} minDate={minDate} maxDate={maxDate} onSelect={onSelect} onConfirm={onConfirm}/>)
+      const $dates = wrapper.findAll(`.${name}__dates-item`);
+      expect($dates).toHaveLength(31);
+
+      // 默认日期是2022/1/15， 模拟点选第2项，触发 onSelect 事件，此时选中日期为2022/1/3
+      const selectIndex = 2;
+      await $dates[selectIndex].trigger('click');
+      expect(onSelect).toHaveBeenCalledTimes(1);
+      expect(selectTime.value).toEqual([year, month + 1, selectIndex + 1].join(character));
+      const $button = wrapper.findComponent(Button);
+      await $button.trigger('click')
+      expect(onConfirm).toHaveBeenCalledTimes(1);
+      expect(selectTime).toEqual(time);
+    });
+  });
+})
diff --git a/src/cascader/__test__/index.test.jsx b/src/cascader/__test__/index.test.jsx
new file mode 100644
index 00000000..313b4b00
--- /dev/null
+++ b/src/cascader/__test__/index.test.jsx
@@ -0,0 +1,146 @@
+import { h } from 'vue';
+import { mount } from '@vue/test-utils';
+import { describe, it, expect, vi } from 'vitest';
+import Cascader from '../cascader.vue';
+import { AppIcon as TIconApp, CloseIcon } from 'tdesign-icons-vue-next';
+import CascaderItem from '../cascader-item.vue';
+import CascaderSteps from '../steps.vue';
+
+
+const prefix = 't'
+const name = `${prefix}-cascader`;
+
+const TEXT = 'tdesign-mobile-vue';
+const iconFunc = () => h(TIconApp);
+const options = [
+  {
+    label: '广东省',
+    value: 'gd',
+    children: [
+      {
+        label: '深圳市',
+        value: 'sz',
+        children: [
+          {
+            label: '南山区',
+            value: 'ns',
+          },
+          {
+            label: '福田区',
+            value: 'ft',
+          },
+        ],
+      },
+      {
+        label: '广州市',
+        value: 'gz',
+        children: [
+          {
+            label: '白云区',
+            value: 'by',
+          },
+          {
+            label: '海珠区',
+            value: 'hz',
+          },
+        ],
+      },
+    ],
+  },
+  {
+    label: '福建省',
+    value: 'fj',
+    children: [
+      {
+        label: '厦门市',
+        value: 'xm',
+      },
+      {
+        label: '泉州市',
+        value: 'qz',
+      },
+    ],
+  },
+];
+describe('cascader', () => {
+  describe('props', () => {
+    it(': title', async () => {
+      const wrapper = mount(<Cascader options={options} />);
+      const $title = wrapper.find(`.${name}__title`);
+
+      const title = TEXT;
+      const newTitle = 'Cascader 级联选择器';
+      await wrapper.setProps({
+        title
+      });
+      expect($title.text()).toBe(TEXT);
+      await wrapper.setProps({
+        title: newTitle
+      });
+
+    });
+
+    it(': closeIcon', async () => {
+      const wrapper = mount(<Cascader options={options} />);
+      // 默认
+      expect(wrapper.findComponent(CloseIcon).exists()).toBeTruthy();
+    });
+  })
+
+  describe('slots', () => {
+    it(': title', () => {
+      const title = 'Cascader 级联选择器';
+      const wrapper = mount(<Cascader options={options} />, {
+        slots: {
+          title
+        }
+      })
+      const $title = wrapper.find(`.${name}__title`);
+      expect($title.text()).toBe(title);
+    })
+
+    it(': closeIcon', () => {
+      const wrapper = mount(<Cascader options={options} />, {
+        slots: {
+          closeIcon: iconFunc
+        }
+      })
+      expect(wrapper.findComponent(TIconApp).exists()).toBeTruthy();
+    })
+  })
+
+  describe('events', () => {
+    it(': cancel', async () => {
+      const onCancel = vi.fn();
+      const wrapper = mount(<Cascader options={options} onCancel={onCancel} />);
+      const $closeBtn = wrapper.find(`.${name}__close-btn`);
+      await $closeBtn.trigger('click');
+      expect(onCancel).toHaveBeenCalledTimes(1);
+    })
+
+    it(': pick && click-tab', async () => {
+      const onClickTab = vi.fn();
+      const onPick = vi.fn();
+      const wrapper = mount(<Cascader options={options} onPick={onPick} onClickTab={onClickTab } />);
+      const $cascaderItems = wrapper.findAllComponents(CascaderItem);
+      const $cascaderSteps = wrapper.findAll(`.${name}__step`);
+      expect($cascaderItems).toHaveLength(2);
+
+      // 模拟点击 第1项 CascaderItem
+      const clickIndex = 0;
+      // await $cascaderItems[clickIndex].find(`.${name}-item`).trigger('click')
+      await $cascaderItems[clickIndex].trigger('click')
+      expect(onPick).toHaveBeenCalledTimes(1);
+      expect($cascaderItems[clickIndex].findAll(`.${name}-item-active-icon`)).toHaveLength(1);
+      expect($cascaderSteps[clickIndex].text()).toEqual(options[clickIndex].label);
+
+      // 模拟点击 第1项，step，触发 click-tab 事件名
+      await $cascaderSteps[clickIndex].trigger('click');
+      expect(onClickTab).toHaveBeenCalledTimes(1);
+
+      const $stepDot = wrapper.findAll(`.${name}__step-dot`);
+      expect($stepDot[clickIndex].attributes('class').includes(`${name}__step-dot--active`));
+
+    })
+  })
+})
diff --git a/src/navbar/__test__/__snapshots__/demo.test.jsx.snap b/src/navbar/__test__/__snapshots__/demo.test.jsx.snap
index 4d247958..8a8c7fe1 100644
--- a/src/navbar/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/navbar/__test__/__snapshots__/demo.test.jsx.snap
@@ -1,12 +1,132 @@
 // Vitest Snapshot v1
 
 exports[`Navbar > Navbar baseVue demo works fine 1`] = `
-<div>
+<div
+  class="t-navbar"
+>
+  <div
+    class="t-navbar__back"
+  >
+    <span
+      class="t-navbar__back--arrow"
+    >
+      <svg
+        class="t-icon t-icon-chevron-left"
+        fill="none"
+        height="1em"
+        viewBox="0 0 16 16"
+        width="1em"
+      >
+        <path
+          d="M9.54 3.54l.92.92L6.92 8l3.54 3.54-.92.92L5.08 8l4.46-4.46z"
+          fill="currentColor"
+          fill-opacity="0.9"
+        />
+      </svg>
+    </span>
+    <!--v-if-->
+    <!---->
+    <!---->
+  </div>
+  <div
+    class="t-navbar__text"
+  >
+    标题
+  </div>
+  <div
+    class="t-navbar__right"
+  >
+    <!---->
+  </div>
+</div>
+`;
+
+exports[`Navbar > Navbar leftIconVue demo works fine 1`] = `
+<div
+  class="t-navbar"
+>
+  <div
+    class="t-navbar__back"
+  >
+    <!--v-if-->
+    <!--v-if-->
+    <!---->
+    
+    <svg
+      class="t-icon t-icon-chevron-left btn-left"
+      style="font-size: 24px;"
+    >
+      <use
+        href="#t-icon-chevron-left"
+      />
+    </svg>
+    <svg
+      class="t-icon t-icon-close"
+      style="font-size: 24px;"
+    >
+      <use
+        href="#t-icon-close"
+      />
+    </svg>
+    
+  </div>
+  <div
+    class="t-navbar__text"
+  >
+    标题
+  </div>
+  <div
+    class="t-navbar__right"
+  >
+    
+    <svg
+      class="t-icon t-icon-ellipsis"
+      style="font-size: 24px;"
+    >
+      <use
+        href="#t-icon-ellipsis"
+      />
+    </svg>
+    
+  </div>
+</div>
+`;
+
+exports[`Navbar > Navbar mobileVue demo works fine 1`] = `
+<div
+  class="tdesign-mobile-demo"
+  data-v-16defdf7=""
+>
+  <h1
+    class="title"
+    data-v-16defdf7=""
+  >
+    NavBar 导航栏
+  </h1>
+  <p
+    class="summary"
+    data-v-16defdf7=""
+  >
+    用于不同页面之间切换或者跳转，位于内容区的上方，系统状态栏的下方。
+  </p>
   <div
-    class="tdesign-demo-block"
+    class="tdesign-mobile-demo-block"
+    data-v-16defdf7=""
   >
+    <h2
+      class="tdesign-mobile-demo-block__title"
+    >
+      01 类型
+    </h2>
+    <p
+      class="tdesign-mobile-demo-block__summary"
+    >
+      基础导航栏
+    </p>
+    
     <div
       class="t-navbar"
+      data-v-16defdf7=""
     >
       <div
         class="t-navbar__back"
@@ -28,126 +148,99 @@ exports[`Navbar > Navbar baseVue demo works fine 1`] = `
             />
           </svg>
         </span>
-        
-        
+        <!--v-if-->
+        <!---->
+        <!---->
       </div>
       <div
         class="t-navbar__text"
       >
-        
-         界面标题 
-        
+        标题
       </div>
       <div
         class="t-navbar__right"
       >
-        
-        
-        <i
-          class="t-navbar__right--more"
-        >
-          <svg
-            class="icon"
-            height="24"
-            p-id="2091"
-            t="1614321969302"
-            version="1.1"
-            viewBox="0 0 1024 1024"
-            width="24"
-            xmlns="http://www.w3.org/2000/svg"
-          >
-            <path
-              d="M512 449.749333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m-318.805333-1.109333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m638.677333 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"
-              fill="#444444"
-              p-id="2092"
-            />
-          </svg>
-        </i>
+        <!---->
       </div>
     </div>
+    
   </div>
-</div>
-`;
-
-exports[`Navbar > Navbar eventVue demo works fine 1`] = `
-<div>
   <div
-    class="tdesign-demo-block"
+    class="tdesign-mobile-demo-block"
+    data-v-16defdf7=""
   >
+    <!--v-if-->
+    <p
+      class="tdesign-mobile-demo-block__summary"
+    >
+      左侧双操作导航栏
+    </p>
+    
     <div
       class="t-navbar"
+      data-v-16defdf7=""
     >
       <div
         class="t-navbar__back"
       >
-        <span
-          class="t-navbar__back--arrow"
-        >
-          <svg
-            class="t-icon t-icon-chevron-left"
-            fill="none"
-            height="1em"
-            viewBox="0 0 16 16"
-            width="1em"
-          >
-            <path
-              d="M9.54 3.54l.92.92L6.92 8l3.54 3.54-.92.92L5.08 8l4.46-4.46z"
-              fill="currentColor"
-              fill-opacity="0.9"
-            />
-          </svg>
-        </span>
+        <!--v-if-->
+        <!--v-if-->
+        <!---->
         
-        <t-icon
-          name="close"
-        />
+        <svg
+          class="t-icon t-icon-chevron-left btn-left"
+          style="font-size: 24px;"
+        >
+          <use
+            href="#t-icon-chevron-left"
+          />
+        </svg>
+        <svg
+          class="t-icon t-icon-close"
+          style="font-size: 24px;"
+        >
+          <use
+            href="#t-icon-close"
+          />
+        </svg>
         
       </div>
       <div
         class="t-navbar__text"
       >
-        
-         界面标题 
-        
+        标题
       </div>
       <div
         class="t-navbar__right"
       >
         
-        
-        <i
-          class="t-navbar__right--more"
+        <svg
+          class="t-icon t-icon-ellipsis"
+          style="font-size: 24px;"
         >
-          <svg
-            class="icon"
-            height="24"
-            p-id="2091"
-            t="1614321969302"
-            version="1.1"
-            viewBox="0 0 1024 1024"
-            width="24"
-            xmlns="http://www.w3.org/2000/svg"
-          >
-            <path
-              d="M512 449.749333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m-318.805333-1.109333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m638.677333 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"
-              fill="#444444"
-              p-id="2092"
-            />
-          </svg>
-        </i>
+          <use
+            href="#t-icon-ellipsis"
+          />
+        </svg>
+        
       </div>
     </div>
+    
   </div>
-</div>
-`;
-
-exports[`Navbar > Navbar iconVue demo works fine 1`] = `
-<div>
   <div
-    class="tdesign-demo-block"
+    class="tdesign-mobile-demo-block"
+    data-v-16defdf7=""
   >
+    <!--v-if-->
+    <p
+      class="tdesign-mobile-demo-block__summary"
+    >
+      右侧双操作导航栏
+    </p>
+    
     <div
       class="t-navbar"
+      data-v-16defdf7=""
     >
       <div
         class="t-navbar__back"
@@ -169,253 +262,97 @@ exports[`Navbar > Navbar iconVue demo works fine 1`] = `
             />
           </svg>
         </span>
-        
-        
+        <!--v-if-->
+        <!---->
+        <!---->
       </div>
       <div
         class="t-navbar__text"
       >
-        
-         导航栏 - 带icon 
-        
+        标题
       </div>
       <div
         class="t-navbar__right"
       >
         
         <svg
-          class="t-icon t-icon-sound"
-          fill="none"
-          height="1em"
-          viewBox="0 0 16 16"
-          width="1em"
+          class="t-icon t-icon-notification btn-left"
+          style="font-size: 24px;"
         >
-          <path
-            d="M4 11l4.24 2.55a.5.5 0 00.76-.43V2.88a.5.5 0 00-.76-.43L4 5H2.1a.6.6 0 00-.6.6v4.8c0 .33.27.6.6.6H4zm1-5.43l3-1.8v8.46l-3-1.8V5.57zM4 10H2.5V6H4v4zM13.52 5.73a6 6 0 00-1.4-1.95l.68-.73c.7.65 1.25 1.42 1.63 2.27a6.6 6.6 0 01-1.63 7.63l-.68-.73a6 6 0 001.4-1.95 5.6 5.6 0 000-4.54z"
-            fill="currentColor"
-            fill-opacity="0.9"
+          <use
+            href="#t-icon-notification"
           />
-          <path
-            d="M11.5 8c0-.87-.36-1.75-1.07-2.45l.7-.72c.9.88 1.37 2.02 1.37 3.17 0 1.16-.48 2.29-1.38 3.17l-.7-.72A3.44 3.44 0 0011.5 8z"
-            fill="currentColor"
-            fill-opacity="0.9"
+        </svg>
+        <svg
+          class="t-icon t-icon-ellipsis"
+          style="font-size: 24px;"
+        >
+          <use
+            href="#t-icon-ellipsis"
           />
         </svg>
         
-        <i
-          class="t-navbar__right--more"
-        >
-          <svg
-            class="icon"
-            height="24"
-            p-id="2091"
-            t="1614321969302"
-            version="1.1"
-            viewBox="0 0 1024 1024"
-            width="24"
-            xmlns="http://www.w3.org/2000/svg"
-          >
-            <path
-              d="M512 449.749333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m-318.805333-1.109333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m638.677333 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"
-              fill="#444444"
-              p-id="2092"
-            />
-          </svg>
-        </i>
       </div>
     </div>
+    
   </div>
 </div>
 `;
 
-exports[`Navbar > Navbar mobileVue demo works fine 1`] = `
+exports[`Navbar > Navbar rightIconVue demo works fine 1`] = `
 <div
-  class="t-navbar-base-demo"
-  data-v-16defdf7=""
+  class="t-navbar"
 >
-  <p
-    class="divide-line"
-    data-v-16defdf7=""
-  />
   <div
-    class="t-navbar"
-    data-v-16defdf7=""
+    class="t-navbar__back"
   >
-    <div
-      class="t-navbar__back"
-    >
-      <span
-        class="t-navbar__back--arrow"
-      >
-        <svg
-          class="t-icon t-icon-chevron-left"
-          fill="none"
-          height="1em"
-          viewBox="0 0 16 16"
-          width="1em"
-        >
-          <path
-            d="M9.54 3.54l.92.92L6.92 8l3.54 3.54-.92.92L5.08 8l4.46-4.46z"
-            fill="currentColor"
-            fill-opacity="0.9"
-          />
-        </svg>
-      </span>
-      
-      
-    </div>
-    <div
-      class="t-navbar__text"
-    >
-      
-      界面标题
-      
-    </div>
-    <div
-      class="t-navbar__right"
+    <span
+      class="t-navbar__back--arrow"
     >
-      
-      
-      <!--v-if-->
-    </div>
-  </div>
-  <p
-    class="divide-line"
-    data-v-16defdf7=""
-  />
-  <div
-    class="t-navbar"
-    data-v-16defdf7=""
-  >
-    <div
-      class="t-navbar__back"
-    >
-      <span
-        class="t-navbar__back--arrow"
-      >
-        <svg
-          class="t-icon t-icon-chevron-left"
-          fill="none"
-          height="1em"
-          viewBox="0 0 16 16"
-          width="1em"
-        >
-          <path
-            d="M9.54 3.54l.92.92L6.92 8l3.54 3.54-.92.92L5.08 8l4.46-4.46z"
-            fill="currentColor"
-            fill-opacity="0.9"
-          />
-        </svg>
-      </span>
-      
-      <svg
-        class="t-icon t-icon-close"
-        data-v-16defdf7=""
-        style="font-size: 24px;"
-      >
-        <use
-          href="#t-icon-close"
-        />
-      </svg>
-      
-    </div>
-    <div
-      class="t-navbar__text"
-    >
-      
-       界面标题 
-      
-    </div>
-    <div
-      class="t-navbar__right"
-    >
-      
       <svg
-        class="t-icon t-icon-app"
-        data-v-16defdf7=""
-        style="font-size: 24px;"
+        class="t-icon t-icon-chevron-left"
+        fill="none"
+        height="1em"
+        viewBox="0 0 16 16"
+        width="1em"
       >
-        <use
-          href="#t-icon-app"
+        <path
+          d="M9.54 3.54l.92.92L6.92 8l3.54 3.54-.92.92L5.08 8l4.46-4.46z"
+          fill="currentColor"
+          fill-opacity="0.9"
         />
       </svg>
-      
-      <!--v-if-->
-    </div>
+    </span>
+    <!--v-if-->
+    <!---->
+    <!---->
   </div>
-  <p
-    class="divide-line"
-    data-v-16defdf7=""
-  />
   <div
-    class="t-navbar"
-    data-v-16defdf7=""
+    class="t-navbar__text"
   >
-    <div
-      class="t-navbar__back"
+    标题
+  </div>
+  <div
+    class="t-navbar__right"
+  >
+    
+    <svg
+      class="t-icon t-icon-notification btn-left"
+      style="font-size: 24px;"
     >
-      <span
-        class="t-navbar__back--arrow"
-      >
-        <svg
-          class="t-icon t-icon-chevron-left"
-          fill="none"
-          height="1em"
-          viewBox="0 0 16 16"
-          width="1em"
-        >
-          <path
-            d="M9.54 3.54l.92.92L6.92 8l3.54 3.54-.92.92L5.08 8l4.46-4.46z"
-            fill="currentColor"
-            fill-opacity="0.9"
-          />
-        </svg>
-      </span>
-      
-      
-    </div>
-    <div
-      class="t-navbar__text"
+      <use
+        href="#t-icon-notification"
+      />
+    </svg>
+    <svg
+      class="t-icon t-icon-ellipsis"
+      style="font-size: 24px;"
     >
-      
-       界面标题 
-      
-    </div>
-    <div
-      class="t-navbar__right"
-    >
-      
-      <svg
-        class="t-icon t-icon-app"
-        data-v-16defdf7=""
-        style="font-size: 24px;"
-      >
-        <use
-          href="#t-icon-app"
-        />
-      </svg>
-      
-      <i
-        class="t-navbar__right--more"
-      >
-        <svg
-          class="icon"
-          height="24"
-          p-id="2091"
-          t="1614321969302"
-          version="1.1"
-          viewBox="0 0 1024 1024"
-          width="24"
-          xmlns="http://www.w3.org/2000/svg"
-        >
-          <path
-            d="M512 449.749333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m-318.805333-1.109333a64 64 0 1 1 0 128 64 64 0 0 1 0-128z m638.677333 0a64 64 0 1 1 0 128 64 64 0 0 1 0-128z"
-            fill="#444444"
-            p-id="2092"
-          />
-        </svg>
-      </i>
-    </div>
+      <use
+        href="#t-icon-ellipsis"
+      />
+    </svg>
+    
   </div>
 </div>
 `;
diff --git a/src/navbar/__test__/demo.test.jsx b/src/navbar/__test__/demo.test.jsx
index e739e0f6..4ef34268 100644
--- a/src/navbar/__test__/demo.test.jsx
+++ b/src/navbar/__test__/demo.test.jsx
@@ -4,15 +4,15 @@
 
 import { mount } from '@vue/test-utils';
 import baseVue from '@/navbar/demos/base.vue';
-import eventVue from '@/navbar/demos/event.vue';
-import iconVue from '@/navbar/demos/icon.vue';
+import leftIconVue from '@/navbar/demos/left-icon.vue';
 import mobileVue from '@/navbar/demos/mobile.vue';
+import rightIconVue from '@/navbar/demos/right-icon.vue';
 
 const mapper = {
   baseVue,
-  eventVue,
-  iconVue,
+  leftIconVue,
   mobileVue,
+  rightIconVue,
 };
 
 describe('Navbar', () => {
diff --git a/src/navbar/__test__/index.test.jsx b/src/navbar/__test__/index.test.jsx
new file mode 100644
index 00000000..9f351adf
--- /dev/null
+++ b/src/navbar/__test__/index.test.jsx
@@ -0,0 +1,129 @@
+import { mount } from '@vue/test-utils';
+import { describe, it, expect } from 'vitest';
+import NavBar from '../navbar.vue';
+
+describe('navbar', () => {
+  describe('props', () => {
+    it('fixed', async () => {
+      const navbar = mount(<NavBar title="标题" />);
+      expect(navbar.vm.navStyle).toContain('position: fixed');
+
+      await navbar.setProps({ fixed: false });
+      expect(navbar.vm.navStyle).toContain('position: relative');
+    });
+
+    it('background', () => {
+      const navbar = mount(<NavBar background="red" />);
+      expect(navbar.vm.navStyle).toContain('background: red');
+    });
+
+    it('homeIcon', () => {
+      const navbar = mount(<NavBar title="标题" homeIcon />);
+      expect(navbar.find('.t-icon-home').exists()).toBeTruthy();
+    });
+
+    it('leftIcon', () => {
+      const navbar = mount(<NavBar title="标题" leftIcon />);
+      expect(navbar.find('.t-icon-chevron-left').exists()).toBeTruthy();
+    });
+
+    it('title', () => {
+      const navbar = mount(<NavBar title="标题" />);
+      expect(navbar.text()).toContain('标题');
+    });
+
+    it('titleMaxLength', async () => {
+      const navbar = mount(<NavBar title="标题超过了六个字符" titleMaxLength={6} />);
+      expect(navbar.text()).toContain('标题超过了六...');
+
+      await navbar.setProps({ titleMaxLength: -1 });
+      expect(navbar.text()).toContain('标题超过了六个字符');
+      await navbar.setProps({ titleMaxLength: 10, title: '测试标题' });
+      expect(navbar.text()).toContain('测试标题');
+    });
+
+    it('visible', () => {
+      const navbar = mount(<NavBar title="标题" visible={false} />);
+      expect(navbar.isVisible()).toBe(false);
+    });
+  });
+
+  describe('events', () => {
+    it('left-click', async () => {
+      const fn = vi.fn();
+      const navbar = mount(<NavBar title="标题" leftIcon onLeftClick={fn} />);
+      await navbar.find('.t-icon-chevron-left').trigger('click');
+      expect(fn).toHaveBeenCalled();
+    });
+  });
+
+  describe('functions', () => {
+    it('homeIcon', () => {
+      const test = () => '测试';
+      const navbar = mount(<NavBar title="标题" homeIcon={test} />);
+      expect(navbar.text()).toContain('测试');
+      expect(navbar.find('.t-icon-home').exists()).toBeFalsy();
+    });
+
+    it('leftIcon', () => {
+      const test = () => '测试';
+      const navbar = mount(<NavBar title="标题" leftIcon={test} />);
+      expect(navbar.text()).toContain('测试');
+      expect(navbar.find('.t-icon-chevron-left').exists()).toBeFalsy();
+    });
+
+    it('rightIcon', () => {
+      const test = () => '测试';
+      const navbar = mount(<NavBar title="标题" rightIcon={test} />);
+      expect(navbar.text()).toContain('测试');
+    });
+
+    it('title', () => {
+      const test = () => '测试';
+      const navbar = mount(<NavBar title={test} />);
+      expect(navbar.text()).toContain('测试');
+    });
+  });
+
+  describe('slots', () => {
+    it('homeIcon', () => {
+      const slot = <div>测试</div>;
+      const navbar = mount(<NavBar title="标题" />, {
+        slots: {
+          'home-icon': slot,
+        },
+      });
+      expect(navbar.text()).toContain('测试');
+    });
+
+    it('leftIcon', () => {
+      const slot = <div>测试</div>;
+      const navbar = mount(<NavBar title="标题" />, {
+        slots: {
+          'left-icon': slot,
+        },
+      });
+      expect(navbar.text()).toContain('测试');
+    });
+
+    it('rightIcon', () => {
+      const slot = <div>测试</div>;
+      const navbar = mount(<NavBar title="标题" />, {
+        slots: {
+          'right-icon': slot,
+        },
+      });
+      expect(navbar.text()).toContain('测试');
+    });
+
+    it('title', () => {
+      const slot = <div>测试</div>;
+      const navbar = mount(<NavBar />, {
+        slots: {
+          title: slot,
+        },
+      });
+      expect(navbar.text()).toContain('测试');
+    });
+  });
+});
diff --git a/src/overlay/__test__/index.test.jsx b/src/overlay/__test__/index.test.jsx
index aea64ac3..4e62f3aa 100644
--- a/src/overlay/__test__/index.test.jsx
+++ b/src/overlay/__test__/index.test.jsx
@@ -50,13 +50,19 @@ describe('Overlay', () => {
       const wrapper = mount(Overlay, {
         props: {
           visible: true,
+
         }
       })
-      expect(getComputedStyle(wrapper.find('.t-overlay').element)['transition-duration']).toEqual('300ms')
+      // duration = 300，默认
+      const $overlay = wrapper.find('.t-overlay');
+      expect(wrapper.find('.t-overlay').attributes('style').includes(`transition-duration: 300ms`)).toBeTruthy();
+
+      // duration = 100
+      const duration = 100;
       await wrapper.setProps({
-        duration: 100
+        duration,
       })
-      expect(getComputedStyle(wrapper.find('.t-overlay').element)['transition-duration']).toEqual('100ms')
+      expect(wrapper.find('.t-overlay').attributes('style').includes(`transition-duration: ${duration}ms`)).toBeTruthy();
     });
 
     it (': zIndex', async () => {
@@ -72,6 +78,18 @@ describe('Overlay', () => {
       expect(getComputedStyle(wrapper.find('.t-overlay').element)['z-index']).toEqual('99')
     })
 
+    it(': customStyle', async () => {
+      const customStyle = 'color: red';
+      const wrapper = mount(Overlay, {
+        props: {
+          visible: true,
+          customStyle,
+        }
+      })
+      const $overlay = wrapper.find('.t-overlay');
+      expect($overlay.attributes('style').includes(customStyle)).toBeTruthy();
+    })
+
   })
 
   describe('slots', () => {
diff --git a/src/slider/__test__/__snapshots__/demo.test.jsx.snap b/src/slider/__test__/__snapshots__/demo.test.jsx.snap
index 8af47150..77599182 100644
--- a/src/slider/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/slider/__test__/__snapshots__/demo.test.jsx.snap
@@ -15,13 +15,13 @@ exports[`Slider > Slider baseVue demo works fine 1`] = `
     <!-- 滑块长度 -->
     <div
       class="t-slider__track"
-      style="width: 60%;"
+      style="width: 0%;"
     />
     <!-- 滑块操作 -->
     
     <div
       class="t-slider__handle"
-      style="left: 60%;"
+      style="left: 0%;"
     />
     
     <!-- 刻度内容 -->
@@ -39,200 +39,38 @@ exports[`Slider > Slider baseVue demo works fine 1`] = `
 
 exports[`Slider > Slider disableVue demo works fine 1`] = `
 <div
-  data-v-app=""
+  class="t-slider-wrap t-is-disabled"
 >
-  
+  <!--v-if-->
   <div
-    class="section"
-    data-v-65b1e7f4=""
+    class="t-slider"
   >
+    <!-- 总长度 -->
     <div
-      class="t-slider-wrap t-is-disabled"
-      data-v-65b1e7f4=""
-    >
-      <!--v-if-->
-      <div
-        class="t-slider"
-      >
-        <!-- 总长度 -->
-        <div
-          class="t-slider__bar"
-        />
-        <!-- 滑块长度 -->
-        <div
-          class="t-slider__track"
-          style="width: 60%;"
-        />
-        <!-- 滑块操作 -->
-        
-        <div
-          class="t-slider__handle"
-          style="left: 60%;"
-        />
-        
-        <!-- 刻度内容 -->
-        <div
-          class="t-slider__mark"
-        >
-          
-          
-        </div>
-      </div>
-      <div
-        class="t-slider-wrap__value"
-      >
-        60
-      </div>
-      <!--v-if-->
-    </div>
-  </div>
-  <div
-    class="section"
-    data-v-65b1e7f4=""
-  >
+      class="t-slider__bar"
+    />
+    <!-- 滑块长度 -->
     <div
-      class="t-slider-wrap t-is-disabled"
-      data-v-65b1e7f4=""
-    >
-      <!--v-if-->
-      <div
-        class="t-slider"
-      >
-        <!-- 总长度 -->
-        <div
-          class="t-slider__bar"
-        />
-        <!-- 滑块长度 -->
-        <div
-          class="t-slider__track"
-          style="width: 0%;"
-        />
-        <!-- 滑块操作 -->
-        
-        <div
-          class="t-slider__handle"
-          style="left: 0%;"
-        />
-        
-        <!-- 刻度内容 -->
-        <div
-          class="t-slider__mark"
-        >
-          
-          
-        </div>
-      </div>
-      <div
-        class="t-slider-wrap__value"
-      >
-        30
-      </div>
-      <!--v-if-->
-    </div>
-  </div>
-  <div
-    class="section"
-    data-v-65b1e7f4=""
-  >
+      class="t-slider__track"
+      style="width: 0%;"
+    />
+    <!-- 滑块操作 -->
+    
     <div
-      class="t-slider-wrap t-is-disabled t-is-mark"
-      data-v-65b1e7f4=""
-    >
-      <!--v-if-->
-      <div
-        class="t-slider"
-      >
-        <!-- 总长度 -->
-        <div
-          class="t-slider__bar"
-        />
-        <!-- 滑块长度 -->
-        <div
-          class="t-slider__track"
-          style="width: 50%;"
-        />
-        <!-- 滑块操作 -->
-        
-        <div
-          class="t-slider__handle"
-          style="left: 50%;"
-        />
-        
-        <!-- 刻度内容 -->
-        <div
-          class="t-slider__mark"
-        >
-          
-          <div
-            class="t-slider__mark-text t-is-active"
-            style="left: 0%;"
-          >
-            小
-          </div>
-          <div
-            class="t-slider__mark-text t-is-"
-            style="left: 50%;"
-          >
-            中
-          </div>
-          <div
-            class="t-slider__mark-text t-is-"
-            style="left: 100%;"
-          >
-            大
-          </div>
-          
-        </div>
-      </div>
-      <!--v-if-->
-      <!--v-if-->
-    </div>
-  </div>
-  <div
-    class="section"
-    data-v-65b1e7f4=""
-  >
+      class="t-slider__handle"
+      style="left: 0%;"
+    />
+    
+    <!-- 刻度内容 -->
     <div
-      class="t-slider-wrap t-is-disabled"
-      data-v-65b1e7f4=""
+      class="t-slider__mark"
     >
-      <!--v-if-->
-      <div
-        class="t-slider"
-      >
-        <!-- 总长度 -->
-        <div
-          class="t-slider__bar"
-        />
-        <!-- 滑块长度 -->
-        <div
-          class="t-slider__track"
-          style="left: 30%; width: 40%;"
-        />
-        <!-- 滑块操作 -->
-        
-        <div
-          class="t-slider__handle"
-          style="left: 30%;"
-        />
-        <div
-          class="t-slider__handle"
-          style="left: 70%;"
-        />
-        
-        <!-- 刻度内容 -->
-        <div
-          class="t-slider__mark"
-        >
-          
-          
-        </div>
-      </div>
-      <!--v-if-->
-      <!--v-if-->
+      
+      
     </div>
   </div>
-  
+  <!--v-if-->
+  <!--v-if-->
 </div>
 `;
 
@@ -306,7 +144,7 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
     class="summary"
     data-v-98828574=""
   >
-    滑动滑块来选择一个数值，在具体场景中也可以增加来刻度和展示数值来方便用户使用
+    用于选择横轴上的数值、区间、档位
   </p>
   <div
     class="tdesign-mobile-demo-block"
@@ -342,13 +180,13 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
           <!-- 滑块长度 -->
           <div
             class="t-slider__track"
-            style="width: 60%;"
+            style="width: 0%;"
           />
           <!-- 滑块操作 -->
           
           <div
             class="t-slider__handle"
-            style="left: 60%;"
+            style="left: 0%;"
           />
           
           <!-- 刻度内容 -->
@@ -395,13 +233,13 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
           <!-- 滑块长度 -->
           <div
             class="t-slider__track"
-            style="width: 60%;"
+            style="width: 50%;"
           />
           <!-- 滑块操作 -->
           
           <div
             class="t-slider__handle"
-            style="left: 60%;"
+            style="left: 50%;"
           />
           
           <!-- 刻度内容 -->
@@ -415,7 +253,7 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
         <div
           class="t-slider-wrap__value"
         >
-          60
+          50
         </div>
         <!--v-if-->
       </div>
@@ -469,11 +307,7 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
             
           </div>
         </div>
-        <div
-          class="t-slider-wrap__value"
-        >
-          30
-        </div>
+        <!--v-if-->
         <!--v-if-->
       </div>
     </div>
@@ -634,160 +468,127 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
       class="slider-demo"
       data-v-98828574=""
     >
-      
       <div
-        class="section"
-        data-v-65b1e7f4=""
+        class="t-slider-wrap t-is-disabled"
+        data-v-98828574=""
       >
+        <!--v-if-->
         <div
-          class="t-slider-wrap t-is-disabled"
-          data-v-65b1e7f4=""
+          class="t-slider"
         >
-          <!--v-if-->
+          <!-- 总长度 -->
           <div
-            class="t-slider"
-          >
-            <!-- 总长度 -->
-            <div
-              class="t-slider__bar"
-            />
-            <!-- 滑块长度 -->
-            <div
-              class="t-slider__track"
-              style="width: 60%;"
-            />
-            <!-- 滑块操作 -->
-            
-            <div
-              class="t-slider__handle"
-              style="left: 60%;"
-            />
-            
-            <!-- 刻度内容 -->
-            <div
-              class="t-slider__mark"
-            >
-              
-              
-            </div>
-          </div>
+            class="t-slider__bar"
+          />
+          <!-- 滑块长度 -->
           <div
-            class="t-slider-wrap__value"
-          >
-            60
-          </div>
-          <!--v-if-->
-        </div>
-      </div>
-      <div
-        class="section"
-        data-v-65b1e7f4=""
-      >
-        <div
-          class="t-slider-wrap t-is-disabled"
-          data-v-65b1e7f4=""
-        >
-          <!--v-if-->
+            class="t-slider__track"
+            style="width: 0%;"
+          />
+          <!-- 滑块操作 -->
+          
           <div
-            class="t-slider"
+            class="t-slider__handle"
+            style="left: 0%;"
+          />
+          
+          <!-- 刻度内容 -->
+          <div
+            class="t-slider__mark"
           >
-            <!-- 总长度 -->
-            <div
-              class="t-slider__bar"
-            />
-            <!-- 滑块长度 -->
-            <div
-              class="t-slider__track"
-              style="width: 0%;"
-            />
-            <!-- 滑块操作 -->
             
-            <div
-              class="t-slider__handle"
-              style="left: 0%;"
-            />
             
-            <!-- 刻度内容 -->
-            <div
-              class="t-slider__mark"
-            >
-              
-              
-            </div>
-          </div>
-          <div
-            class="t-slider-wrap__value"
-          >
-            30
           </div>
-          <!--v-if-->
         </div>
+        <!--v-if-->
+        <!--v-if-->
       </div>
+    </div>
+    
+  </div>
+  <div
+    class="tdesign-mobile-demo-block"
+    data-v-98828574=""
+  >
+    <h2
+      class="tdesign-mobile-demo-block__title"
+    >
+      03 规格
+    </h2>
+    <p
+      class="tdesign-mobile-demo-block__summary"
+    >
+      无标题滑动选择器
+    </p>
+    
+    <div
+      class="slider-demo"
+      data-v-98828574=""
+    >
       <div
-        class="section"
-        data-v-65b1e7f4=""
+        class="t-slider-wrap"
+        data-v-98828574=""
       >
+        <!--v-if-->
         <div
-          class="t-slider-wrap t-is-disabled t-is-mark"
-          data-v-65b1e7f4=""
+          class="t-slider"
         >
-          <!--v-if-->
+          <!-- 总长度 -->
           <div
-            class="t-slider"
+            class="t-slider__bar"
+          />
+          <!-- 滑块长度 -->
+          <div
+            class="t-slider__track"
+            style="width: 0%;"
+          />
+          <!-- 滑块操作 -->
+          
+          <div
+            class="t-slider__handle"
+            style="left: 0%;"
+          />
+          
+          <!-- 刻度内容 -->
+          <div
+            class="t-slider__mark"
           >
-            <!-- 总长度 -->
-            <div
-              class="t-slider__bar"
-            />
-            <!-- 滑块长度 -->
-            <div
-              class="t-slider__track"
-              style="width: 50%;"
-            />
-            <!-- 滑块操作 -->
             
-            <div
-              class="t-slider__handle"
-              style="left: 50%;"
-            />
             
-            <!-- 刻度内容 -->
-            <div
-              class="t-slider__mark"
-            >
-              
-              <div
-                class="t-slider__mark-text t-is-active"
-                style="left: 0%;"
-              >
-                小
-              </div>
-              <div
-                class="t-slider__mark-text t-is-"
-                style="left: 50%;"
-              >
-                中
-              </div>
-              <div
-                class="t-slider__mark-text t-is-"
-                style="left: 100%;"
-              >
-                大
-              </div>
-              
-            </div>
           </div>
-          <!--v-if-->
-          <!--v-if-->
         </div>
+        <!--v-if-->
+        <!--v-if-->
       </div>
+    </div>
+    
+  </div>
+  <div
+    class="tdesign-mobile-demo-block"
+    data-v-98828574=""
+  >
+    <!--v-if-->
+    <p
+      class="tdesign-mobile-demo-block__summary"
+    >
+      有标题滑动选择器
+    </p>
+    
+    <div
+      class="slider-demo"
+      data-v-98828574=""
+    >
       <div
-        class="section"
-        data-v-65b1e7f4=""
+        class="flex"
+        data-v-98828574=""
       >
+        <span
+          style="width: 120px; margin-right: 10px;"
+        >
+           选择器标题 
+        </span>
         <div
-          class="t-slider-wrap t-is-disabled"
-          data-v-65b1e7f4=""
+          class="t-slider-wrap"
         >
           <!--v-if-->
           <div
@@ -800,17 +601,13 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
             <!-- 滑块长度 -->
             <div
               class="t-slider__track"
-              style="left: 30%; width: 40%;"
+              style="width: 0%;"
             />
             <!-- 滑块操作 -->
             
             <div
               class="t-slider__handle"
-              style="left: 30%;"
-            />
-            <div
-              class="t-slider__handle"
-              style="left: 70%;"
+              style="left: 0%;"
             />
             
             <!-- 刻度内容 -->
@@ -825,7 +622,6 @@ exports[`Slider > Slider mobileVue demo works fine 1`] = `
           <!--v-if-->
         </div>
       </div>
-      
     </div>
     
   </div>
@@ -881,6 +677,89 @@ exports[`Slider > Slider rangeVue demo works fine 1`] = `
 </div>
 `;
 
+exports[`Slider > Slider titleVue demo works fine 1`] = `
+<div
+  class="flex"
+>
+  <span
+    style="width: 120px; margin-right: 10px;"
+  >
+     选择器标题 
+  </span>
+  <div
+    class="t-slider-wrap"
+  >
+    <!--v-if-->
+    <div
+      class="t-slider"
+    >
+      <!-- 总长度 -->
+      <div
+        class="t-slider__bar"
+      />
+      <!-- 滑块长度 -->
+      <div
+        class="t-slider__track"
+        style="width: 0%;"
+      />
+      <!-- 滑块操作 -->
+      
+      <div
+        class="t-slider__handle"
+        style="left: 0%;"
+      />
+      
+      <!-- 刻度内容 -->
+      <div
+        class="t-slider__mark"
+      >
+        
+        
+      </div>
+    </div>
+    <!--v-if-->
+    <!--v-if-->
+  </div>
+</div>
+`;
+
+exports[`Slider > Slider unZeroVue demo works fine 1`] = `
+<div
+  class="t-slider-wrap"
+>
+  <!--v-if-->
+  <div
+    class="t-slider"
+  >
+    <!-- 总长度 -->
+    <div
+      class="t-slider__bar"
+    />
+    <!-- 滑块长度 -->
+    <div
+      class="t-slider__track"
+      style="width: 0%;"
+    />
+    <!-- 滑块操作 -->
+    
+    <div
+      class="t-slider__handle"
+      style="left: 0%;"
+    />
+    
+    <!-- 刻度内容 -->
+    <div
+      class="t-slider__mark"
+    >
+      
+      
+    </div>
+  </div>
+  <!--v-if-->
+  <!--v-if-->
+</div>
+`;
+
 exports[`Slider > Slider valueVue demo works fine 1`] = `
 <div
   class="t-slider-wrap"
@@ -896,13 +775,13 @@ exports[`Slider > Slider valueVue demo works fine 1`] = `
     <!-- 滑块长度 -->
     <div
       class="t-slider__track"
-      style="width: 60%;"
+      style="width: 50%;"
     />
     <!-- 滑块操作 -->
     
     <div
       class="t-slider__handle"
-      style="left: 60%;"
+      style="left: 50%;"
     />
     
     <!-- 刻度内容 -->
@@ -916,7 +795,7 @@ exports[`Slider > Slider valueVue demo works fine 1`] = `
   <div
     class="t-slider-wrap__value"
   >
-    60
+    50
   </div>
   <!--v-if-->
 </div>
diff --git a/src/slider/__test__/demo.test.jsx b/src/slider/__test__/demo.test.jsx
index dc354a25..59bb1a10 100644
--- a/src/slider/__test__/demo.test.jsx
+++ b/src/slider/__test__/demo.test.jsx
@@ -8,6 +8,8 @@ import disableVue from '@/slider/demos/disable.vue';
 import markVue from '@/slider/demos/mark.vue';
 import mobileVue from '@/slider/demos/mobile.vue';
 import rangeVue from '@/slider/demos/range.vue';
+import titleVue from '@/slider/demos/title.vue';
+import unZeroVue from '@/slider/demos/unZero.vue';
 import valueVue from '@/slider/demos/value.vue';
 
 const mapper = {
@@ -16,6 +18,8 @@ const mapper = {
   markVue,
   mobileVue,
   rangeVue,
+  titleVue,
+  unZeroVue,
   valueVue,
 };
 
diff --git a/src/stepper/__test__/__snapshots__/demo.test.jsx.snap b/src/stepper/__test__/__snapshots__/demo.test.jsx.snap
index f4ae685d..6a04f5b4 100644
--- a/src/stepper/__test__/__snapshots__/demo.test.jsx.snap
+++ b/src/stepper/__test__/__snapshots__/demo.test.jsx.snap
@@ -73,155 +73,6 @@ exports[`Stepper > Stepper baseVue demo works fine 1`] = `
 </div>
 `;
 
-exports[`Stepper > Stepper disableInputVue demo works fine 1`] = `
-<div
-  class="stepper-demo"
->
-  <div
-    class="t-cell t-cell--middle t-cell--bordered"
-  >
-    <div
-      class="t-cell__left-icon"
-    >
-      <!--v-if-->
-      <!--v-if-->
-    </div>
-    <div
-      class="t-cell__title"
-    >
-      禁止输入
-      <!--v-if-->
-      <!--v-if-->
-    </div>
-    <div
-      class="t-cell__note"
-    >
-      
-      <div
-        class="t-stepper"
-      >
-        <div
-          class="t-stepper__minus t-is-disabled"
-        >
-          <svg
-            class="t-icon t-icon-remove t-stepper__icon"
-            fill="none"
-            height="1em"
-            viewBox="0 0 16 16"
-            width="1em"
-          >
-            <path
-              d="M3.5 7.35h9v1.3h-9v-1.3z"
-              fill="currentColor"
-              fill-opacity="0.9"
-            />
-          </svg>
-        </div>
-        <input
-          class="t-stepper__input"
-          disabled=""
-          readonly=""
-          type="tel"
-        />
-        <div
-          class="t-stepper__plus"
-        >
-          <svg
-            class="t-icon t-icon-add t-stepper__icon"
-            fill="none"
-            height="1em"
-            viewBox="0 0 16 16"
-            width="1em"
-          >
-            <path
-              d="M7.35 8.65v3.85h1.3V8.65h3.85v-1.3H8.65V3.5h-1.3v3.85H3.5v1.3h3.85z"
-              fill="currentColor"
-              fill-opacity="0.9"
-            />
-          </svg>
-        </div>
-      </div>
-      
-    </div>
-    <!--v-if-->
-  </div>
-</div>
-`;
-
-exports[`Stepper > Stepper disabledVue demo works fine 1`] = `
-<div
-  class="stepper-demo"
->
-  <div
-    class="t-cell t-cell--middle t-cell--bordered"
-  >
-    <div
-      class="t-cell__left-icon"
-    >
-      <!--v-if-->
-      <!--v-if-->
-    </div>
-    <div
-      class="t-cell__title"
-    >
-      禁用
-      <!--v-if-->
-      <!--v-if-->
-    </div>
-    <div
-      class="t-cell__note"
-    >
-      
-      <div
-        class="t-stepper t-is-disabled"
-      >
-        <div
-          class="t-stepper__minus t-is-disabled"
-        >
-          <svg
-            class="t-icon t-icon-remove t-stepper__icon"
-            fill="none"
-            height="1em"
-            viewBox="0 0 16 16"
-            width="1em"
-          >
-            <path
-              d="M3.5 7.35h9v1.3h-9v-1.3z"
-              fill="currentColor"
-              fill-opacity="0.9"
-            />
-          </svg>
-        </div>
-        <input
-          class="t-stepper__input"
-          disabled=""
-          type="tel"
-        />
-        <div
-          class="t-stepper__plus t-is-disabled"
-        >
-          <svg
-            class="t-icon t-icon-add t-stepper__icon"
-            fill="none"
-            height="1em"
-            viewBox="0 0 16 16"
-            width="1em"
-          >
-            <path
-              d="M7.35 8.65v3.85h1.3V8.65h3.85v-1.3H8.65V3.5h-1.3v3.85H3.5v1.3h3.85z"
-              fill="currentColor"
-              fill-opacity="0.9"
-            />
-          </svg>
-        </div>
-      </div>
-      
-    </div>
-    <!--v-if-->
-  </div>
-</div>
-`;
-
 exports[`Stepper > Stepper mobileVue demo works fine 1`] = `
 <div
   class="tdesign-mobile-demo"