first commit

This commit is contained in:
lingxiao865
2026-02-10 08:05:03 +08:00
commit c5af079d8c
1094 changed files with 97530 additions and 0 deletions

View File

@@ -0,0 +1,76 @@
:: BASE_DOC ::
## API
### Navbar Props
name | type | default | description | required
-- | -- | -- | -- | --
custom-style | Object | - | CSS(Cascading Style Sheets) | N
animation | Boolean | true | \- | N
background | String | - | `deprecated`。background | N
delta | Number | 1 | \- | N
fixed | Boolean | true | \- | N
home-icon | String | - | `deprecated`。homeIcon | N
left-arrow | Boolean | false | \- | N
left-icon | String | - | `deprecated` | N
placeholder | Boolean | false | \- | N
safe-area-inset-top | Boolean | true | \- | N
title | String | - | page title | N
title-max-length | Number | - | \- | N
visible | Boolean | true | \- | N
z-index | Number | 1 | \- | N
### Navbar Events
name | params | description
-- | -- | --
complete | \- | \-
fail | \- | \-
go-back | \- | \-
go-home | \- | `deprecated`
right-click | \- | \-
success | \- | \-
### Navbar Slots
name | Description
-- | --
capsule | \-
left | \-
right | \-
title | page title
### Navbar External Classes
className | Description
-- | --
t-class | \-
t-class-capsule | \-
t-class-center | \-
t-class-home-icon | \-
t-class-left | \-
t-class-left-icon | \-
t-class-nav-btn | \-
t-class-title | \-
### CSS Variables
The component provides the following CSS variables, which can be used to customize styles.
Name | Default Value | Description
-- | -- | --
--td-navbar-padding-top | 20px | -
--td-navbar-right | 95px | -
--td-navbar-background | @navbar-bg-color | -
--td-navbar-bg-color | @bg-color-container | -
--td-navbar-capsule-border-color | @border-level-1-color | -
--td-navbar-capsule-border-radius | 16px | -
--td-navbar-capsule-height | 32px | -
--td-navbar-capsule-width | 88px | -
--td-navbar-center-left | @navbar-right | -
--td-navbar-center-width | 187px | -
--td-navbar-color | @text-color-primary | -
--td-navbar-height | 48px | -
--td-navbar-left-arrow-size | 24px | -
--td-navbar-left-max-width | --td-navbar-left-max-width | -
--td-navbar-title-font | @font-title-large | -

View File

