This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
@@ -0,0 +1 @@
export type positionType = "top" | "top-left" | "top-right" | "bottom" | "bottom-left" | "bottom-right"
@@ -0,0 +1,252 @@
<script lang="ts" setup>
import { ref, computed, watch, onMounted, onBeforeUnmount } from "vue"
import { checkIsCssUnit, getUid } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, colorAddDeepen } from "../../core/util/xCoreColorUtil.uts"
import { xConfig } from "../../config/xConfig.uts"
import { SNACKBAR_INFO, SNACKBAR_ITEM } from '../../interface.uts';
import {positionType} from "./interface.uts"
/**
* @name 消息条 xSnackbar
* @description 消息条是可以在上方或者正文累加显示,新的消息在旧的前面。而不会只显示一条,会自动消失。支持6个方向的弹出
* @page /pages/index/snackbar
* @category 反馈组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xSnackbar"})
type xSnackbarPropsType = {
/**
* 距离顶/底的偏移量。
*/
offset: string,
/**
* 是否让消息条横屏占满,默认是根据内容自动宽。
*/
block: boolean,
/**
* 消息数据
* 注意,id一定要提供且是数字,可以随意,只要相对上一次变更下,就会触发
* 显示新的消息条。这种显示的方式就是避免你们引用ref方式来调用方法,相对更简单。
*/
content: SNACKBAR_INFO,
/**
* 多少毫秒后销毁
*/
duration: number,
/**
* 出现的位置
* top:正上方
* top-left:左上方
* top-right:右上角
* bottom-right:右下角
* bottom-left:左下角
* bottom:正下方
*/
position: positionType
}
// const props = withDefaults(defineProps<xSnackbarPropsType>(), {
// offset: '12px',
// block: false,
// content: ():SNACKBAR_INFO=>({
// background: "",
// color: "",
// fontSize: "",
// content: "",
// id: -1,
// icon: ""
// } as SNACKBAR_INFO),
// duration: 2500,
// position: 'bottom'
// })
const props = defineProps({
/**
* 距离顶/底的偏移量。
*/
offset:{
type:String,
default:"12px"
},
/**
* 出现的位置
* top:正上方
* top-left:左上方
* top-right:右上角
* bottom-right:右下角
* bottom-left:左下角
* bottom:正下方
*/
position:{
type:String as PropType<positionType>,
default:"12px"
},
/**
* 多少毫秒后销毁
*/
duration:{
type:Number,
default:2500
},
/**
* 是否让消息条横屏占满,默认是根据内容自动宽。
*/
block:{
type:Boolean,
default:false
},
/**
* 消息数据
* 注意,id一定要提供且是数字,可以随意,只要相对上一次变更下,就会触发
* 显示新的消息条。这种显示的方式就是避免你们引用ref方式来调用方法,相对更简单。
*/
content:{
type:Object as PropType<SNACKBAR_INFO>,
default:():SNACKBAR_INFO=>({
background: "",
color: "",
fontSize: "",
content: "",
id: -1,
icon: ""
} as SNACKBAR_INFO)
}
})
// 响应式数据
const _width = ref(0)
const _height = ref('100%')
const windowTop = ref(0)
/** 缓存历史记录消息条。 */
const list = ref<SNACKBAR_ITEM[]>([])
// 计算属性
const _offset = computed((): string => {
let lt = checkIsCssUnit(props.offset, 'rpx');
if (props.position.indexOf('top') > -1) return 'top:' + lt
return 'bottom:' + lt
})
const _block = computed((): string => {
return 'flex:' + (props.block ? '1' : 'none')
})
const _position = computed((): string => {
let pos = new Map<string, string>([
['top', 'xSnackbarTop'],
['top-left', 'xSnackbarTopLeft'],
['top-right', 'xSnackbarTopRight'],
['bottom', 'xSnackbarBottom'],
['bottom-left', 'xSnackbarBottomLeft'],
['bottom-right', 'xSnackbarBottomRight']
])
let realposClass = pos.get(props.position);
return realposClass == null ? 'xSnackbarBottom' : (realposClass!)
})
// 方法
function setListContent(item: SNACKBAR_INFO): void {
list.value.unshift({
id: item.id,
content: item.content,
fontSize: item.fontSize != null ? (item.fontSize as string) : "14px",
background: item.background != null ? (item.background as string) : "black",
color: item.color != null ? (item.color as string) : "white",
icon: item.icon != null ? (item.icon as string) : "information-line",
} as SNACKBAR_ITEM)
}
function itemClose(index: number): void {
list.value.splice(index, 1)
}
// 监听器
watch((): SNACKBAR_INFO => props.content, (newValue: SNACKBAR_INFO, oldValue: SNACKBAR_INFO) => {
if (newValue.id != -1 && oldValue.id != newValue.id && newValue.content != '') {
setListContent(newValue)
}
}, { deep: true })
// 生命周期
onMounted(() => {
let sys = uni.getWindowInfo()
// #ifdef WEB
_width.value = sys.windowWidth
windowTop.value = sys.windowTop;
_height.value = `calc(100% - ${sys.windowTop}px)`;
// #endif
// #ifdef APP
_width.value = sys.windowWidth
_height.value = '100%';
// #endif
})
onBeforeUnmount(() => {
list.value = [] as SNACKBAR_ITEM[]
})
</script>
<template>
<view v-if="list.length>0" class="x-snackbar" :class="[_position]"
:style="{top:windowTop+'px',width:'100%',height:_height}">
<view class="xSnackbarBox" :style="[_offset,_block]">
<x-snackbar-item :duration="duration" class="xSnackbarBoxITEMS_OTYPE" :key-index="index" @close="itemClose(index)"
v-for="(item,index) in list" :key="item.id" :content="item"></x-snackbar-item>
</view>
</view>
</template>
<style scoped>
.x-snackbar {
pointer-events: none;
position: fixed;
left: 0;
top: 0;
background-color: transparent;
display: flex;
flex-direction: row;
justify-content: center;
z-index: 5;
}
.xSnackbarBoxITEMS_OTYPE {
pointer-events: auto;
}
.xSnackbarBox {
margin-right: 16px;
margin-left: 16px;
display: flex;
flex-direction: column;
justify-content: center;
}
.xSnackbarTop {
align-items: flex-start;
}
.xSnackbarTopLeft {
align-items: flex-start;
justify-content: flex-start;
}
.xSnackbarTopRight {
align-items: flex-start;
justify-content: flex-end;
}
.xSnackbarBottom {
align-items: flex-end;
}
.xSnackbarBottomLeft {
align-items: flex-end;
justify-content: flex-start;
}
.xSnackbarBottomRight {
align-items: flex-end;
justify-content: flex-end;
}
</style>