更新:登录功能
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
@import '../common/abstracts/variable';
|
||||
@import '../common/abstracts/mixin';
|
||||
|
||||
.wot-theme-dark {
|
||||
@include b(drop-menu) {
|
||||
color: $-dark-color;
|
||||
@include e(list) {
|
||||
background-color: $-dark-background2;
|
||||
}
|
||||
|
||||
@include e(item) {
|
||||
@include when(disabled) {
|
||||
color: $-dark-color-gray;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include b(drop-menu) {
|
||||
box-sizing: border-box;
|
||||
color: $-drop-menu-color;
|
||||
font-size: $-drop-menu-fs;
|
||||
position: relative;
|
||||
|
||||
@include e(list) {
|
||||
display: flex;
|
||||
text-align: center;
|
||||
background-color: #fff;
|
||||
}
|
||||
@include e(item) {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
height: $-drop-menu-height;
|
||||
line-height: $-drop-menu-height;
|
||||
text-align: center;
|
||||
|
||||
@include when(active) {
|
||||
font-weight: $-fw-medium;
|
||||
|
||||
.wd-drop-menu__item-title::after {
|
||||
opacity: 1;
|
||||
}
|
||||
:deep(.wd-drop-menu__arrow) {
|
||||
transform: rotate(-180deg);
|
||||
transform-origin: center center;
|
||||
}
|
||||
}
|
||||
@include when(disabled) {
|
||||
color: $-drop-menu-disabled-color;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(item-title) {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
max-width: 100%;
|
||||
padding: 0 $-drop-menu-side-padding;
|
||||
box-sizing: border-box;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 19px;
|
||||
height: $-drop-menu-line-height;
|
||||
bottom: 6px;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
background: $-drop-menu-line-color;
|
||||
border-radius: $-drop-menu-line-height;
|
||||
transition: opacity .15s;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(item-title-text) {
|
||||
position: relative;
|
||||
@include lineEllipsis;
|
||||
}
|
||||
|
||||
@include edeep(arrow) {
|
||||
font-size: $-drop-menu-arrow-fs;
|
||||
margin-left: 2px;
|
||||
}
|
||||
}
|
||||
38
uni_modules/wot-design-uni/components/wd-drop-menu/types.ts
Normal file
38
uni_modules/wot-design-uni/components/wd-drop-menu/types.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { type ExtractPropTypes, type InjectionKey, type Ref } from 'vue'
|
||||
import { baseProps, makeBooleanProp, makeNumberProp, makeStringProp } from '../common/props'
|
||||
|
||||
export type DropDirection = 'up' | 'down'
|
||||
|
||||
export type DropMenuProvide = {
|
||||
props: Partial<DropMenuProps>
|
||||
fold: (child?: any) => void
|
||||
offset: Ref<number>
|
||||
}
|
||||
|
||||
export const DROP_MENU_KEY: InjectionKey<DropMenuProvide> = Symbol('wd-drop-menu')
|
||||
|
||||
export const dropMenuProps = {
|
||||
...baseProps,
|
||||
/**
|
||||
* 弹框层级
|
||||
*/
|
||||
zIndex: makeNumberProp(12),
|
||||
/**
|
||||
* 菜单展开方向,可选值为up 或 down
|
||||
*/
|
||||
direction: makeStringProp<DropDirection>('down'),
|
||||
/**
|
||||
* 是否展示蒙层
|
||||
*/
|
||||
modal: makeBooleanProp(true),
|
||||
/**
|
||||
* 是否点击蒙层时关闭
|
||||
*/
|
||||
closeOnClickModal: makeBooleanProp(true),
|
||||
/**
|
||||
* 菜单展开收起动画时间,单位 ms
|
||||
*/
|
||||
duration: makeNumberProp(200)
|
||||
}
|
||||
|
||||
export type DropMenuProps = ExtractPropTypes<typeof dropMenuProps>
|
||||
@@ -0,0 +1,166 @@
|
||||
<template>
|
||||
<view :style="customStyle" :class="`wd-drop-menu ${customClass}`" @click.stop.prevent="noop" :id="dropMenuId">
|
||||
<wd-overlay
|
||||
:show="overlayVisible"
|
||||
:duration="duration"
|
||||
:z-index="12"
|
||||
:custom-style="modalStyle"
|
||||
@click="handleClickOverlay"
|
||||
@touchmove="noop"
|
||||
v-if="modal"
|
||||
/>
|
||||
|
||||
<!-- #ifdef MP-DINGTALK -->
|
||||
<view :id="dropMenuId">
|
||||
<!-- #endif -->
|
||||
<view class="wd-drop-menu__list">
|
||||
<view
|
||||
v-for="(child, index) in children"
|
||||
:key="index"
|
||||
@click="toggle(child)"
|
||||
:class="`wd-drop-menu__item ${child.disabled ? 'is-disabled' : ''} ${child.$.exposed!.getShowPop() ? 'is-active' : ''}`"
|
||||
>
|
||||
<view class="wd-drop-menu__item-title">
|
||||
<view class="wd-drop-menu__item-title-text">{{ getDisplayTitle(child) }}</view>
|
||||
<wd-icon :name="child.icon" :size="child.iconSize" custom-class="wd-drop-menu__arrow" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<slot />
|
||||
<!-- #ifdef MP-DINGTALK -->
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-drop-menu',
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, getCurrentInstance, inject, onBeforeMount, ref, watch } from 'vue'
|
||||
import { closeOther } from '../common/clickoutside'
|
||||
import { type Queue, queueKey } from '../composables/useQueue'
|
||||
import { getRect, uuid } from '../common/util'
|
||||
import { useChildren } from '../composables/useChildren'
|
||||
import { DROP_MENU_KEY, dropMenuProps } from './types'
|
||||
import wdOverlay from '../wd-overlay/wd-overlay.vue'
|
||||
|
||||
const props = defineProps(dropMenuProps)
|
||||
const queue = inject<Queue | null>(queueKey, null)
|
||||
const dropMenuId = ref<string>(`dropMenuId${uuid()}`)
|
||||
const offset = ref<number>(0)
|
||||
const windowHeight = ref<number>(0)
|
||||
const modalStyle = computed(() => {
|
||||
return props.direction === 'down'
|
||||
? `top: calc(var(--window-top) + ${offset.value}px); bottom: 0;`
|
||||
: `top: 0; bottom: calc(var(--window-bottom) + ${offset.value}px)`
|
||||
})
|
||||
|
||||
const { proxy } = getCurrentInstance() as any
|
||||
|
||||
const { linkChildren, children } = useChildren(DROP_MENU_KEY)
|
||||
|
||||
const showOverlay = computed(() => {
|
||||
return children.some((child) => child.$.exposed!.getShowPop())
|
||||
})
|
||||
|
||||
const overlayVisible = ref(false)
|
||||
let overlayTimer: ReturnType<typeof setTimeout> | null
|
||||
|
||||
// 延迟关闭遮罩层,避免闪烁
|
||||
// 小程序中,即使先 fold 再 closeOther 也会有闪烁,使用延迟关闭遮罩层处理
|
||||
watch(showOverlay, (newVal) => {
|
||||
if (overlayTimer) {
|
||||
clearTimeout(overlayTimer)
|
||||
}
|
||||
if (newVal) {
|
||||
overlayVisible.value = true
|
||||
} else {
|
||||
overlayTimer = setTimeout(() => {
|
||||
overlayVisible.value = false
|
||||
overlayTimer = null
|
||||
}, 16)
|
||||
}
|
||||
})
|
||||
|
||||
linkChildren({ props, fold, offset })
|
||||
|
||||
watch(
|
||||
() => props.direction,
|
||||
(newValue) => {
|
||||
if (!['up', 'down'].includes(newValue)) {
|
||||
// eslint-disable-next-line quotes
|
||||
console.error("[wot ui] warning(wd-drop-menu): direction must be 'up' or 'down'")
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
onBeforeMount(() => {
|
||||
windowHeight.value = uni.getSystemInfoSync().windowHeight
|
||||
})
|
||||
|
||||
function noop() {}
|
||||
|
||||
function getDisplayTitle(child: any) {
|
||||
const { title, modelValue, options, valueKey, labelKey } = child
|
||||
|
||||
if (title) {
|
||||
return title
|
||||
}
|
||||
for (let i = 0, len = options.length; i < len; i++) {
|
||||
if (modelValue === options[i][valueKey]) {
|
||||
return options[i][labelKey]
|
||||
}
|
||||
}
|
||||
console.error('[wot-design] warning(wd-drop-menu-item): no value is matched in the options option.')
|
||||
}
|
||||
|
||||
function toggle(child: any) {
|
||||
// 点击当前 menu, 关闭其他 menu
|
||||
if (child && !child.disabled) {
|
||||
if (queue && queue.closeOther) {
|
||||
queue.closeOther(child)
|
||||
} else {
|
||||
closeOther(child)
|
||||
}
|
||||
fold(child)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 控制菜单内容是否展开
|
||||
*/
|
||||
function fold(child: any) {
|
||||
getRect(`#${dropMenuId.value}`, false, proxy).then((rect) => {
|
||||
if (!rect) return
|
||||
const { top, bottom } = rect
|
||||
if (props.direction === 'down') {
|
||||
offset.value = Number(bottom)
|
||||
} else {
|
||||
offset.value = windowHeight.value - Number(top)
|
||||
}
|
||||
child.$.exposed!.toggle()
|
||||
})
|
||||
}
|
||||
|
||||
function handleClickOverlay() {
|
||||
if (props.closeOnClickModal) {
|
||||
// 关闭所有打开的菜单项
|
||||
children.forEach((child) => {
|
||||
child.$.exposed!.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user