@@ -0,0 +1,121 @@
---
title: Navbar 导航栏
description: 用于不同页面之间切换或者跳转,位于内容区的上方,系统状态栏的下方。
spline: navigation
isComponent: true
---
## 引入
可在 `main.ts` 或在需要使用的页面或组件中引入。
```js
import TNavbar from '@tdesign/uniapp/navbar/navbar.vue';
```
### 基础导航栏
{{ base }}
### 胶囊样式导航栏
{{ back-home }}
### 带搜索导航栏
{{ search }}
### 带图片导航栏
{{ img }}
### 组件样式
{{ left-title }}
### 自定义颜色
{{ custom-color }}
### FAQ
#### 高度说明
`navbar` 组件可由 `--td-navbar-height` 控制。在 H5 或 APP-PLUS 平台下,`--td-navbar-height` 变量需要业务自己设置,小程序平台则会根据 `statusBarHeight` 等变量计算得到。
## API
### Navbar Props
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
custom-style | Object | - | 自定义样式 | N
animation | Boolean | true | 是否添加动画效果 | N
background | String | - | 已废弃。背景 | N
delta | Number | 1 | 后退按钮后退层数,含义参考 [wx.navigateBack](https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateBack.html),特殊的,传入 0 不会发生执行 wx.navigateBack | N
fixed | Boolean | true | 是否固定在顶部 | N
home-icon | String | - | 已废弃。首页图标地址。值为 '' 或者 undefined 则表示不显示返回图标,值为 'circle' 表示显示默认图标,值为 'slot' 表示使用插槽渲染,值为其他则表示图标地址 | N
left-arrow | Boolean | false | 是否展示左侧箭头 | N
left-icon | String | - | 已废弃。左侧图标地址,值为 '' 或者 undefined 则表示不显示返回图标,值为 'arrow-left' 表示显示返回图标,值为 'slot' 表示使用插槽渲染,值为其他则表示图标地址 | N
placeholder | Boolean | false | 固定在顶部时是否开启占位 | N
safe-area-inset-top | Boolean | true | 是否开启顶部安全区适配 | N
title | String | - | 页面标题 | N
title-max-length | Number | - | 标题文字最大长度,超出的范围使用 `...` 表示 | N
visible | Boolean | true | 是否显示 | N
z-index | Number | 1 | 导航栏栏层级 | N
### Navbar Events
名称 | 参数 | 描述
-- | -- | --
complete | \- | navigateBack 执行完成后触发(失败或成功均会触发)
fail | \- | navigateBack 执行失败后触发
go-back | \- | 点击左侧箭头时触发
go-home | \- | 已废弃。点击 Home 触发
right-click | \- | 点击右侧图标时触发
success | \- | navigateBack 执行成功后触发
### Navbar Slots
名称 | 描述
-- | --
capsule | 左侧胶囊区域
left | 左侧内容区域
right | 右侧内容区域
title | 自定义 `title` 显示内容
### Navbar External Classes
类名 | 描述
-- | --
t-class | 根节点样式类
t-class-capsule | 左侧胶囊区域样式类
t-class-center | 中间内容样式类
t-class-home-icon | 首页图标样式类
t-class-left | 左侧内容样式类
t-class-left-icon | 左侧图标样式类
t-class-nav-btn | 导航按钮样式类
t-class-title | 标题样式类
### CSS Variables
组件提供了下列 CSS 变量,可用于自定义样式。
名称 | 默认值 | 描述
-- | -- | --
--td-navbar-padding-top | 20px | -
--td-navbar-right | 95px | -
--td-navbar-background | @navbar-bg-color | -
--td-navbar-bg-color | @bg-color-container | -
--td-navbar-capsule-border-color | @border-level-1-color | -
--td-navbar-capsule-border-radius | 16px | -
--td-navbar-capsule-height | 32px | -
--td-navbar-capsule-width | 88px | -
--td-navbar-center-left | @navbar-right | -
--td-navbar-center-width | 187px | -
--td-navbar-color | @text-color-primary | -
--td-navbar-height | 48px | -
--td-navbar-left-arrow-size | 24px | -
--td-navbar-left-max-width | --td-navbar-left-max-width | -
--td-navbar-title-font | @font-title-large | -

View File

