Files
hospital-rescue-app-v2/uni_modules/tmx-ui/components/x-notice/x-notice.uvue
T
2026-09-24 16:25:22 +08:00

316 lines
8.4 KiB
Plaintext

<script lang="ts" setup>
import { ref, computed, watch, onMounted, onBeforeUnmount, onActivated, onDeactivated, nextTick } from "vue"
import { getUid } from '../../core/util/xCoreUtil.uts';
import { checkIsCssUnit, getUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { colors, getDefaultColor, getDefaultColorObj, getTextColorObj, getThinColorObj, setBgColorLightByDark } from "../../core/util/xCoreColorUtil.uts"
/**
* @name 通知栏 xNotice
* @description 速度可控,样式比较方便的调整。如果想要竖向的,请使用官方的轮播实现。
* @page /pages/index/notice
* @category 展示组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xNotice"})
const emits = defineEmits([
/**
* 项目被点击
* @param {number} index - 项目索引
*/
'click'
])
type xNoticePropsType = {
/**
* 速率
*/
speed: number,
/**
* 背景色
*/
color: string,
/**
* 暗黑时的背景色,如果不填写,取color暗黑浅背景。
*/
darkColor: string,
/**
* 文字和图标色
*/
fontColor: string,
/**
* 文字大小和图标大小
*/
fontSize: string,
/**
* 图标,如果为空将不显示 。
*/
icon: string,
/**
* 不填写的话取fontColor
*/
iconColor: string,
/**
* 不填写的话取fontSize
*/
iconSize: string,
/**
* 通知文本。
*/
label: string[]
}
const props = withDefaults(defineProps<xNoticePropsType>(), {
speed: 50,
color: "#ebf4ff",
darkColor: "",
fontColor: "primary",
fontSize: "14",
icon: "megaphone-line",
iconColor: "",
iconSize: "",
label: ():string[] =>[] as string[]
})
// 响应式数据
const realWidth = ref<number>(0)
const boxWidth = ref<number>(20000)
const parentBoxwidth = ref<number>(0)
const isOk = ref<boolean>(false)
const status = ref<string>('play')
const id = ref<string>("xNoticeLengWrap" + getUid())
const xNoticeLengBoxRef = ref<UniElement|null>(null)
const xNoticeLengWrapRef = ref<UniElement|null>(null)
const totalDur = ref<number>(0)
const tid = ref<number>(54)
const proxy = getCurrentInstance()?.proxy
// 计算属性
const _fontSize = computed((): string => {
let fontSize = checkIsCssUnit(props.fontSize, xConfig.unit);
if (xConfig.fontScale == 1) return fontSize;
let sizeNumber = parseInt(fontSize)
if (isNaN(sizeNumber)) {
sizeNumber = 14
}
return (sizeNumber * xConfig.fontScale).toString() + getUnit(fontSize)
})
const _iconSize = computed((): string => {
if(props.iconSize=='') return _fontSize.value;
return props.iconSize
})
const _fontColor = computed((): string => {
return getDefaultColor(props.fontColor)
})
const _iconColor = computed((): string => {
if(props.iconColor=='') return _fontColor.value;
return props.iconColor
})
const _bgColor = computed((): string => {
if (xConfig.dark == 'dark') {
if (props.darkColor != '') return getDefaultColor(props.darkColor)
let defaultObj : UTSJSONObject = getThinColorObj(setBgColorLightByDark(props.color), props.color, true).getJSON("default")!
return defaultObj.getString("background")!
}
return getDefaultColor(props.color)
})
const _icon = computed((): string => {
return props.icon
})
const _label = computed((): string[] => {
return props.label
})
// 方法
function itemClick(index: number): void {
/**
* 项目被点击
* @param {number} index 项目索引
*/
emits("click", index)
}
function getNodes(): void {
if (_label.value.length == 0) return;
uni.createSelectorQuery().in(proxy)
.selectAll(".xNoticeLengText")
.boundingClientRect().exec((ret) => {
let nodeinfo = ret[0] as NodeInfo[];
if (ret.length > 0) {
let arw = 0
for (let i = 0; i < nodeinfo.length; i++) {
let item = nodeinfo[i]
arw += item.width!;
}
let elewrap = xNoticeLengWrapRef.value
let boxEle = xNoticeLengBoxRef.value
if (elewrap == null||boxEle==null) return;
// let boxwidth = elewrap!.parentElement!.getBoundingClientRect().width
boxEle!.getBoundingClientRectAsync()!.then((rect)=>{
let boxwidth = rect.width
parentBoxwidth.value = boxwidth
// 如果内容宽度不超过容器,则不滚动
if (arw <= boxwidth) {
realWidth.value = arw;
isOk.value = true;
status.value = 'idle'
elewrap!.style.setProperty("transition-duration", '0ms')
elewrap!.style.setProperty("transform", `translateX(0px)`)
return;
}
realWidth.value = arw;
let pxPerSec = Math.max(10, props.speed)
let duration = Math.ceil((arw + boxwidth) / pxPerSec) * 1000
totalDur.value = duration
elewrap!.style.setProperty("transition-duration", `${totalDur.value}ms`)
elewrap!.style.setProperty("transition-delay", `1000ms`)
elewrap!.style.setProperty("transform", `translateX(-${arw}px)`)
isOk.value = true;
status.value = 'play'
})
}
})
}
function onEnd(): void {
clearTimeout(tid.value)
isOk.value = false;
let elewrap = xNoticeLengWrapRef.value
if (elewrap == null) return;
// 内容不满一屏时,不做循环滚动
if (status.value == 'idle') {
isOk.value = true;
return;
}
if (status.value == 'play') {
isOk.value = false;
elewrap!.style.setProperty("transition-delay", `0ms`)
elewrap.style.setProperty("transition-duration", "50ms")
elewrap.style.setProperty("transform", `translateX(${parentBoxwidth.value}px)`)
status.value = 'reset'
} else {
isOk.value = true;
if (_label.value.length == 0) return;
elewrap!.style.setProperty("transition-delay", `0ms`)
elewrap.style.setProperty("transition-duration", totalDur.value.toString() + 'ms')
elewrap.style.setProperty("transform", `translateX(-${realWidth.value}px)`)
status.value = 'play'
}
}
function onInit(): void {
// #ifdef APP
tid.value = setTimeout(function () {
getNodes();
// 部分机型 老旧点的要加大延长时间。
}, 350);
// #endif
// #ifdef WEB || MP-WEIXIN
getNodes();
// #endif
}
// 监听器
watch((): string[] => props.label, () => {
nextTick(() => {
getNodes();
})
})
// 生命周期
onMounted(() => {
onInit()
})
onBeforeUnmount(() => {
clearTimeout(tid.value)
})
onActivated(() => {
// #ifdef WEB
isOk.value = true;
let elewrap = uni.getElementById(id.value) as UniElement;
if (elewrap == null) return;
if (_label.value.length == 0) return;
if (status.value == 'idle') {
// 不滚动,保持原位
elewrap.style.setProperty("transition-duration", '0ms')
elewrap.style.setProperty("transform", `translateX(0px)`)
return;
}
elewrap.style.setProperty("transition-duration", totalDur.value.toString() + 'ms')
elewrap.style.setProperty("transform", `translateX(0px)`)
status.value = 'play'
tid.value = setTimeout(function() {
elewrap.style.setProperty("transform", `translateX(-${realWidth.value}px)`)
}, 150);
// #endif
})
onDeactivated(() => {
// #ifdef WEB
clearTimeout(tid.value)
// #endif
})
</script>
<template>
<view class="xNoticeLeng" :style="{backgroundColor:_bgColor}">
<x-icon v-if="_icon!=''" :name="_icon" :color="_iconColor" :fontSize="_iconSize"
style="margin-right: 8px;"></x-icon>
<view class="xNoticeLengBox" id="xNoticeLengBox" ref="xNoticeLengBoxRef">
<view @transitionend="onEnd" :id="id" ref="xNoticeLengWrapRef" class="xNoticeLengWrap"
:style="{visibility:isOk?'visible':'hidden',width:(isOk?realWidth:boxWidth)+'px'}">
<text @click="itemClick(index)" v-for="(item,index) in _label" :key="index" class="xNoticeLengText"
:style="{fontSize:_fontSize,color:_fontColor}">{{item}}</text>
</view>
</view>
</view>
</template>
<style scoped>
.xNoticeLeng {
display: flex;
flex-wrap: nowrap;
flex-direction: row;
align-items: center;
padding: 0px 12px;
border-radius: 6px;
}
.xNoticeLengBox {
flex: 1;
overflow: hidden;
}
.xNoticeLengText {
padding: 12px 0;
/* #ifdef MP-WEIXIN */
display:inline-flex;
white-space:nowrap;
/* #endif */
}
.xNoticeLengWrap {
flex-direction: row;
flex-wrap: nowrap;
transition-property: transform;
transition-timing-function: linear;
transition-duration: 0ms;
/* #ifndef APP */
will-change: transform;
/* #endif */
}
</style>