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,62 @@
:: BASE_DOC ::
## API
### NoticeBar Props
name | type | default | description | required
-- | -- | -- | -- | --
custom-style | Object | - | CSS(Cascading Style Sheets) | N
content | String / Array | - | \- | N
direction | String | horizontal | options: horizontal/vertical | N
interval | Number | 2000 | \- | N
marquee | Boolean / Object | false | Typescript`boolean \| NoticeBarMarquee` `interface NoticeBarMarquee { speed?: number; loop?: number; delay?: number }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/notice-bar/type.ts) | N
operation | String | - | \- | N
prefix-icon | String / Boolean / Object | true | \- | N
suffix-icon | String / Object | - | \- | N
theme | String | info | options: info/success/warning/error | N
visible | Boolean | false | `v-model:visible` is supported | N
default-visible | Boolean | false | uncontrolled property | N
### NoticeBar Events
name | params | description
-- | -- | --
change | `(context: { current: number, source: '' \| 'autoplay' \| 'touch' })` | \-
click | `(context: { trigger: NoticeBarTrigger })` | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/notice-bar/type.ts)。<br/>`type NoticeBarTrigger = 'prefix-icon' \| 'content' \| 'operation' \| 'suffix-icon';`<br/>
### NoticeBar Slots
name | Description
-- | --
content | \-
operation | \-
prefix-icon | \-
suffix-icon | \-
### NoticeBar External Classes
className | Description
-- | --
t-class | \-
t-class-content | \-
t-class-operation | \-
t-class-prefix-icon | \-
t-class-suffix-icon | \-
### CSS Variables
The component provides the following CSS variables, which can be used to customize styles.
Name | Default Value | Description
-- | -- | --
--td-notice-bar-error-bg-color | @error-color-1 | -
--td-notice-bar-error-color | @error-color | -
--td-notice-bar-font-color | @text-color-primary | -
--td-notice-bar-info-bg-color | @brand-color-light | -
--td-notice-bar-info-color | @brand-color | -
--td-notice-bar-operation-font-color | @brand-color | -
--td-notice-bar-success-bg-color | @success-color-1 | -
--td-notice-bar-success-color | @success-color | -
--td-notice-bar-suffix-icon-color | @text-color-placeholder | -
--td-notice-bar-warning-bg-color | @warning-color-1 | -
--td-notice-bar-warning-color | @warning-color | -

View File

@@ -0,0 +1,116 @@
---
title: NoticeBar 公告栏
description: 在导航栏下方,用于给用户显示提示消息。
spline: message
isComponent: true
---
## 引入
可在 `main.ts` 或在需要使用的页面或组件中引入。
```js
import TNoticeBar from '@tdesign/uniapp/notice-bar/notice-bar.vue';
```
### 01 组件类型
纯文字的公告栏
{{ base }}
带图标的公告栏
{{ iconDemo }}
带关闭的公告栏
{{ suffixIcon }}
带入口的公告栏
{{ event }}
自定义样式的公告栏
{{ custom }}
自定义内容的公告栏
{{ customization }}
### 02 组件状态
公告栏类型有普通info、警示warning、成功success、错误error
{{ theme }}
### 03 可滚动公告栏
可滚动公告栏有水平horizontal和垂直vertical
{{ scrolling }}
## API
### NoticeBar Props
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
custom-style | Object | - | 自定义样式 | N
content | String / Array | - | 文本内容 | N
direction | String | horizontal | 滚动方向。可选项horizontal/vertical | N
interval | Number | 2000 | 间隔时间【仅在 direction='vertical' 有效】 | N
marquee | Boolean / Object | false | 跑马灯效果。speed 指速度控制loop 指循环播放次数,值为 -1 表示循环播放,值为 0 表示不循环播放delay 表示延迟多久开始播放【仅在 direction='horizontal' 有效】。TS 类型:`boolean \| NoticeBarMarquee` `interface NoticeBarMarquee { speed?: number; loop?: number; delay?: number }`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/notice-bar/type.ts) | N
operation | String | - | 右侧额外信息 | N
prefix-icon | String / Boolean / Object | true | 前缀图标。值为字符串表示图标名称,值为 `false` 表示不显示前缀图标,值为 `Object` 类型,表示透传至 `icon`,不传表示使用主题图标 | N
suffix-icon | String / Object | - | 后缀图标。值为字符串表示图标名称。值为 `Object` 类型,表示透传至 `icon`,不传表示不显示后缀图标 | N
theme | String | info | 内置主题。可选项info/success/warning/error | N
visible | Boolean | false | 显示/隐藏。支持语法糖 `v-model:visible` | N
default-visible | Boolean | false | 显示/隐藏。非受控属性 | N
### NoticeBar Events
名称 | 参数 | 描述
-- | -- | --
change | `(context: { current: number, source: '' \| 'autoplay' \| 'touch' })` | 当 `direction="vertical"` 时轮播切换时触发
click | `(context: { trigger: NoticeBarTrigger })` | 点击事件。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/tree/develop/packages/uniapp-components/notice-bar/type.ts)。<br/>`type NoticeBarTrigger = 'prefix-icon' \| 'content' \| 'operation' \| 'suffix-icon';`<br/>
### NoticeBar Slots
名称 | 描述
-- | --
content | 文本内容
operation | 自定义 `operation` 显示内容
prefix-icon | 前缀图标
suffix-icon | 后缀图标
### NoticeBar External Classes
类名 | 描述
-- | --
t-class | 根节点样式类
t-class-content | 内容样式类
t-class-operation | 右侧额外信息样式类
t-class-prefix-icon | 前置图标样式类
t-class-suffix-icon | 后置图标样式类
### CSS Variables
组件提供了下列 CSS 变量,可用于自定义样式。
名称 | 默认值 | 描述
-- | -- | --
--td-notice-bar-error-bg-color | @error-color-1 | -
--td-notice-bar-error-color | @error-color | -
--td-notice-bar-font-color | @text-color-primary | -
--td-notice-bar-info-bg-color | @brand-color-light | -
--td-notice-bar-info-color | @brand-color | -
--td-notice-bar-operation-font-color | @brand-color | -
--td-notice-bar-success-bg-color | @success-color-1 | -
--td-notice-bar-success-color | @success-color | -
--td-notice-bar-suffix-icon-color | @text-color-placeholder | -
--td-notice-bar-warning-bg-color | @warning-color-1 | -
--td-notice-bar-warning-color | @warning-color | -

View File

@@ -0,0 +1,75 @@
.t-notice-bar {
display: flex;
align-items: flex-start;
padding: 26rpx 32rpx;
}
.t-notice-bar__content-wrap {
flex: 1;
overflow-x: hidden;
font: var(--td-font-body-medium, 28rpx / 44rpx var(--td-font-family, PingFang SC, Microsoft YaHei, Arial Regular));
color: var(--td-notice-bar-font-color, var(--td-text-color-primary, var(--td-font-gray-1, rgba(0, 0, 0, 0.9))));
}
.t-notice-bar__content {
display: inline-block;
white-space: nowrap;
}
.t-notice-bar__content-wrapable {
white-space: normal;
}
.t-notice-bar__content--vertical {
display: block;
height: 44rpx;
line-height: 44rpx;
}
.t-notice-bar__content--vertical-item {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.t-notice-bar__prefix-icon {
color: inherit;
}
.t-notice-bar__prefix-icon:not(:empty) {
padding-right: var(--td-spacer, 16rpx);
width: 44rpx;
}
.t-notice-bar__suffix-icon {
color: var(--td-notice-bar-suffix-icon-color, var(--td-text-color-placeholder, var(--td-font-gray-3, rgba(0, 0, 0, 0.4))));
}
.t-notice-bar__prefix-icon,
.t-notice-bar__suffix-icon {
font-size: 44rpx;
}
.t-notice-bar__prefix-icon:empty,
.t-notice-bar__suffix-icon:empty {
display: none;
}
.t-notice-bar__operation {
display: inline-flex;
vertical-align: top;
color: var(--td-notice-bar-operation-font-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)));
font-weight: 700;
}
.t-notice-bar__operation:empty {
display: none;
}
.t-notice-bar__suffix-icon:not(:empty) {
padding-left: var(--td-spacer, 16rpx);
width: 44rpx;
}
.t-notice-bar--info {
color: var(--td-notice-bar-info-color, var(--td-brand-color, var(--td-primary-color-7, #0052d9)));
background-color: var(--td-notice-bar-info-bg-color, var(--td-brand-color-light, var(--td-primary-color-1, #f2f3ff)));
}
.t-notice-bar--success {
color: var(--td-notice-bar-success-color, var(--td-success-color, var(--td-success-color-5, #2ba471)));
background-color: var(--td-notice-bar-success-bg-color, var(--td-success-color-1, #e3f9e9));
}
.t-notice-bar--warning {
color: var(--td-notice-bar-warning-color, var(--td-warning-color, var(--td-warning-color-5, #e37318)));
background-color: var(--td-notice-bar-warning-bg-color, var(--td-warning-color-1, #fff1e9));
}
.t-notice-bar--error {
color: var(--td-notice-bar-error-color, var(--td-error-color, var(--td-error-color-6, #d54941)));
background-color: var(--td-notice-bar-error-bg-color, var(--td-error-color-1, #fff0ed));
}

View File

@@ -0,0 +1,326 @@
<template>
<view
v-if="visible"
:style="tools._style([customStyle])"
:class="[
classPrefix + ' ' + classPrefix + '--' + theme,
tClass
]"
>
<view
:class="classPrefix + '__prefix-icon'"
@click="clickPrefixIcon"
>
<slot name="prefix-icon" />
<block
v-if="_prefixIcon"
name="icon"
>
<t-icon
:custom-style="_prefixIcon.style || ''"
:t-class="tClassPrefixIcon"
:prefix="_prefixIcon.prefix"
:name="_prefixIcon.name"
:size="_prefixIcon.size"
:color="_prefixIcon.color"
:aria-hidden="!!_prefixIcon.ariaHidden"
:aria-label="_prefixIcon.ariaLabel"
:aria-role="_prefixIcon.ariaRole"
@click="_prefixIcon.bindclick || ''"
/>
</block>
</view>
<view
:class="classPrefix + '__content-wrap'"
@click="clickContent"
>
<view v-if="direction === 'vertical' && tools.isArray(content)">
<swiper
:autoplay="true"
:vertical="true"
:circular="true"
:interval="interval"
display-multiple-items="1"
:class="classPrefix + '__content ' + classPrefix + '__content--vertical'"
@change="onChange"
>
<block
v-for="(item, index) in content"
:key="index"
>
<swiper-item>
<view :class="classPrefix + '__content--vertical-item'">
{{ item }}
</view>
</swiper-item>
</block>
</swiper>
</view>
<view
v-else
:class="classPrefix + '__content ' + tClassContent + ' ' + (!marquee ? classPrefix + '__content-wrapable' : '')"
:animation="animationData"
>
<block v-if="content">
{{ content }}
</block>
<slot name="content" />
<view
:class="classPrefix + '__operation ' + tClassOperation"
@click.stop.prevent="clickOperation"
>
<block v-if="operation">
{{ operation }}
</block>
<slot name="operation" />
</view>
</view>
</view>
<view
:class="classPrefix + '__suffix-icon'"
@click="clickSuffixIcon"
>
<slot name="suffix-icon" />
<block
v-if="_suffixIcon"
name="icon"
>
<t-icon
:custom-style="_suffixIcon.style || ''"
:t-class="tClassSuffixIcon"
:prefix="_suffixIcon.prefix"
:name="_suffixIcon.name"
:size="_suffixIcon.size"
:color="_suffixIcon.color"
:aria-hidden="!!_suffixIcon.ariaHidden"
:aria-label="_suffixIcon.ariaLabel"
:aria-role="_suffixIcon.ariaRole"
@click="_suffixIcon.bindclick || ''"
/>
</block>
</view>
</view>
</template>
<script>
import TIcon from '../icon/icon.vue';
import { uniComponent } from '../common/src/index';
import { getRect, getAnimationFrame, calcIcon } from '../common/utils';
import props from './props';
import { prefix } from '../common/config';
import tools from '../common/utils.wxs';
const name = `${prefix}-notice-bar`;
const THEME_ICON = {
info: 'info-circle-filled',
success: 'check-circle-filled',
warning: 'error-circle-filled',
error: 'error-circle-filled',
};
export default uniComponent({
name,
options: {
styleIsolation: 'shared',
},
externalClasses: [
`${prefix}-class`,
`${prefix}-class-content`,
`${prefix}-class-prefix-icon`,
`${prefix}-class-operation`,
`${prefix}-class-suffix-icon`,
],
components: {
TIcon,
},
props: {
...props,
},
emits: [
'click',
'change',
],
data() {
return {
prefix,
classPrefix: name,
loop: -1,
__ready: false,
animationData: null,
tools,
};
},
watch: {
marquee: {
handler(val) {
if (JSON.stringify(val) === '{}' || JSON.stringify(val) === 'true') {
this.dataMarquee = {
speed: 50,
loop: -1,
delay: 0,
};
}
},
immediate: true,
},
visible: {
handler(visible) {
if (!this.__ready) return;
if (visible) {
this.show();
} else {
this.clearNoticeBarAnimation();
}
},
immediate: true,
},
prefixIcon: {
handler(prefixIcon) {
this.setPrefixIcon(prefixIcon);
},
immediate: true,
},
suffixIcon: {
handler(v) {
this._suffixIcon = calcIcon(v);
},
immediate: true,
},
content: {
handler() {
if (!this.__ready) return;
this.clearNoticeBarAnimation();
this.initAnimation();
},
immediate: true,
},
},
created() {
this.resetAnimation = uni.createAnimation({
duration: 0,
timingFunction: 'linear',
});
},
mounted() {
this.show();
this.__ready = true;
},
beforeUnMount() {
this.clearNoticeBarAnimation();
},
methods: {
initAnimation() {
// 获取外部容器和滚动内容的宽度
const warpID = `.${name}__content-wrap`;
const nodeID = `.${name}__content`;
getAnimationFrame(this, () => {
Promise.all([getRect(this, nodeID), getRect(this, warpID)])
.then(([nodeRect, wrapRect]) => {
const { marquee } = this;
if (nodeRect == null || wrapRect == null || !nodeRect.width || !wrapRect.width || marquee === false) {
return;
}
if (marquee || wrapRect.width < nodeRect.width) {
const speeding = marquee.speed || 50;
const delaying = marquee.delay || 0;
const animationDuration = ((wrapRect.width + nodeRect.width) / speeding) * 1000;
const firstAnimationDuration = (nodeRect.width / speeding) * 1000;
this.wrapWidth = Number(wrapRect.width);
this.nodeWidth = Number(nodeRect.width);
this.animationDuration = animationDuration;
this.delay = delaying;
this.loop = marquee.loop - 1;
this.firstAnimationDuration = firstAnimationDuration;
marquee.loop !== 0 && this.startScrollAnimation(true);
}
})
.catch(() => {});
});
},
startScrollAnimation(isFirstScroll = false) {
this.clearNoticeBarAnimation();
const { wrapWidth, nodeWidth, firstAnimationDuration, animationDuration, delay } = this;
const delayTime = isFirstScroll ? delay : 0;
const durationTime = isFirstScroll ? firstAnimationDuration : animationDuration;
// 滚动内容: 初始位置
this.animationData = this.resetAnimation
.translateX(isFirstScroll ? 0 : wrapWidth)
.step()
.export(),
setTimeout(() => {
// 滚动内容: 最终位置
this.animationData = uni
.createAnimation({ duration: durationTime, timingFunction: 'linear', delay: delayTime })
.translateX(-nodeWidth)
.step()
.export();
}, 20);
// 滚动一次完成, 开启下一次的滚动
this.nextAnimationContext = setTimeout(() => {
if (this.loop > 0) {
this.loop -= 1;
this.startScrollAnimation();
} else if (this.loop === 0) {
// 动画回到初始位置
this.animationData = this.resetAnimation.translateX(0).step()
.export();
} else if (this.loop < 0) {
this.startScrollAnimation();
}
}, durationTime + delayTime);
},
show() {
this.clearNoticeBarAnimation();
this.setPrefixIcon(this.prefixIcon);
this.initAnimation();
},
/** 清除动画 */
clearNoticeBarAnimation() {
this.nextAnimationContext && clearTimeout(this.nextAnimationContext);
this.nextAnimationContext = null;
},
setPrefixIcon(v) {
const { theme } = this;
this._prefixIcon = calcIcon(v, THEME_ICON[theme]);
},
onChange(e) {
const { current, source } = e.detail;
this.$emit('change', { current, source });
},
clickPrefixIcon() {
this.$emit('click', { trigger: 'prefix-icon' });
},
clickContent() {
this.$emit('click', { trigger: 'content' });
},
clickSuffixIcon() {
this.$emit('click', { trigger: 'suffix-icon' });
},
clickOperation() {
this.$emit('click', { trigger: 'operation' });
},
},
});
</script>
<style scoped>
@import './notice-bar.css';
</style>

View File

@@ -0,0 +1,71 @@
/* eslint-disable */
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
import type { TdNoticeBarProps } from './type';
export default {
/** 文本内容 */
content: {
type: [String, Array],
},
/** 滚动方向 */
direction: {
type: String,
default: 'horizontal' as TdNoticeBarProps['direction'],
validator(val: TdNoticeBarProps['direction']): boolean {
if (!val) return true;
return ['horizontal', 'vertical'].includes(val);
},
},
/** 间隔时间【仅在 direction='vertical' 有效】 */
interval: {
type: Number,
default: 2000,
},
/** 跑马灯效果。speed 指速度控制loop 指循环播放次数,值为 -1 表示循环播放,值为 0 表示不循环播放delay 表示延迟多久开始播放【仅在 direction='horizontal' 有效】 */
marquee: {
type: [Boolean, Object],
default: false as TdNoticeBarProps['marquee'],
},
/** 右侧额外信息 */
operation: {
type: String,
},
/** 前缀图标。值为字符串表示图标名称,值为 `false` 表示不显示前缀图标,值为 `Object` 类型,表示透传至 `icon`,不传表示使用主题图标 */
prefixIcon: {
type: [String, Boolean, Object],
default: true as TdNoticeBarProps['prefixIcon'],
},
/** 后缀图标。值为字符串表示图标名称。值为 `Object` 类型,表示透传至 `icon`,不传表示不显示后缀图标 */
suffixIcon: {
type: [String, Object],
},
/** 内置主题 */
theme: {
type: String,
default: 'info' as TdNoticeBarProps['theme'],
validator(val: TdNoticeBarProps['theme']): boolean {
if (!val) return true;
return ['info', 'success', 'warning', 'error'].includes(val);
},
},
/** 显示/隐藏 */
visible: {
type: Boolean,
default: undefined,
},
/** 显示/隐藏,非受控属性 */
defaultVisible: Boolean,
/** 当 `direction="vertical"` 时轮播切换时触发 */
onChange: {
type: Function,
default: () => ({}),
},
/** 点击事件 */
onClick: {
type: Function,
default: () => ({}),
},
};

View File

@@ -0,0 +1,71 @@
/* eslint-disable */
/**
* 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC
* */
export interface TdNoticeBarProps {
/**
* 文本内容
*/
content?: string | string[];
/**
* 滚动方向
* @default horizontal
*/
direction?: 'horizontal' | 'vertical';
/**
* 间隔时间【仅在 direction='vertical' 有效】
* @default 2000
*/
interval?: number;
/**
* 跑马灯效果。speed 指速度控制loop 指循环播放次数,值为 -1 表示循环播放,值为 0 表示不循环播放delay 表示延迟多久开始播放【仅在 direction='horizontal' 有效】
* @default false
*/
marquee?: boolean | NoticeBarMarquee;
/**
* 右侧额外信息
*/
operation?: string;
/**
* 前缀图标。值为字符串表示图标名称,值为 `false` 表示不显示前缀图标,值为 `Object` 类型,表示透传至 `icon`,不传表示使用主题图标
* @default true
*/
prefixIcon?: string | boolean | object;
/**
* 后缀图标。值为字符串表示图标名称。值为 `Object` 类型,表示透传至 `icon`,不传表示不显示后缀图标
*/
suffixIcon?: string | object;
/**
* 内置主题
* @default info
*/
theme?: 'info' | 'success' | 'warning' | 'error';
/**
* 显示/隐藏
* @default false
*/
visible?: boolean;
/**
* 显示/隐藏,非受控属性
* @default false
*/
defaultVisible?: boolean;
/**
* 当 `direction="vertical"` 时轮播切换时触发
*/
onChange?: (context: { current: number; source: '' | 'autoplay' | 'touch' }) => void;
/**
* 点击事件
*/
onClick?: (context: { trigger: NoticeBarTrigger }) => void;
}
export interface NoticeBarMarquee {
speed?: number;
loop?: number;
delay?: number;
}
export type NoticeBarTrigger = 'prefix-icon' | 'content' | 'operation' | 'suffix-icon';