@@ -0,0 +1,106 @@
.t-navbar {
position: relative;
}
.t-navbar--fixed .t-navbar__content {
position: fixed;
top: 0;
left: 0;
}
.t-navbar--visible {
display: '';
}
.t-navbar--visible-animation {
opacity: 1;
transition: opacity 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.t-navbar--hide-animation {
opacity: 0;
transition: opacity 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.t-navbar--hide {
display: none;
}
.t-navbar__placeholder {
height: var(--td-navbar-height, 48px);
padding-top: var(--td-navbar-padding-top, 20px);
position: relative;
visibility: hidden;
box-sizing: content-box;
}
.t-navbar__content {
position: relative;
height: var(--td-navbar-height, 48px);
width: calc(100% - var(--td-navbar-right, 95px));
padding-right: var(--td-navbar-right, 95px);
padding-top: var(--td-navbar-padding-top, 20px);
color: var(--td-navbar-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))));
background: var(--td-navbar-background, var(--td-navbar-bg-color, var(--td-bg-color-container, var(--td-font-white-1, #ffffff))));
display: flex;
align-items: center;
box-sizing: content-box;
}
.t-navbar__left {
position: relative;
box-sizing: border-box;
max-width: var(--td-navbar-left-max-width);
overflow: hidden;
display: flex;
align-items: center;
margin-left: var(--td-spacer-1, 24rpx);
transition: opacity 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
:deep(.t-navbar__left-arrow) {
font-size: var(--td-navbar-left-arrow-size, 24px);
}
.t-navbar__left--hide {
opacity: 0;
}
.t-navbar__capsule {
box-sizing: border-box;
width: var(--td-navbar-capsule-width, 88px);
height: var(--td-navbar-capsule-height, 32px);
display: flex;
align-items: center;
}
.t-navbar__capsule::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: 0 0;
box-sizing: border-box;
border-radius: calc(var(--td-navbar-capsule-border-radius, 16px) * 2);
border: 2rpx solid var(--td-navbar-capsule-border-color, var(--td-border-level-1-color, var(--td-gray-color-3, #e7e7e7)));
}
.t-navbar__capsule:empty {
display: none;
}
.t-navbar__center {
text-align: center;
position: absolute;
bottom: 0;
left: var(--td-navbar-center-left, var(--td-navbar-right, 95px));
width: var(--td-navbar-center-width, 187px);
height: var(--td-navbar-height, 48px);
flex: 1;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
transition: opacity 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.t-navbar__center:empty {
display: none;
}
.t-navbar__center-title {
font: var(--td-navbar-title-font, var(--td-font-title-large, 600 36rpx / 52rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular)));
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
.t-navbar__center--hide {
opacity: 0;
}

View File

@@ -0,0 +1,293 @@
<template>
<view
:class="tools.cls(classPrefix, [['fixed', fixed]]) + ' ' + visibleClass + ' ' + tClass"
:style="tools._style([boxStyle, customStyle])"
>
<view
v-if="fixed && placeholder"
:class="classPrefix + '__placeholder ' + tClassPlaceholder"
/>
<view :class="classPrefix + '__content ' + tClassContent">
<view :class="classPrefix + '__left ' + (hideLeft ? classPrefix + '__left--hide' : '') + ' ' + tClassLeft">
<view
v-if="leftArrow"
:class="classPrefix + '__btn'"
aria-role="button"
aria-label="返回"
@click="goBack"
>
<t-icon
name="chevron-left"
:custom-style="leftArrowCustomStyle"
:t-class="classPrefix + '__left-arrow'"
/>
</view>
<slot name="left" />
<view :class="classPrefix + '__capsule ' + tClassCapsule">
<slot name="capsule" />
</view>
</view>
<view :class="classPrefix + '__center ' + (hideCenter ? classPrefix + '__center--hide' : '') + ' ' + tClassCenter">
<slot name="title" />
<text
v-if="title"
:class="classPrefix + '__center-title ' + tClassTitle"
>
{{ showTitle }}
</text>
</view>
<view
:class="classPrefix + '__right'"
@click="onClickRight"
>
<slot name="right" />
</view>
</view>
</view>
</template>
<script>
import TIcon from '../icon/icon';
import { uniComponent } from '../common/src/index';
import { getRect, systemInfo } from '../common/utils';
import { prefix } from '../common/config';
import props from './props';
import tools from '../common/utils.wxs';
const name = `${prefix}-navbar`;
const BASE_MENU_RECT = {
width: 87,
height: 32,
top: 24,
right: systemInfo.windowWidth - 10,
};
export default uniComponent({
name,
options: {
styleIsolation: 'shared',
},
externalClasses: [
`${prefix}-class`,
`${prefix}-class-placeholder`,
`${prefix}-class-content`,
`${prefix}-class-title`,
`${prefix}-class-left`,
`${prefix}-class-center`,
`${prefix}-class-left-icon`,
`${prefix}-class-home-icon`,
`${prefix}-class-capsule`,
`${prefix}-class-nav-btn`,
],
components: {
TIcon,
},
props: {
...props,
},
emits: [
'fail',
'complete',
'success',
'go-back',
'right-click',
],
data() {
return {
timer: null,
prefix,
classPrefix: name,
boxStyle: '',
showTitle: '',
hideLeft: false,
hideCenter: false,
_menuRect: null,
_leftRect: null,
_boxStyle: {},
tools,
visibleClass: '',
};
},
computed: {
leftArrowCustomStyle() {
return 'font-size: var(--td-navbar-left-arrow-size, 24px);';
},
},
watch: {
visible(visible) {
const { animation } = this;
const visibleClass = `${name}${visible ? '--visible' : '--hide'}`;
this.visibleClass = `${visibleClass}${animation ? '-animation' : ''}`;
if (this.timer) {
clearTimeout(this.timer);
}
if (animation) {
this.timer = setTimeout(() => {
this.visibleClass = visibleClass;
}, 300);
}
},
title: 'onWatchTitle',
titleMaxLength: 'onWatchTitle',
},
mounted() {
this.onWatchTitle();
this.initStyle();
this.getLeftRect();
this.onMenuButtonBoundingClientRectWeightChange();
},
beforeUnMount() {
this.offMenuButtonBoundingClientRectWeightChange();
},
methods: {
initStyle() {
this.getMenuRect();
const { _menuRect, _leftRect } = this;
if (!_menuRect || !_leftRect || !systemInfo) return;
const _boxStyle = {
'--td-navbar-padding-top': `${systemInfo.statusBarHeight}px`,
'--td-navbar-right': `${systemInfo.windowWidth - _menuRect.left}px`, // 导航栏右侧小程序胶囊按钮宽度
'--td-navbar-left-max-width': `${_menuRect.left}px`, // 左侧内容最大宽度
'--td-navbar-capsule-height': `${_menuRect.height}px`, // 胶囊高度
'--td-navbar-capsule-width': `${_menuRect.width}px`, // 胶囊宽度
'--td-navbar-height': `${(_menuRect.top - systemInfo.statusBarHeight) * 2 + _menuRect.height}px`,
};
// #ifdef H5 || APP-PLUS
delete _boxStyle['--td-navbar-height'];
// #endif
this.calcCenterStyle(_leftRect, _menuRect, _boxStyle);
},
onWatchTitle() {
const { title } = this;
const titleMaxLength = this.titleMaxLength || Number.MAX_SAFE_INTEGER;
let temp = title.slice(0, titleMaxLength);
if (titleMaxLength < title.length) temp += '...';
this.showTitle = temp;
},
calcCenterStyle(
leftRect,
menuRect,
defaultStyle,
) {
const maxSpacing = Math.max(leftRect.right, systemInfo.windowWidth - menuRect.left);
const _boxStyle = {
...defaultStyle,
'z-index': this.zIndex,
'--td-navbar-center-left': `${maxSpacing}px`, // 标题左侧距离
'--td-navbar-center-width': `${Math.max(menuRect.left - maxSpacing, 0)}px`, // 标题宽度
};
const boxStyle = Object.entries(_boxStyle)
.map(([k, v]) => `${k}: ${v}`)
.join('; ');
this.boxStyle = boxStyle;
this._boxStyle = _boxStyle;
},
getLeftRect() {
getRect(this, `.${name}__left`).then((res) => {
if (res.right > this._leftRect.right) {
this.calcCenterStyle(res, this._menuRect, this._boxStyle);
}
});
},
getMenuRect() {
// 场景值为1177视频号直播间和1175 视频号profile页小程序禁用了 uni.getMenuButtonBoundingClientRect
let rect = {
...BASE_MENU_RECT,
bottom: BASE_MENU_RECT.top + BASE_MENU_RECT.height,
left: BASE_MENU_RECT.right - BASE_MENU_RECT.width,
};
if (uni.getMenuButtonBoundingClientRect
&& typeof uni.getMenuButtonBoundingClientRect === 'function'
&& typeof uni.getMenuButtonBoundingClientRect() === 'object'
) {
rect = uni.getMenuButtonBoundingClientRect() || {};
}
this._menuRect = rect;
this._leftRect = {
right: systemInfo.windowWidth - rect.left,
};
},
onMenuButtonBoundingClientRectWeightChange() {
if (uni.onMenuButtonBoundingClientRectWeightChange) {
this.onMenuButtonBoundingClientRectWeightChangeCallback = res => this.queryElements(res);
uni.onMenuButtonBoundingClientRectWeightChange(this.onMenuButtonBoundingClientRectWeightChangeCallback);
}
},
offMenuButtonBoundingClientRectWeightChange() {
if (this.onMenuButtonBoundingClientRectWeightChangeCallback) {
uni.offMenuButtonBoundingClientRectWeightChange(this.onMenuButtonBoundingClientRectWeightChangeCallback);
}
},
/**
* 比较胶囊条和navbar内容决定是否隐藏
* @param capsuleRect API返回值胶囊条的位置信息
*/
queryElements(capsuleRect) {
Promise.all([
getRect(this, `.${this.classPrefix}__left`),
getRect(this, `.${this.classPrefix}__center`),
]).then(([leftRect, centerRect]) => {
// 部分安卓机型中目前仅在Magic6/7中复现仍存在精度问题暂使用 Math.round() 取整规避
const leftRight = Math.round(leftRect.right);
const centerRight = Math.round(centerRect.right);
const capsuleLeft = capsuleRect.left;
this.hideLeft = leftRight > capsuleLeft;
this.hideCenter = leftRight > capsuleLeft ? true : centerRight > capsuleLeft;
});
},
goBack() {
const { delta } = this;
// eslint-disable-next-line
const that = this;
this.$emit('go-back');
if (delta > 0) {
uni.navigateBack({
delta,
fail(e) {
that.$emit('fail', e);
},
complete(e) {
that.$emit('complete', e);
},
success(e) {
that.$emit('success', e);
},
});
}
},
onClickRight() {
this.$emit('right-click');
},
},
});
</script>
<style scoped>
@import './navbar.css';
</style>

View File

@@ -0,0 +1,75 @@
/* eslint-disable */
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
export default {
/** 是否添加动画效果 */
animation: {
type: Boolean,
default: true,
},
/** 后退按钮后退层数,含义参考 [wx.navigateBack](https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateBack.html),特殊的,传入 0 不会发生执行 wx.navigateBack */
delta: {
type: Number,
default: 1,
},
/** 是否固定在顶部 */
fixed: {
type: Boolean,
default: true,
},
/** 是否展示左侧箭头 */
leftArrow: Boolean,
/** 固定在顶部时是否开启占位 */
placeholder: Boolean,
/** 是否开启顶部安全区适配 */
safeAreaInsetTop: {
type: Boolean,
default: true,
},
/** 页面标题 */
title: {
type: String,
},
/** 标题文字最大长度,超出的范围使用 `...` 表示 */
titleMaxLength: {
type: Number,
},
/** 是否显示 */
visible: {
type: Boolean,
default: true,
},
/** 导航栏栏层级 */
zIndex: {
type: Number,
default: 1,
},
/** navigateBack 执行完成后触发(失败或成功均会触发) */
onComplete: {
type: Function,
default: () => ({}),
},
/** navigateBack 执行失败后触发 */
onFail: {
type: Function,
default: () => ({}),
},
/** 点击左侧箭头时触发 */
onGoBack: {
type: Function,
default: () => ({}),
},
/** 点击右侧图标时触发 */
onRightClick: {
type: Function,
default: () => ({}),
},
/** navigateBack 执行成功后触发 */
onSuccess: {
type: Function,
default: () => ({}),
},
};

View File

@@ -0,0 +1,76 @@
/* eslint-disable */
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
export interface TdNavbarProps {
/**
* 是否添加动画效果
* @default true
*/
animation?: boolean;
/**
* 后退按钮后退层数,含义参考 [wx.navigateBack](https://developers.weixin.qq.com/miniprogram/dev/api/route/wx.navigateBack.html),特殊的,传入 0 不会发生执行 wx.navigateBack
* @default 1
*/
delta?: number;
/**
* 是否固定在顶部
* @default true
*/
fixed?: boolean;
/**
* 是否展示左侧箭头
* @default false
*/
leftArrow?: boolean;
/**
* 固定在顶部时是否开启占位
* @default false
*/
placeholder?: boolean;
/**
* 是否开启顶部安全区适配
* @default true
*/
safeAreaInsetTop?: boolean;
/**
* 页面标题
*/
title?: string;
/**
* 标题文字最大长度,超出的范围使用 `...` 表示
*/
titleMaxLength?: number;
/**
* 是否显示
* @default true
*/
visible?: boolean;
/**
* 导航栏栏层级
* @default 1
*/
zIndex?: number;
/**
* navigateBack 执行完成后触发(失败或成功均会触发)
*/
onComplete?: () => void;
/**
* navigateBack 执行失败后触发
*/
onFail?: () => void;
/**
* 点击左侧箭头时触发
*/
onGoBack?: () => void;
/**
* 点击右侧图标时触发
*/
onRightClick?: () => void;
/**
* navigateBack 执行成功后触发
*/
onSuccess?: () => void;
}