Files
2026-09-24 16:25:22 +08:00

426 lines
11 KiB
Plaintext

<script lang="ts" setup>
import { PropType, onMounted } from "vue"
import { getUid } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, setBgColorLightByDark, isBlackAndWhite, hexToRgb } from "../../core/util/xCoreColorUtil.uts"
import { checkIsCssUnit } from "../../core/util/xCoreUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { xProvitae } from "../../config/xConfig.uts"
/**
* @name 标题导航 xNavbar
* @description 标题导航,用途页面头部标题。可以默认透明,滚动实现背景可变的标题导航。
* @page /pages/index/navbar
* @category 导航组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xNavbar"})
const emits = defineEmits([
/**
* 当滚动页面时,导航栏位置
* 与滚动之间的距离差比(0~1)
* 0表示在顶部,1表示已经超过了导航高
* 主要是用来类型滚动时设置导航顶部的一些布局变化。
*/
'fiexdChange',
/**
* 初始化完成向下发送一个事件用于告知本组件实际的高
* @param {number} height -单位px,组件高。
*/
'init'
])
const props = defineProps(
{
/**
* 是否底部占位内容,如果为false底部悬空。你的页面内容将自动压在导航栏的底部。
*/
isPlace: {
type: Boolean,
default: true
},
/**
* 背景颜色,注意这是静态时的背景色。
*/
bgColor: {
type: String,
default: 'white'
},
/**
* 暗黑的背景颜色。
*/
darkBgColor: {
type: String,
default: "#000000"
},
/**
* 背景颜色,这是滑动时超过指定本状态栏高度时自动渐变到此颜色
* 如果为空时,不会有动态背景
* 如果提供的是白或者黑,暗黑时自动取反。
*/
activeBgColor: {
type: String,
default: ''
},
/**
* 返回按钮的颜色.默认是取titleColor,如果你单独定义了
* 以你定义的为准
*/
backColor: {
type: String,
default: ''
},
/**
* 标题
*/
title: {
type: String,
default: '标题'
},
/**
* 默认标题颜色,暗黑是取白,如果有其它需求建议插槽。
*/
titleColor: {
type: String,
default: '#333333'
},
/**
* 动态悬浮时标题颜色,如果为暗黑时,你提供的颜色为白或者黑会反色
* 如果提供的是彩色自动加深或者提亮
*/
titleActiveColor: {
type: String,
default: '#333333'
},
/**
* 标题文字大小
*/
titleFontSize: {
type: String,
default: '17',
},
/**
* 右边的宽度。
*/
lrWidth: {
type: String,
default: '100',
},
/**
* 左边的宽度。
*/
llWidth: {
type: String,
default: '100',
},
/**
* 层级。
*/
zIndex: {
type: Number,
default: 90,
},
/**
* 左边按钮默认点击返回上页。
* 但如果上页返回失败(通常见于直接程序启动本面,无法进行上页返回时)
* 失败后返回的页面。默认是首页。
*/
backErrorPath: {
type: String,
default: '/pages/index/index'
},
/**
* 是否显示返回按钮。
*/
showNavBack: {
type: Boolean,
default: true
},
/**
* 渐变背景,如果提供,上面的BgColor背景和暗黑背景将失效。
* 例:['to right','#ff667f','#ff5416']
*/
linearGradient: {
type: Array as PropType<string[]>,
default: () : string[] => [] as string[]
},
/**
* 动态悬浮时的渐变背景,
* 提供后上面的 activeBgColor的背景和暗黑背景将失效。
* 例:['to right','#ff667f','#ff5416']
*/
linearActiveGradient: {
type: Array as PropType<string[]>,
default: () : string[] => [] as string[]
},
/**
* 静态在顶部时是否透明背景
*/
staticTransparent: {
type: Boolean,
default: false
},
/**
* 某些时候你可能需要设定最大宽
* 任意单位;设置为none时表示不设置.
* 仅web生效
*/
maxWidth:{
type:String,
default:'none'
},
/**
* 这个高不是最终高,它只是导航高
* 状态栏高是自动的不包含在这里面.
* 单位为像素单位,不是rpx
*/
height:{
type:Number,
default: 50
}
}
)
const statusBarHeight = ref(44)
const navbarHeight = computed(():number=>props.height)
const isFiexd = ref(false)
const fiexRatio = ref(1)
const _stop = ref(0)
const _linearGradient = computed(() : string => {
if (props.linearGradient.length < 3) return '';
let str = props.linearGradient.join(",");
return `linear-gradient(${str})`
})
const _linearActiveGradient = computed(() : string => {
if (props.linearActiveGradient.length < 3) return '';
let str = props.linearActiveGradient.join(",");
return `linear-gradient(${str})`
})
const _lrWidth = computed(() : string => checkIsCssUnit(props.lrWidth, xConfig.unit))
const _llWidth = computed(() : string => checkIsCssUnit(props.llWidth, xConfig.unit))
const _titleFontSize = computed(() : string => checkIsCssUnit(props.titleFontSize, xConfig.unit))
const _bgColor = computed(() : string => {
let bgcolor = props.bgColor
if (xConfig.dark == 'dark') {
bgcolor = props.darkBgColor == "" ? "#000000" : props.darkBgColor
}
return getDefaultColor(bgcolor)
})
const _maxWidth = computed(():string=>{
let w = 'none'
if(props.maxWidth!=''&&props.maxWidth=='none'){
w = props.maxWidth
}
// #ifndef WEB
w = 'none'
// #endif
return w=='none'? w : checkIsCssUnit(props.maxWidth,xConfig.unit)
})
const _activeBgColor = computed(() : string => {
let color = _bgColor.value
if (props.activeBgColor != "") {
color = getDefaultColor(props.activeBgColor)
}
let isba = isBlackAndWhite(props.titleActiveColor)
if (isba) {
if (xConfig.dark == 'dark') {
color = setBgColorLightByDark(color)
}
}
return color
})
const _titleColor = computed(() : string => {
if (xConfig.dark == 'dark') return "#ffffff"
return getDefaultColor(props.titleColor)
})
const _backColor = computed(() : string => {
if (props.backColor != '') return getDefaultColor(props.backColor)
return _titleColor.value
})
const _titleActiveColor = computed(() : string => {
let isba = isBlackAndWhite(props.titleActiveColor)
if (isba) {
if (xConfig.dark == 'dark') return "#ffffff"
}
return getDefaultColor(props.titleActiveColor)
})
const _title = computed(() : string => props.title)
const _styleMap = computed(() : Map<string, string> => {
let maps = new Map<string, string>();
if (props.staticTransparent) {
maps.set('opacity', fiexRatio.value.toString());
} else {
maps.set('opacity', '1');
}
maps.set('height', (statusBarHeight.value + navbarHeight.value) + 'px');
if (isFiexd.value) {
if (_linearActiveGradient.value != '') {
maps.set('background-image', _linearActiveGradient.value)
} else if (_activeBgColor.value != '') {
maps.set('background-color', _activeBgColor.value)
} else {
maps.set('background-color', _bgColor.value)
}
} else {
if (_linearGradient.value != '') {
maps.set('background-image', _linearGradient.value)
} else {
maps.set('background-color', _bgColor.value)
}
}
return maps
})
onMounted(() => {
let sys = uni.getWindowInfo();
statusBarHeight.value = sys.statusBarHeight
emits('init', statusBarHeight.value + navbarHeight.value)
if (props.staticTransparent) {
fiexRatio.value = 0
}
})
onPageScroll((evt : OnPageScrollOptions) => {
_stop.value = evt.scrollTop;
})
watchEffect(() => {
let barheight = statusBarHeight.value + navbarHeight.value;
isFiexd.value = _stop.value > 0
if (props.staticTransparent) {
fiexRatio.value = Math.max(Math.min(_stop.value / barheight, 1), 0)
}
emits("fiexdChange", fiexRatio.value)
})
const backGo = () => {
// #ifdef WEB
window.history.back()
// #endif
// #ifndef WEB
uni.navigateBack({
fail() {
uni.reLaunch({
url: props.backErrorPath
})
}
})
// #endif
}
</script>
<template>
<view>
<view class="xNavbar" :style="{zIndex:zIndex,maxWidth:_maxWidth}">
<view :key="_linearActiveGradient" :style="_styleMap" class="xNavbarBg"></view>
<view class="xNavbarContentBox">
<view :style="{height:(statusBarHeight)+'px'}"></view>
<view class="xNavbarContent" :style="{height:(navbarHeight)+'px'}">
<view class="xNavBarLeft" :style="{width:_llWidth}">
<view v-if="showNavBack" @click="backGo" class="xNavBarBack" style="paddingLeft: 6px;">
<x-icon font-size="28" name="arrow-left-s-line"
:color="isFiexd?_titleActiveColor:_backColor"></x-icon>
</view>
<!--
@slot 左边插槽
@prop {boolean} isFiexd - 当前是处于悬浮状态还是静态顶部
-->
<slot name="left" :isFiexd="isFiexd"></slot>
</view>
<view class="xNavBarTtitle">
<!--
@slot 标题插槽
@prop {boolean} isFiexd - 当前是处于悬浮状态还是静态顶部
-->
<slot name="title" :isFiexd="isFiexd">
<text class="xNvbarTtitleOverls" :style="{fontSize:_titleFontSize,fontWeight:'bold',color:isFiexd?_titleActiveColor:_titleColor}">{{_title}}</text>
</slot>
</view>
<view class="xNavBarRight" :style="{width:_lrWidth}">
<!--
@slot 右边插槽
@prop {boolean} isFiexd - 当前是处于悬浮状态还是静态顶部
-->
<slot name="right" :isFiexd="isFiexd"></slot>
</view>
</view>
</view>
</view>
<view v-if="isPlace" :style="{height:(statusBarHeight+navbarHeight)+'px'}"></view>
</view>
</template>
<style scoped>
.xNvbarTtitleOverls{
/* #ifdef APP */
lines:1;
text-overflow: ellipsis;
/* #endif */
/* #ifndef APP */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
width:100%;
/* #endif */
text-align: center;
}
.xNavbarContentBox {
position: absolute;
top: 0;
left: 0;
width: 100%;
}
.xNavBarBack {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
/* background-image: linear-gradient; */
}
.xNavbar {
/* transition-duration: 500ms;
transition-property: background-color;
transition-timing-function: linear; */
position: fixed;
width: 100%;
}
.xNavbarContent {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.xNavBarLeft {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
}
.xNavBarRight {
display: flex;
flex-direction: row;
justify-content: flex-end;
align-items: center;
}
.xNavBarTtitle {
flex: 1;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
height: 100%;
}
</style>