更新:登录功能
This commit is contained in:
75
uni_modules/wot-design-uni/components/wd-toast/index.scss
Normal file
75
uni_modules/wot-design-uni/components/wd-toast/index.scss
Normal file
@@ -0,0 +1,75 @@
|
||||
@import "./../common/abstracts/_mixin.scss";
|
||||
@import "./../common/abstracts/variable.scss";
|
||||
|
||||
@include b(toast) {
|
||||
display: inline-block;
|
||||
max-width: $-toast-max-width;
|
||||
padding: $-toast-padding;
|
||||
background-color: $-toast-bg;
|
||||
border-radius: $-toast-radius;
|
||||
color: $-toast-color;
|
||||
transition: all 0.2s;
|
||||
font-size: $-toast-fs;
|
||||
box-sizing: border-box;
|
||||
box-shadow: $-toast-box-shadow;
|
||||
|
||||
@include when(vertical) {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@include e(msg) {
|
||||
font-size: $-toast-fs;
|
||||
word-break: break-all;
|
||||
line-height: $-toast-line-height;
|
||||
text-align: left;
|
||||
font-family: "San Francisco", Rotobo, arial, "PingFang SC", "Noto SansCJK", "Microsoft Yahei", sans-serif;
|
||||
}
|
||||
@include edeep(icon) {
|
||||
display: inline-block;
|
||||
margin-right: $-toast-icon-margin-right;
|
||||
font-size: $-toast-icon-size;
|
||||
|
||||
@include when(vertical) {
|
||||
margin-right: 0;
|
||||
margin-bottom: $-toast-icon-margin-bottom;
|
||||
}
|
||||
}
|
||||
@include e(iconWrap){
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@include e(iconBox){
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
@include e(iconSvg){
|
||||
width: $-toast-icon-size;
|
||||
height: $-toast-icon-size;
|
||||
background-size: cover;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
@include e(loading) {
|
||||
margin-bottom: $-toast-loading-margin-bottom;
|
||||
display: inline-block;
|
||||
}
|
||||
@include m(top) {
|
||||
transform: translate3d(0, -40vh, 0);
|
||||
}
|
||||
@include m(middle-top){
|
||||
transform: translate3d(0%,-18.8vh,0);
|
||||
}
|
||||
@include m(bottom) {
|
||||
transform: translate3d(0, 40vh, 0);
|
||||
}
|
||||
@include m(with-icon) {
|
||||
min-width: $-toast-with-icon-min-width;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
@include m(loading) {
|
||||
min-width: auto;
|
||||
padding: $-toast-loading-padding;
|
||||
}
|
||||
}
|
||||
92
uni_modules/wot-design-uni/components/wd-toast/index.ts
Normal file
92
uni_modules/wot-design-uni/components/wd-toast/index.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import { inject, provide, ref } from 'vue'
|
||||
import type { Toast, ToastOptions } from './types'
|
||||
import { deepMerge } from '../common/util'
|
||||
|
||||
/**
|
||||
* useToast 用到的key
|
||||
*/
|
||||
const toastDefaultOptionKey = '__TOAST_OPTION__'
|
||||
|
||||
// 默认模板
|
||||
export const defaultOptions: ToastOptions = {
|
||||
duration: 2000,
|
||||
show: false
|
||||
}
|
||||
|
||||
const None = Symbol('None')
|
||||
|
||||
export function useToast(selector: string = ''): Toast {
|
||||
const toastOptionKey = getToastOptionKey(selector)
|
||||
const toastOption = inject(toastOptionKey, ref<ToastOptions | typeof None>(None)) // toast选项
|
||||
if (toastOption.value === None) {
|
||||
toastOption.value = defaultOptions
|
||||
provide(toastOptionKey, toastOption)
|
||||
}
|
||||
let timer: ReturnType<typeof setTimeout> | null = null
|
||||
|
||||
const createMethod = (toastOptions: ToastOptions) => {
|
||||
return (options: ToastOptions | string) => {
|
||||
return show(deepMerge(toastOptions, typeof options === 'string' ? { msg: options } : options) as ToastOptions)
|
||||
}
|
||||
}
|
||||
|
||||
const show = (option: ToastOptions | string) => {
|
||||
const options = deepMerge(defaultOptions, typeof option === 'string' ? { msg: option } : option) as ToastOptions
|
||||
toastOption.value = deepMerge(options, {
|
||||
show: true
|
||||
}) as ToastOptions
|
||||
// 开始渲染,并在 duration ms之后执行清除
|
||||
timer && clearTimeout(timer)
|
||||
if (toastOption.value.duration && toastOption.value.duration > 0) {
|
||||
timer = setTimeout(() => {
|
||||
timer && clearTimeout(timer)
|
||||
close()
|
||||
}, options.duration)
|
||||
}
|
||||
}
|
||||
|
||||
const loading = createMethod({
|
||||
iconName: 'loading',
|
||||
duration: 0,
|
||||
cover: true
|
||||
})
|
||||
const success = createMethod({
|
||||
iconName: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
const error = createMethod({ iconName: 'error' })
|
||||
const warning = createMethod({ iconName: 'warning' })
|
||||
const info = createMethod({ iconName: 'info' })
|
||||
|
||||
const close = () => {
|
||||
toastOption.value = { show: false }
|
||||
}
|
||||
return {
|
||||
show,
|
||||
loading,
|
||||
success,
|
||||
error,
|
||||
warning,
|
||||
info,
|
||||
close
|
||||
}
|
||||
}
|
||||
|
||||
export const getToastOptionKey = (selector: string) => {
|
||||
return selector ? `${toastDefaultOptionKey}${selector}` : toastDefaultOptionKey
|
||||
}
|
||||
|
||||
export const toastIcon = {
|
||||
success() {
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="2 2 44 44" width="48" height="48"><circle cx="24" cy="26" r="22" fill="#000" opacity=".1"/><circle cx="24" cy="24" r="20" fill="#34D19D" opacity=".4"/><circle cx="24" cy="24" r="16" fill="#34D19D"/><path d="M19 24l4 4 8-8" stroke="#FFF" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>'
|
||||
},
|
||||
warning() {
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="2 2 44 44" width="48" height="48"><circle cx="24" cy="26" r="22" fill="#000" opacity=".1"/><circle cx="24" cy="24" r="20" fill="#F0883A" opacity=".4"/><circle cx="24" cy="24" r="16" fill="#F0883A"/><rect x="22.5" y="14" width="3" height="12" fill="#FFF" rx="1.5"/><circle cx="24" cy="30" r="2" fill="#FFF"/></svg>'
|
||||
},
|
||||
info() {
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="2 2 44 44" width="48" height="48"><circle cx="24" cy="26" r="22" fill="#000" opacity=".1"/><circle cx="24" cy="24" r="20" fill="#909CB7" opacity=".4"/><circle cx="24" cy="24" r="16" fill="#909CB7"/><circle cx="24" cy="18" r="2" fill="#FFF"/><rect x="22.5" y="22" width="3" height="12" fill="#FFF" rx="1.5"/></svg>'
|
||||
},
|
||||
error() {
|
||||
return '<svg xmlns="http://www.w3.org/2000/svg" viewBox="2 2 44 44" width="48" height="48"><circle cx="24" cy="26" r="22" fill="#000" opacity=".1"/><circle cx="24" cy="24" r="20" fill="#fa4350" opacity=".4"/><circle cx="24" cy="24" r="16" fill="#fa4350"/><path d="M18 18l12 12M30 18L18 30" stroke="#FFF" stroke-width="2.5" stroke-linecap="round"/></svg>'
|
||||
}
|
||||
}
|
||||
190
uni_modules/wot-design-uni/components/wd-toast/types.ts
Normal file
190
uni_modules/wot-design-uni/components/wd-toast/types.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
* @Author: weisheng
|
||||
* @Date: 2023-06-19 12:47:57
|
||||
* @LastEditTime: 2025-02-16 15:52:17
|
||||
* @LastEditors: weisheng
|
||||
* @Description:
|
||||
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-toast/types.ts
|
||||
* 记得注释
|
||||
*/
|
||||
import type { ExtractPropTypes, PropType } from 'vue'
|
||||
import { baseProps, makeStringProp } from '../common/props'
|
||||
import type { LoadingType } from '../wd-loading/types'
|
||||
|
||||
export type ToastIconType = 'success' | 'error' | 'warning' | 'loading' | 'info' // 图标类型
|
||||
|
||||
export type ToastPositionType = 'top' | 'middle-top' | 'middle' | 'bottom' // 提示信息框的位置类型
|
||||
|
||||
export type ToastDirection = 'vertical' | 'horizontal' // 提示信息框的排列方向
|
||||
|
||||
export type ToastLoadingType = LoadingType // 提示信息加载状态类型
|
||||
|
||||
export type ToastOptions = {
|
||||
msg?: string
|
||||
duration?: number
|
||||
direction?: ToastDirection
|
||||
iconName?: ToastIconType
|
||||
iconSize?: number
|
||||
loadingType?: ToastLoadingType
|
||||
loadingColor?: string
|
||||
loadingSize?: number
|
||||
iconColor?: string
|
||||
position?: ToastPositionType
|
||||
show?: boolean
|
||||
zIndex?: number
|
||||
/**
|
||||
* 是否存在遮罩层
|
||||
*/
|
||||
cover?: boolean
|
||||
/**
|
||||
* 图标类名
|
||||
*/
|
||||
iconClass?: string
|
||||
/**
|
||||
* 类名前缀,用于使用自定义图标
|
||||
*/
|
||||
classPrefix?: string
|
||||
/**
|
||||
* 完全展示后的回调函数
|
||||
*/
|
||||
opened?: () => void
|
||||
/**
|
||||
* 完全关闭时的回调函数
|
||||
*/
|
||||
closed?: () => void
|
||||
}
|
||||
|
||||
export interface Toast {
|
||||
// 打开Toast
|
||||
show(toastOptions: ToastOptions | string): void
|
||||
// 成功提示
|
||||
success(toastOptions: ToastOptions | string): void
|
||||
// 关闭提示
|
||||
error(toastOptions: ToastOptions | string): void
|
||||
// 常规提示
|
||||
info(toastOptions: ToastOptions | string): void
|
||||
// 警告提示
|
||||
warning(toastOptions: ToastOptions | string): void
|
||||
// 加载提示
|
||||
loading(toastOptions: ToastOptions | string): void
|
||||
// 关闭Toast
|
||||
close(): void
|
||||
}
|
||||
|
||||
export const toastProps = {
|
||||
...baseProps,
|
||||
/**
|
||||
* 选择器
|
||||
* @type {string}
|
||||
* @default ''
|
||||
*/
|
||||
selector: makeStringProp(''),
|
||||
/**
|
||||
* 提示信息
|
||||
* @type {string}
|
||||
* @default ''
|
||||
*/
|
||||
msg: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 排列方向
|
||||
* @type {'vertical' | 'horizontal'}
|
||||
* @default 'horizontal'
|
||||
*/
|
||||
direction: makeStringProp<ToastDirection>('horizontal'),
|
||||
/**
|
||||
* 图标名称
|
||||
* @type {'success' | 'error' | 'warning' | 'loading' | 'info'}
|
||||
* @default ''
|
||||
*/
|
||||
iconName: {
|
||||
type: String as PropType<ToastIconType>,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 图标大小
|
||||
* @type {number}
|
||||
*/
|
||||
iconSize: Number,
|
||||
/**
|
||||
* 加载类型
|
||||
* @type {'outline' | 'ring'}
|
||||
* @default 'outline'
|
||||
*/
|
||||
loadingType: makeStringProp<ToastLoadingType>('outline'),
|
||||
/**
|
||||
* 加载颜色
|
||||
* @type {string}
|
||||
* @default '#4D80F0'
|
||||
*/
|
||||
loadingColor: {
|
||||
type: String,
|
||||
default: '#4D80F0'
|
||||
},
|
||||
/**
|
||||
* 加载大小
|
||||
* @type {number}
|
||||
*/
|
||||
loadingSize: Number,
|
||||
/**
|
||||
* 图标颜色
|
||||
* @type {string}
|
||||
*/
|
||||
iconColor: String,
|
||||
/**
|
||||
* 位置
|
||||
* @type {'top' | 'middle-top' | 'middle' | 'bottom'}
|
||||
* @default 'middle-top'
|
||||
*/
|
||||
position: makeStringProp<ToastPositionType>('middle-top'),
|
||||
/**
|
||||
* 层级
|
||||
* @type {number}
|
||||
* @default 100
|
||||
*/
|
||||
zIndex: {
|
||||
type: Number,
|
||||
default: 100
|
||||
},
|
||||
/**
|
||||
* 是否存在遮罩层
|
||||
* @type {boolean}
|
||||
* @default false
|
||||
*/
|
||||
cover: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
/**
|
||||
* 图标类名
|
||||
* @type {string}
|
||||
* @default ''
|
||||
*/
|
||||
iconClass: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
/**
|
||||
* 类名前缀
|
||||
* @type {string}
|
||||
* @default 'wd-icon'
|
||||
*/
|
||||
classPrefix: {
|
||||
type: String,
|
||||
default: 'wd-icon'
|
||||
},
|
||||
/**
|
||||
* 完全展示后的回调函数
|
||||
* @type {Function}
|
||||
*/
|
||||
opened: Function as PropType<() => void>,
|
||||
/**
|
||||
* 完全关闭时的回调函数
|
||||
* @type {Function}
|
||||
*/
|
||||
closed: Function as PropType<() => void>
|
||||
}
|
||||
|
||||
export type ToastProps = ExtractPropTypes<typeof toastProps>
|
||||
192
uni_modules/wot-design-uni/components/wd-toast/wd-toast.vue
Normal file
192
uni_modules/wot-design-uni/components/wd-toast/wd-toast.vue
Normal file
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<wd-overlay v-if="cover" :z-index="zIndex" lock-scroll :show="show" custom-style="background-color: transparent;pointer-events: auto;"></wd-overlay>
|
||||
<wd-transition name="fade" :show="show" :custom-style="transitionStyle" @after-enter="handleAfterEnter" @after-leave="handleAfterLeave">
|
||||
<view :class="rootClass">
|
||||
<!--iconName优先级更高-->
|
||||
<wd-loading
|
||||
v-if="iconName === 'loading'"
|
||||
:type="loadingType"
|
||||
:color="loadingColor"
|
||||
:size="loadingSize"
|
||||
:custom-class="`wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
|
||||
/>
|
||||
<view
|
||||
:class="`wd-toast__iconWrap wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
|
||||
v-else-if="iconName === 'success' || iconName === 'warning' || iconName === 'info' || iconName === 'error'"
|
||||
>
|
||||
<view class="wd-toast__iconBox">
|
||||
<view class="wd-toast__iconSvg" :style="svgStyle"></view>
|
||||
</view>
|
||||
</view>
|
||||
<wd-icon
|
||||
v-else-if="iconClass"
|
||||
:custom-class="`wd-toast__icon ${direction === 'vertical' ? 'is-vertical' : ''}`"
|
||||
:size="iconSize"
|
||||
:class-prefix="classPrefix"
|
||||
:name="iconClass"
|
||||
></wd-icon>
|
||||
<!--文本-->
|
||||
<view v-if="msg" class="wd-toast__msg">{{ msg }}</view>
|
||||
</view>
|
||||
</wd-transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-toast',
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
virtualHost: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||
import wdLoading from '../wd-loading/wd-loading.vue'
|
||||
import wdOverlay from '../wd-overlay/wd-overlay.vue'
|
||||
import wdTransition from '../wd-transition/wd-transition.vue'
|
||||
|
||||
import { computed, inject, onBeforeMount, ref, watch, type CSSProperties } from 'vue'
|
||||
import base64 from '../common/base64'
|
||||
import { defaultOptions, getToastOptionKey, toastIcon } from '.'
|
||||
import { toastProps, type ToastDirection, type ToastLoadingType, type ToastOptions, type ToastProps } from './types'
|
||||
import { addUnit, isDef, isFunction, objToStyle } from '../common/util'
|
||||
|
||||
const props = defineProps(toastProps)
|
||||
const iconName = ref<string>('') // 图标类型
|
||||
const msg = ref<string>('') // 消息内容
|
||||
const position = ref<string>('middle')
|
||||
const show = ref<boolean>(false)
|
||||
const zIndex = ref<number>(100)
|
||||
const loadingType = ref<ToastLoadingType>('outline')
|
||||
const loadingColor = ref<string>('#4D80F0')
|
||||
const iconSize = ref<string>() // 图标大小
|
||||
const loadingSize = ref<string>() // loading大小
|
||||
const svgStr = ref<string>('') // 图标
|
||||
const cover = ref<boolean>(false) // 是否存在遮罩层
|
||||
const classPrefix = ref<string>('wd-icon') // 图标前缀
|
||||
const iconClass = ref<string>('') // 图标类名
|
||||
const direction = ref<ToastDirection>('horizontal') // toast布局方向
|
||||
|
||||
let opened: (() => void) | null = null
|
||||
|
||||
let closed: (() => void) | null = null
|
||||
|
||||
const toastOptionKey = getToastOptionKey(props.selector)
|
||||
const toastOption = inject(toastOptionKey, ref<ToastOptions>(defaultOptions)) // toast选项
|
||||
|
||||
// 监听options变化展示
|
||||
watch(
|
||||
() => toastOption.value,
|
||||
(newVal: ToastOptions) => {
|
||||
reset(newVal)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
// 监听options变化展示
|
||||
watch(
|
||||
() => iconName.value,
|
||||
() => {
|
||||
buildSvg()
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
/**
|
||||
* 动画自定义样式
|
||||
*/
|
||||
const transitionStyle = computed(() => {
|
||||
const style: CSSProperties = {
|
||||
'z-index': zIndex.value,
|
||||
position: 'fixed',
|
||||
top: '50%',
|
||||
left: 0,
|
||||
width: '100%',
|
||||
transform: 'translate(0, -50%)',
|
||||
'text-align': 'center',
|
||||
'pointer-events': 'none'
|
||||
}
|
||||
return objToStyle(style)
|
||||
})
|
||||
|
||||
const rootClass = computed(() => {
|
||||
return `wd-toast ${props.customClass} wd-toast--${position.value} ${
|
||||
(iconName.value !== 'loading' || msg.value) && (iconName.value || iconClass.value) ? 'wd-toast--with-icon' : ''
|
||||
} ${iconName.value === 'loading' && !msg.value ? 'wd-toast--loading' : ''} ${direction.value === 'vertical' ? 'is-vertical' : ''}`
|
||||
})
|
||||
|
||||
const svgStyle = computed(() => {
|
||||
const style: CSSProperties = {
|
||||
backgroundImage: `url(${svgStr.value})`
|
||||
}
|
||||
if (isDef(iconSize.value)) {
|
||||
style.width = iconSize.value
|
||||
style.height = iconSize.value
|
||||
}
|
||||
return objToStyle(style)
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
buildSvg()
|
||||
})
|
||||
|
||||
function handleAfterEnter() {
|
||||
if (isFunction(opened)) {
|
||||
opened()
|
||||
}
|
||||
}
|
||||
|
||||
function handleAfterLeave() {
|
||||
if (isFunction(closed)) {
|
||||
closed()
|
||||
}
|
||||
}
|
||||
|
||||
function buildSvg() {
|
||||
if (iconName.value !== 'success' && iconName.value !== 'warning' && iconName.value !== 'info' && iconName.value !== 'error') return
|
||||
const iconSvg = toastIcon[iconName.value]()
|
||||
const iconSvgStr = `"data:image/svg+xml;base64,${base64(iconSvg)}"`
|
||||
svgStr.value = iconSvgStr
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置toast选项值
|
||||
* @param option toast选项值
|
||||
*/
|
||||
function reset(option: ToastOptions) {
|
||||
show.value = isDef(option.show) ? option.show : false
|
||||
|
||||
if (show.value) {
|
||||
mergeOptionsWithProps(option, props)
|
||||
}
|
||||
}
|
||||
|
||||
function mergeOptionsWithProps(option: ToastOptions, props: ToastProps) {
|
||||
iconName.value = isDef(option.iconName!) ? option.iconName! : props.iconName
|
||||
iconClass.value = isDef(option.iconClass!) ? option.iconClass! : props.iconClass
|
||||
msg.value = isDef(option.msg!) ? option.msg! : props.msg
|
||||
position.value = isDef(option.position!) ? option.position! : props.position
|
||||
zIndex.value = isDef(option.zIndex!) ? option.zIndex! : props.zIndex
|
||||
loadingType.value = isDef(option.loadingType!) ? option.loadingType! : props.loadingType
|
||||
loadingColor.value = isDef(option.loadingColor!) ? option.loadingColor! : props.loadingColor
|
||||
iconSize.value = isDef(option.iconSize) ? addUnit(option.iconSize) : isDef(props.iconSize) ? addUnit(props.iconSize) : undefined
|
||||
loadingSize.value = isDef(option.loadingSize) ? addUnit(option.loadingSize) : isDef(props.loadingSize) ? addUnit(props.loadingSize) : undefined
|
||||
cover.value = isDef(option.cover!) ? option.cover! : props.cover
|
||||
classPrefix.value = isDef(option.classPrefix) ? option.classPrefix : props.classPrefix
|
||||
direction.value = isDef(option.direction) ? option.direction : props.direction
|
||||
closed = isFunction(option.closed) ? option.closed : isFunction(props.closed) ? props.closed : null
|
||||
opened = isFunction(option.opened) ? option.opened : isFunction(props.opened) ? props.opened : null
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user