更新:登录功能

This commit is contained in:
2025-11-04 12:37:04 +08:00
commit a21fb92916
897 changed files with 51500 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
@import '../common/abstracts/variable';
@import '../common/abstracts/mixin';
@include b(notify) {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
padding: $-notify-padding;
font-size: $-notify-font-size;
line-height: $-notify-line-height;
color: $-notify-text-color;
// allow newline character
white-space: pre-wrap;
text-align: center;
word-wrap: break-word;
@include m(primary) {
background: $-notify-primary-background;
}
@include m(success) {
background: $-notify-success-background;
}
@include m(danger) {
background: $-notify-danger-background;
}
@include m(warning) {
background: $-notify-warning-background;
}
}

View File

@@ -0,0 +1,61 @@
import { inject, provide, reactive, ref } from 'vue'
import type { NotifyProps } from './types'
import { deepMerge, isString } from '../common/util'
let timer: ReturnType<typeof setTimeout>
let currentOptions = getDefaultOptions()
const notifyDefaultOptionKey = '__NOTIFY_OPTION__'
const None = Symbol('None')
export const setNotifyDefaultOptions = (options: NotifyProps) => {
currentOptions = deepMerge(currentOptions, options) as NotifyProps
}
export const resetNotifyDefaultOptions = () => {
currentOptions = getDefaultOptions()
}
export const useNotify = (selector: string = '') => {
const notifyOptionKey = getNotifyOptionKey(selector)
const notifyOption = inject(notifyOptionKey, ref<NotifyProps | typeof None>(None))
if (notifyOption.value === None) {
notifyOption.value = currentOptions
provide(notifyOptionKey, notifyOption)
}
const showNotify = (option: NotifyProps | string) => {
const options = deepMerge(currentOptions, isString(option) ? { message: option } : option) as NotifyProps
notifyOption.value = deepMerge(options, { visible: true })
if (notifyOption.value.duration && notifyOption.value.duration > 0) {
timer && clearTimeout(timer)
timer = setTimeout(() => closeNotify(), options.duration)
}
}
const closeNotify = () => {
timer && clearTimeout(timer)
if (notifyOption.value !== None) {
notifyOption.value.visible = false
}
}
return {
showNotify,
closeNotify
}
}
export const getNotifyOptionKey = (selector: string) => {
return selector ? `${notifyDefaultOptionKey}${selector}` : notifyDefaultOptionKey
}
function getDefaultOptions(): NotifyProps {
return {
type: 'danger',
color: undefined,
zIndex: 99,
message: '',
duration: 3000,
position: 'top',
safeHeight: undefined,
background: undefined,
onClick: undefined,
onClosed: undefined,
onOpened: undefined
}
}

View File

@@ -0,0 +1,66 @@
import type { PropType, ExtractPropTypes } from 'vue'
import { makeBooleanProp, makeNumberProp, makeNumericProp, makeStringProp } from '../common/props'
export type NotifyType = 'primary' | 'success' | 'danger' | 'warning'
export type NotifyPosition = 'top' | 'bottom'
export type NotifyProps = Omit<Partial<ExtractPropTypes<typeof notifyProps>>, 'selector'> & {
onClick?: (event: MouseEvent) => void
onClosed?: () => void
onOpened?: () => void
}
export type NotifyThemeVars = {
notifyPadding?: string
notifyFontSize?: string
notifyTextColor?: string
notifyLineHeight?: number | string
notifyDangerBackground?: string
notifyPrimaryBackground?: string
notifySuccessBackground?: string
notifyWarningBackground?: string
}
export const notifyProps = {
/**
* 类型,可选值为 primary success danger warning
*/
type: makeStringProp<NotifyType>('danger'),
/**
* 字体颜色
*/
color: makeStringProp(''),
/**
* 将组件的 z-index 层级设置为一个固定值
*/
zIndex: makeNumberProp(99),
/**
* 显示
*/
visible: makeBooleanProp(false),
/**
* 展示文案,支持通过\n换行
*/
message: makeNumericProp(''),
/**
* 指定唯一标识
*/
selector: makeStringProp(''),
/**
* 展示时长(ms),值为 0 时notify 不会消失
*/
duration: makeNumberProp(3000),
/**
* 弹出位置,可选值为 top bottom
*/
position: makeStringProp<NotifyPosition>('top'),
/**
* 顶部安全高度(
*/
safeHeight: Number,
/**
* 背景颜色
*/
background: makeStringProp(''),
/**
* 是否从页面中脱离出来,用于解决各种 fixed 失效问题 (H5: teleport, APP: renderjs, 小程序: root-portal)
*/
rootPortal: makeBooleanProp(false)
}

View File

@@ -0,0 +1,85 @@
<template>
<wd-popup
v-model="state.visible"
:custom-style="customStyle"
:position="state.position"
:z-index="state.zIndex"
:duration="250"
:modal="false"
:root-portal="state.rootPortal"
@leave="onClosed"
@enter="onOpened"
>
<view class="wd-notify" :class="[`wd-notify--${state.type}`]" :style="{ color: state.color, background: state.background }" @click="onClick">
<slot>{{ state.message }}</slot>
</view>
</wd-popup>
</template>
<script lang="ts">
export default {
name: 'wd-notify',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared'
}
}
</script>
<script lang="ts" setup>
import wdPopup from '../wd-popup/wd-popup.vue'
import { inject, computed, watch, ref } from 'vue'
import { notifyProps, type NotifyProps } from './types'
import { getNotifyOptionKey } from '.'
import { addUnit, isFunction } from '../common/util'
const props = defineProps(notifyProps)
const emits = defineEmits<{
(e: 'update:visible', value: boolean): void
(e: 'click', event: MouseEvent): void
(e: 'closed'): void
(e: 'opened'): void
}>()
const state = inject(getNotifyOptionKey(props.selector), ref<NotifyProps>(props))
const customStyle = computed(() => {
const { safeHeight, position } = state.value
let customStyle: string = ''
switch (position) {
case 'top':
customStyle = `top: calc(var(--window-top) + ${addUnit(safeHeight || 0)})`
break
case 'bottom':
customStyle = 'bottom: var(--window-bottom)'
break
default:
break
}
return customStyle
})
const onClick = (event: MouseEvent) => {
if (isFunction(state.value.onClick)) return state.value.onClick(event)
emits('click', event)
}
const onClosed = () => {
if (isFunction(state.value.onClosed)) return state.value.onClosed()
emits('closed')
}
const onOpened = () => {
if (isFunction(state.value.onOpened)) return state.value.onOpened()
emits('opened')
}
watch(
() => state.value.visible,
(visible) => {
emits('update:visible', visible as boolean)
},
{ deep: true }
)
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>