商城网站建设案例网络策划营销
UVIEW-UI兼容多端好用,自己使用时遇到几个问题:
1)Toast组件在有popup组件时,会出现Toast看不到的问题
2)原代码是javascript写的,更改一下适配Typescript,尤其是type指定的方式
3)由于是暗色色系,需要重新定义一下皮肤
本着解决这几个问题的思路,就读了下uview-ui的toast组件u-toast.vue源码:
u-toast.vue:
<template><view class="u-toast"><u-overlay:show="isShow":zIndex="tmpConfig.overlay ? 10070 : -1":custom-style="overlayStyle"><viewclass="u-toast__content":style="[contentStyle]":class="['u-type-' + tmpConfig.type, (tmpConfig.type === 'loading' || tmpConfig.loading) ? 'u-toast__content--loading' : '']"><u-loading-iconv-if="tmpConfig.type === 'loading'"mode="circle"color="rgb(255, 255, 255)"inactiveColor="rgb(120, 120, 120)"size="25"></u-loading-icon><u-iconv-else-if="tmpConfig.type !== 'defalut' && iconName":name="iconName"size="17":color="tmpConfig.type":customStyle="iconStyle"></u-icon><u-gapv-if="tmpConfig.type === 'loading' || tmpConfig.loading"height="12"bgColor="transparent"></u-gap><textclass="u-toast__content__text":class="['u-toast__content__text--' + tmpConfig.type]"style="max-width: 400rpx;">{{ tmpConfig.message }}</text></view></u-overlay></view>
</template><script>import { mpMixin } from '../../libs/mixin/mpMixin';import { mixin } from '../../libs/mixin/mixin';import { os, getWindowInfo, deepMerge, type2icon } from '../../libs/function/index';import color from '../../libs/config/color';import { hexToRgb } from '../../libs/function/colorGradient';/*** toast 消息提示* @description 此组件表现形式类似uni的uni.showToastAPI,但也有不同的地方。* @tutorial https://ijry.github.io/uview-plus/components/toast.html* @property {String | Number} zIndex toast展示时的zIndex值 (默认 10090 )* @property {Boolean} loading 是否加载中 (默认 false )* @property {String | Number} message 显示的文字内容* @property {String} icon 图标,或者绝对路径的图片* @property {String} type 主题类型 (默认 default)* @property {Boolean} show 是否显示该组件 (默认 false)* @property {Boolean} overlay 是否显示透明遮罩,防止点击穿透 (默认 true )* @property {String} position 位置 (默认 'center' )* @property {Object} params 跳转的参数 * @property {String | Number} duration 展示时间,单位ms (默认 2000 )* @property {Boolean} isTab 是否返回的为tab页面 (默认 false )* @property {String} url toast消失后是否跳转页面,有则跳转,优先级高于back参数 * @property {Function} complete 执行完后的回调函数 * @property {Boolean} back 结束toast是否自动返回上一页 (默认 false )* @property {Object} customStyle 组件的样式,对象形式* @event {Function} show 显示toast,如需一进入页面就显示toast,请在onReady生命周期调用* @example <u-toast ref="uToast" />*/export default {name: 'u-toast',mixins: [mpMixin, mixin],data() {return {isShow: false,timer: null, // 定时器config: {message: '', // 显示文本type: '', // 主题类型,primary,success,error,warning,blackduration: 2000, // 显示的时间,毫秒icon: true, // 显示的图标position: 'center', // toast出现的位置complete: null, // 执行完后的回调函数overlay: true, // 是否防止触摸穿透loading: false, // 是否加载中状态},tmpConfig: {}, // 将用户配置和内置配置合并后的临时配置变量}},computed: {iconName() {// 只有不为none,并且type为error|warning|succes|info时候,才显示图标if(!this.tmpConfig.icon || this.tmpConfig.icon == 'none') {return '';}if (this.tmpConfig.icon === true) {if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) {return type2icon(this.tmpConfig.type)} else {return ''}} else {return this.tmpConfig.icon}},overlayStyle() {const style = {justifyContent: 'center',alignItems: 'center',display: 'flex'}// 将遮罩设置为100%透明度,避免出现灰色背景style.backgroundColor = 'rgba(0, 0, 0, 0)'return style},iconStyle() {const style = {}// 图标需要一个右边距,以跟右边的文字有隔开的距离style.marginRight = '4px'// #ifdef APP-NVUE// iOSAPP下,图标有1px的向下偏移,这里进行修正if (os() === 'ios') {style.marginTop = '-1px'}// #endifreturn style},loadingIconColor() {let colorTmp = 'rgb(255, 255, 255)'if (['error', 'warning', 'success', 'primary'].includes(this.tmpConfig.type)) {// loading-icon组件内部会对color参数进行一个透明度处理,该方法要求传入的颜色值// 必须为rgb格式的,所以这里做一个处理colorTmp = hexToRgb(color[this.tmpConfig.type])}return colorTmp},// 内容盒子的样式contentStyle() {const windowHeight = getWindowInfo().windowHeight, style = {}let value = 0// 根据top和bottom,对Y轴进行窗体高度的百分比偏移if(this.tmpConfig.position === 'top') {value = - windowHeight * 0.25} else if(this.tmpConfig.position === 'bottom') {value = windowHeight * 0.25}style.transform = `translateY(${value}px)`return style}},created() {// 通过主题的形式调用toast,批量生成方法函数['primary', 'success', 'error', 'warning', 'default', 'loading'].map(item => {this[item] = message => this.show({type: item,message})})},methods: {// 显示toast组件,由父组件通过this.$refs.xxx.show(options)形式调用show(options) {// 不将结果合并到this.config变量,避免多次调用u-toast,前后的配置造成混乱this.tmpConfig = deepMerge(this.config, options)// 清除定时器this.clearTimer()this.isShow = true// -1时不自动关闭if (this.tmpConfig.duration !== -1) {this.timer = setTimeout(() => {// 倒计时结束,清除定时器,隐藏toast组件this.clearTimer()// 判断是否存在callback方法,如果存在就执行typeof(this.tmpConfig.complete) === 'function' && this.tmpConfig.complete()}, this.tmpConfig.duration)}},// 隐藏toast组件,由父组件通过this.$refs.xxx.hide()形式调用hide() {this.clearTimer()},clearTimer() {this.isShow = false// 清除定时器clearTimeout(this.timer)this.timer = null}},beforeUnmount() {this.clearTimer()}}
</script><style lang="scss" scoped>@import "../../libs/css/components.scss";$u-toast-color:#fff !default;$u-toast-border-radius:4px !default;$u-toast-border-background-color:#585858 !default;$u-toast-border-font-size:14px !default;$u-toast-border-padding:12px 20px !default;$u-toast-loading-border-padding: 20px 20px !default;$u-toast-content-text-color:#fff !default;$u-toast-content-text-font-size:15px !default;$u-toast-u-icon:10rpx !default;$u-toast-u-type-primary-color:$u-primary !default;$u-toast-u-type-primary-background-color:#ecf5ff !default;$u-toast-u-type-primary-border-color:rgb(215, 234, 254) !default;$u-toast-u-type-primary-border-width:1px !default;$u-toast-u-type-success-color: $u-success !default;$u-toast-u-type-success-background-color: #dbf1e1 !default;$u-toast-u-type-success-border-color: #BEF5C8 !default;$u-toast-u-type-success-border-width: 1px !default;$u-toast-u-type-error-color:$u-error !default;$u-toast-u-type-error-background-color:#fef0f0 !default;$u-toast-u-type-error-border-color:#fde2e2 !default;$u-toast-u-type-error-border-width: 1px !default;$u-toast-u-type-warning-color:$u-warning !default;$u-toast-u-type-warning-background-color:#fdf6ec !default;$u-toast-u-type-warning-border-color:#faecd8 !default;$u-toast-u-type-warning-border-width: 1px !default;$u-toast-u-type-default-color:#fff !default;$u-toast-u-type-default-background-color:#585858 !default;.u-toast {&__content {@include flex;padding: $u-toast-border-padding;border-radius: $u-toast-border-radius;background-color: $u-toast-border-background-color;color: $u-toast-color;align-items: center;/* #ifndef APP-NVUE */max-width: 600rpx;/* #endif */position: relative;&--loading {flex-direction: column;padding: $u-toast-loading-border-padding;}&__text {color: $u-toast-content-text-color;font-size: $u-toast-content-text-font-size;line-height: $u-toast-content-text-font-size;&--default {color: $u-toast-content-text-color;}&--error {color: $u-error;}&--primary {color: $u-primary;}&--success {color: $u-success;}&--warning {color: $u-warning;}}}}.u-type-primary {color: $u-toast-u-type-primary-color;background-color: $u-toast-u-type-primary-background-color;border-color: $u-toast-u-type-primary-border-color;border-width: $u-toast-u-type-primary-border-width;}.u-type-success {color: $u-toast-u-type-success-color;background-color: $u-toast-u-type-success-background-color;border-color: $u-toast-u-type-success-border-color;border-width: 1px;}.u-type-error {color: $u-toast-u-type-error-color;background-color: $u-toast-u-type-error-background-color;border-color: $u-toast-u-type-error-border-color;border-width: $u-toast-u-type-error-border-width;}.u-type-warning {color: $u-toast-u-type-warning-color;background-color: $u-toast-u-type-warning-background-color;border-color: $u-toast-u-type-warning-border-color;border-width: 1px;}.u-type-default {color: $u-toast-u-type-default-color;background-color: $u-toast-u-type-default-background-color;}
</style>
然后开始解决问题:
Typescript适配
这个直接丢给AI就行了,GPT呼啦啦一会就翻译好了。当然还有几个BUG,类型不对什么的错误,也不是GPT的问题,强类型的时候有些转换指定没处理好,要手动改一下。
popup弹窗遮挡
关键问题在源码的
tmpConfig.overlay ? 10070 : -1
这里,由于toast的实现原理是利用遮罩层改造,在遮罩层中间放一个提示文本。那么遮罩层在显示的时候,层级是10070。而uview的popup的层级比10070要高(例如:10075),所以在有窗口的时候,toast弹出来就看不到了
重新定义一下皮肤
看下uview toast的参数用法:
API
#Params
这些参数为通过ref
调用<toast/>
组件内部的show
方法时,需要传递参数
参数 | 说明 | 类型 | 默认值 | 可选值 |
---|---|---|---|---|
loading | 是否加载中 | Boolean | false | true |
message | 显示的文本 | String | Number | - | - |
icon | 图标,或者绝对路径的图片 | String | - | - |
position | toast出现的位置 | String | center | top / bottom |
type | 主题类型 | String | - | - |
params | 跳转的参数 | Object | - | - |
duration | 展示时间,单位ms, 值为-1时不自动关闭 | String | Number | 2000 | - |
complete | 执行完后的回调函数 | Function | null | - |
这里type就是指定的风格类型,按照源码的解释,有5类:
'error' | 'warning' | 'success' | 'primary' | 'default'
对应的5个风格 类型,而这些是风格类型对应的色彩配置:
.u-type-primary {color: $u-toast-u-type-primary-color;background-color: $u-toast-u-type-primary-background-color;border-color: $u-toast-u-type-primary-border-color;border-width: $u-toast-u-type-primary-border-width;}.u-type-success {color: $u-toast-u-type-success-color;background-color: $u-toast-u-type-success-background-color;border-color: $u-toast-u-type-success-border-color;border-width: 1px;}.u-type-error {color: $u-toast-u-type-error-color;background-color: $u-toast-u-type-error-background-color;border-color: $u-toast-u-type-error-border-color;border-width: $u-toast-u-type-error-border-width;}.u-type-warning {color: $u-toast-u-type-warning-color;background-color: $u-toast-u-type-warning-background-color;border-color: $u-toast-u-type-warning-border-color;border-width: 1px;}.u-type-default {color: $u-toast-u-type-default-color;background-color: $u-toast-u-type-default-background-color;}
看源码,除了这些类型外,还有
u-type-<type>和u-toast__content__text--<type>
这两个与type有关的参数化样式,分别作用于弹出层和弹出层文本。跟默认的用法没啥作用,实际用于样式扩展。例如我还可以定义一个hello的type。那么此时设置弹窗的背景色和文本颜色,则需要通过全局定义u-type-hello和u-toast__content__text--hello样式来设定。
不过个人觉得没啥扩展的必要,重写组件时拿掉这两个部分。而对于标准的那几个type则通过全局覆盖同名.u-type-primary .u-type-success .u-type-error .u-type-warning .u-type-default这几个样式来实现,至于配色,指定背景色后直接问GPT就好,比自己想要好很多
完成
下面是完成后的代码,与原uviewui-plus的toast用法完全一致:
<template><view class="u-toast"><u-overlay :show="isShow" :zIndex="tmpConfig.overlay ? 20099 : -1" :custom-style="overlayStyle"><viewclass="u-toast__content":style="[contentStyle]":class="['u-type-' + tmpConfig.type, tmpConfig.loading ? 'u-toast__content--loading' : '']"><u-loading-iconv-if="tmpConfig.loading"mode="circle"color="white"inactiveColor="rgb(120, 120, 120)"size="25"/><u-iconv-else-if="tmpConfig.type !== 'default' && iconName":name="iconName"size="17":color="tmpConfig.type":customStyle="iconStyle"/><u-gap v-if="tmpConfig.loading" height="12" bgColor="transparent" /><text style="max-width: 400rpx">{{ tmpConfig.message }}</text></view></u-overlay></view>
</template><script setup lang="ts">
import { ref, reactive, computed, onBeforeUnmount } from 'vue'
import { deepMerge, type2icon, os, getWindowInfo } from 'uview-plus/libs/function/index'type ToastType = 'default' | 'error' | 'warning' | 'success' | 'primary'const isShow = ref(false)
const timer = ref<null | ReturnType<typeof setTimeout>>(null)
const config = reactive({message: '',type: 'default' as ToastType,duration: 2000,icon: undefined as string,position: 'center',complete: null as (() => void) | null,overlay: true,loading: false
})
const tmpConfig = reactive({ ...config })const iconName = computed(() => {if (!tmpConfig.icon && ['error', 'warning', 'success', 'primary'].includes(tmpConfig.type)) {// 不设置icon,默认就是自动识别return type2icon(tmpConfig.type)}if (tmpConfig.icon === 'none') return '' // none就是没有return tmpConfig.icon
})const overlayStyle = computed(() => ({justifyContent: 'center',alignItems: 'center',display: 'flex',backgroundColor: 'rgba(0, 0, 0, 0)'
}))const iconStyle = computed(() => ({marginRight: '4px',...(os() === 'ios' ? { marginTop: '-1px' } : {})
}))
const contentStyle = computed(() => {const windowHeight = getWindowInfo().windowHeightlet value = 0if (tmpConfig.position === 'top') {value = -windowHeight * 0.25} else if (tmpConfig.position === 'bottom') {value = windowHeight * 0.25}return { transform: `translateY(${value}px)` }
})function clearTimer() {isShow.value = falseif (timer.value) {clearTimeout(timer.value)timer.value = null}
}onBeforeUnmount(() => {clearTimer()
})// expose
defineExpose({show: async (options: Partial<typeof config>) => {Object.assign(tmpConfig, deepMerge(config, options))clearTimer()isShow.value = trueif (tmpConfig.duration !== -1) {timer.value = setTimeout(() => {clearTimer()tmpConfig.complete?.()}, tmpConfig.duration)}},hide: async () => {clearTimer()}
})
</script><style lang="scss">
@import 'uview-plus/libs/css/components'; // 样式部分保持不变$u-toast-color: #fff !default;
$u-toast-border-radius: 4px !default;
$u-toast-border-background-color: #585858 !default;
$u-toast-border-font-size: 14px !default;
$u-toast-border-padding: 12px 20px !default;
$u-toast-loading-border-padding: 20px 20px !default;
$u-toast-content-text-color: #fff !default;
$u-toast-content-text-font-size: 15px !default;
$u-toast-u-icon: 10rpx !default;
$u-toast-u-type-primary-color: $u-primary !default;
$u-toast-u-type-primary-background-color: #ecf5ff !default;
$u-toast-u-type-primary-border-color: rgb(215 234 254) !default;
$u-toast-u-type-primary-border-width: 1px !default;
$u-toast-u-type-success-color: $u-success !default;
$u-toast-u-type-success-background-color: #dbf1e1 !default;
$u-toast-u-type-success-border-color: #bef5c8 !default;
$u-toast-u-type-success-border-width: 1px !default;
$u-toast-u-type-error-color: $u-error !default;
$u-toast-u-type-error-background-color: #fef0f0 !default;
$u-toast-u-type-error-border-color: #fde2e2 !default;
$u-toast-u-type-error-border-width: 1px !default;
$u-toast-u-type-warning-color: $u-warning !default;
$u-toast-u-type-warning-background-color: #fdf6ec !default;
$u-toast-u-type-warning-border-color: #faecd8 !default;
$u-toast-u-type-warning-border-width: 1px !default;
$u-toast-u-type-default-color: #fff !default;
$u-toast-u-type-default-background-color: #585858 !default;.u-toast {&__content {@include flex;position: relative;align-items: center;/* #ifndef APP-NVUE */max-width: 600rpx;/* #endif */padding: $u-toast-border-padding;color: $u-toast-color;background-color: $u-toast-border-background-color;border-radius: $u-toast-border-radius;&--loading {flex-direction: column;padding: $u-toast-loading-border-padding;}&__text {font-size: $u-toast-content-text-font-size;line-height: $u-toast-content-text-font-size;color: $u-toast-content-text-color;&--default {color: $u-toast-content-text-color;}&--error {color: $u-error;}&--primary {color: $u-primary;}&--success {color: $u-success;}&--warning {color: $u-warning;}}}
}.u-type-primary {color: $u-toast-u-type-primary-color;background-color: $u-toast-u-type-primary-background-color;border-color: $u-toast-u-type-primary-border-color;border-width: $u-toast-u-type-primary-border-width;
}.u-type-success {color: $u-toast-u-type-success-color;background-color: $u-toast-u-type-success-background-color;border-color: $u-toast-u-type-success-border-color;border-width: 1px;
}.u-type-error {color: $u-toast-u-type-error-color;background-color: $u-toast-u-type-error-background-color;border-color: $u-toast-u-type-error-border-color;border-width: $u-toast-u-type-error-border-width;
}.u-type-warning {color: $u-toast-u-type-warning-color;background-color: $u-toast-u-type-warning-background-color;border-color: $u-toast-u-type-warning-border-color;border-width: 1px;
}.u-type-default {color: $u-toast-u-type-default-color;background-color: $u-toast-u-type-default-background-color;
}
</style>
扩展
然后把这个toast挂到模板里,通过emit消息触发,就实现随时随地无障碍使用改进的toast啦!
/*** 提醒信息框.多端兼容且小程序支持超过7字符中文* @param str 提醒字符* @param type* @returns*/
export function toast(title: string,type: 'default' | 'error' | 'warning' | 'success' | 'primary' = 'default',icon?: string
) {return emitter.emit(Global.CC_MSG_SHOW_TOAST, {title,type,icon}) // 触发全局的toast
}