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,985 @@
<script lang="ts" setup>
import { getCurrentInstance, ref, computed, watch, onMounted, onBeforeUnmount } from "vue"
import { checkIsCssUnit, getUid, getUnit } from "../../core/util/xCoreUtil.uts"
import { getDefaultColor, colorAddDeepen } from "../../core/util/xCoreColorUtil.uts"
import { xConfig,xProvitae } from "../../config/xConfig.uts"
type callbackType = ()=>Promise<boolean>;
type positionType = "top" | "bottom" | "left" | "right"
/**
* @name 抽屉 xDrawer
* @description 提供四个方向的弹出。
* @page /pages/index/drawer
* @category 反馈组件
* @constant 平台兼容
* | Harmony | H5 | andriod | IOS | 小程序 | UTS | UNIAPP-X SDK | version |
| --- | --- | --- | --- | --- | --- | --- | --- |
| ☑ | ☑ | ☑️ | ☑️ | ☑️ | ☑️ | 4.76+ | 1.1.18 |
*/
defineOptions({name:"xDrawer"})
const i18n = xConfig.i18n
const proxy = getCurrentInstance()?.proxy??null;
defineSlots<{
trigger(props: { show: boolean }): any
}>()
const emits = defineEmits([
/**
* 点击遮罩事件
*/
'click',
/**
* 关闭是触发
*/
'close',
/**
* 打开时触发
*/
'open',
/**
* 打开前执行
*/
'beforeOpen',
/**
* 关闭前执行
*/
'beforeClose',
/**
* 等同v-model:show
*/
'update:show',
/**
* 取消时触发
*/
'cancel',
/**
* 确认时触发
*/
'confirm'
])
type xDrawerPropsType = {
/**
* 自定义遮罩样式
*/
customStyle: string,
/**
* 自定义容器背景层样式
*/
customWrapStyle: string,
/**
* 标题
*/
title: string,
/**
* 显示底部操作栏
*/
showFooter: boolean,
/**
* 是否显示标题
*/
showTitle: boolean,
/**
* 是否显示底部关闭按钮
*/
showClose: boolean,
/**
* 遮罩是否允许点击被关闭
*/
overlayClick: boolean,
/**
* 显示可v-model:show双向绑定
*/
show: boolean,
/**
* 显示取消按钮
*/
showCancel: boolean,
/**
* 取消按钮的文本
*/
cancelText: string,
/**
* 确认按钮的文本
*/
confirmText: string,
/**
* 动画时间
*/
duration: number,
/**
* 打开dom的延迟量,如果你打开 弹窗在ios正常。
* 请不要修改此值。如果遇到打不开,或者 打开 后没动画,关闭不了等可能是sdk bug导致
* 此时需要加大值来避免。具体加多少以你弹窗内的节点复杂度有关,需要你自行压力测试。
* 此值仅在ios下生效。
*/
watiDuration: number,
/**
* 打开方向。
*/
position: positionType,
/**
* 打开方向为上和下时的圆角
* 空值时,取全局配置的圆角。
*/
round: string,
/**
* 左右时为内容宽,
* 上下时为内容高
* 百分比,数字字符或者带单位,或者为auto(根据内容自动高度或者宽高)
*/
size: string,
/**
* 弹层最大的高度值,默认为屏幕的可视高
* 提供值时不能为百分比,可以是px,rpx单位数字。如果你不带单位,默认转换为rpx单位。
*/
maxHeight: string,
/**
* 背景颜色
*/
bgColor: string,
/**
* 暗黑背景颜色,如果不提供默认读取全局的sheet配置
*/
darkBgColor: string,
/**
* 遮罩的背景色
*/
overflayBgColor: string,
/**
* 是否禁用内部的scroll标签
* 禁用后内容不会滚动,如果设定了指定高,内容超出指定高,会被裁切
* 但如果没有指定高,内容自动的话,高是自动的。
*/
disabledScroll: boolean,
/**
* 内容区域左右和下的边距。
*/
contentMargin: string,
/**
* 宽屏时是否让内容剧中显示
* 并限制其宽为屏幕宽,只展示中间内容以适应宽屏。
* 注意只有top,bottom才会生效。
*/
widthCoverCenter: boolean,
/**
* 滑动左右或者上下关闭弹出层
* 注意如果设置为0就表示关闭该功能。
* 默认drawer嵌套了scroll-view,再你滚动到顶或者底时,如果继续滑动的距离大于此值关闭层。
* 但如果你是禁用了内部scroll-view,而是采用自己的scorll-view,此时该功能会与你的滚动手势冲突,请自行考虑。
* 建议要打开时设置为80-100比较合理
*/
swiperLenClose: number,
/**
* 距离顶部的偏移量,如果你布局顶会遮罩弹层可以考虑使用此值
*/
offsetTop: string,
/**
* 距离底部的偏移量,如果你布局底会遮罩弹层可以考虑使用此值
*/
offsetBottom: string,
/**
* 弹层的层级
*/
zIndex: number,
/**
* 懒加载
* 为了解决业务布局节点超多时,你可能需要内容延迟加载以免阻塞动画流畅度.
* 如果你启用了lazy,每次打开时,动画执行后才会显示内容.这样动画就流畅,不会因为节点过多造成的卡.
* 开启了此属性后ios端前面的watiDuration属性可以不用再设置了.
*/
lazy: boolean,
/**
* 是否禁用确认按钮
*/
disabledConfirm: boolean,
/**
* 底部按钮操作的主题色,空取全局
*/
btnColor: string,
/**
* 关闭前异步执行的函数,如果返回false阻止关闭,返回true允许关闭
* 必须返回的是Promise异步函数,且类型返回值必须是Promise<boolean>,不然会报错。
*/
beforeClose: callbackType,
/**
* 关闭图标的颜色
*/
closeColor: string,
/**
* 关闭图标的暗黑颜色
*/
closeDarkColor: string
}
const props = withDefaults(defineProps<xDrawerPropsType>(), {
customStyle: "",
customWrapStyle: "",
title: "",
showFooter: false,
showTitle: true,
showClose: false,
overlayClick: true,
show: false,
showCancel: true,
cancelText: "",
confirmText: "",
duration: 300,
watiDuration: 120,
position: "bottom",
round: "",
size: "50%",
maxHeight: "",
bgColor: 'white',
darkBgColor: '',
overflayBgColor: 'rgba(0, 0, 0, 0.4)',
disabledScroll: false,
contentMargin: '16',
widthCoverCenter: false,
swiperLenClose: 0,
offsetTop: '0',
offsetBottom: '0',
zIndex: 1100,
lazy: false,
disabledConfirm: false,
btnColor: "",
beforeClose: () : Promise<boolean> => {
return Promise.resolve(true)
},
closeColor: "#e6e6e6",
closeDarkColor: "#545454"
})
// 响应式数据
const _width = ref(0)
const _height = ref(0)
const showOverflay = ref(false)
const actioning = ref(false)
const status = ref("")
const id = ref("xDrawer" + getUid())
const wrapId = ref("xDrawerWrap" + getUid())
const first = ref(true)
const tid = ref(0)
const windtop = ref(0)
const windowBottom = ref(0)
const start_move_x = ref(0)
const start_move_y = ref(0)
const move_x = ref(0)
const move_y = ref(0)
const move_end_x = ref(0)
const move_end_y = ref(0)
const scrollTop = ref(-1)
const isTopOrBottomByScroll = ref(false)
const xDrawerContentHeight = ref(0)
const safeFooterHeight = ref(0)
const lezyShowModal = ref(true)
const isOpenedDefault = ref(false)
const isLoading = ref(false)
const anitid = ref(23)
// #ifdef H5
const teleportElH5 = ref("uni-app")
const teleportTarget = ref<string | null>(null)
const getTeleportTarget = () => {
try {
if(status.value == ''||status.value=='close') return 'uni-app'
// 优先尝试 uni-page
if (document.querySelector('uni-page')) {
return 'uni-page'
}
// 优先尝试 uni-app
if (document.querySelector('uni-app')) {
return 'uni-app'
}
// 备用方案:尝试 app
if (document.querySelector('#app')) {
return '#app'
}
// 最后备用:body
return 'body'
} catch (error) {
// console.warn('Failed to get teleport target:', error)
return 'body'
}
}
// 检查teleport目标是否可用
const isTeleportTargetValid = (target: string) => {
try {
return !!document.querySelector(target)
} catch {
return false
}
}
// #endif
// 计算属性
const _disabledConfirm = computed((): boolean => props.disabledConfirm)
const _lazy = computed((): boolean => props.lazy)
const _customStyle = computed((): string => props.customStyle)
const _customWrapStyle = computed((): string => props.customWrapStyle)
const _show = computed((): boolean => props.show)
const _widthCoverCenter = computed((): boolean => props.widthCoverCenter)
const _showClose = computed((): boolean => props.showClose)
const _duration = computed((): number => props.duration)
const _position = computed((): string => props.position)
const _showTitle = computed((): boolean => props.showTitle)
const _round = computed((): string => {
let round = props.round;
if (round == "") {
round = xConfig.drawerRadius
}
let radius = checkIsCssUnit(round, xConfig.unit);
let _r = "none"
if (props.position == 'top') {
_r = `0px 0px ${radius} ${radius}`
}
if (props.position == 'bottom') {
_r = `${radius} ${radius} 0px 0px`
}
return _r
})
const _offset = computed((): number => {
let offset = checkIsCssUnit(props.offsetTop, xConfig.unit);
let x = parseFloat(offset)
let unit = getUnit(offset)
if (unit == 'rpx') {
x = uni.rpx2px(x)
}
return x;
})
const _offsetBottom = computed((): number => {
let offset = checkIsCssUnit(props.offsetBottom, xConfig.unit);
let x = parseFloat(offset)
let unit = getUnit(offset)
if (unit == 'rpx') {
x = uni.rpx2px(x)
}
return x;
})
const _size = computed((): string => checkIsCssUnit(props.size, xConfig.unit))
const _contentMargin = computed((): string => checkIsCssUnit(props.contentMargin, xConfig.unit))
const _showFooter = computed((): boolean => props.showFooter)
const _maxHeight = computed((): string => {
if (props.maxHeight == "") return ""
if (props.position == 'left' || props.position == 'right') return ""
return checkIsCssUnit(props.maxHeight, xConfig.unit);
})
const _showCancel = computed((): boolean => props.showCancel)
const _title = computed((): string => {
if(props.title==''){
return i18n.t("tmui4x.modal.title")
}
return props.title
})
const _cancelText = computed((): string => {
if(props.cancelText==''){
return i18n.t("tmui4x.cancel")
}
return props.cancelText
})
const _confirmText = computed((): string => {
if(props.confirmText==''){
return i18n.t("tmui4x.confirm")
}
return props.confirmText
})
const _animationFun = computed((): string => xConfig.animationFun)
const _bgColor = computed((): string => {
let bgcolor = props.bgColor;
if (xConfig.dark == 'dark') {
if (props.darkBgColor != '') {
}
bgcolor = props.darkBgColor != '' ? props.darkBgColor : xConfig.sheetDarkColor
}
return getDefaultColor(bgcolor)
})
const _btnColor = computed((): string => {
if(props.btnColor == '') return getDefaultColor(xConfig.color)
return getDefaultColor(props.btnColor)
})
const __height = computed((): string => {
let h = '100%';
// #ifdef WEB
h = `calc(100% - ${windtop.value}px - ${_offsetBottom.value}px)`
// #endif
// #ifdef APP || MP-WEIXIN
if (_offset.value > 0 || _offsetBottom.value > 0) {
h = (_height.value - _offsetBottom.value) + 'px'
}
// #endif
return h;
})
const _titleFontSize = computed((): string => (xConfig.fontScale * 16).toString() + 'px')
const _isDark = computed((): boolean => xConfig.dark == 'dark')
const _closeIcon = computed((): string => xConfig.closeIcon)
function onEnd() {
actioning.value = false;
if (status.value == 'close') {
showOverflay.value = false;
/**
* 关闭时执行
*/
emits('close')
/**
* 等同v-model:show
*/
emits('update:show', false)
if (_lazy.value) {
lezyShowModal.value = false
}
// #ifdef WEB
teleportTarget.value = getTeleportTarget()
// #endif
} else {
/**
* 打开执行的事件
*/
emits('open')
if (_lazy.value) {
lezyShowModal.value = true
}
}
}
function setStyleAni() {
try {
let sys = uni.getWindowInfo()
_height.value = sys.windowHeight - _offset.value
let watiDuration = 60;
// #ifdef APP-IOS
watiDuration = props.watiDuration
// #endif
if (status.value == 'open') {
showOverflay.value = true;
clearTimeout(tid.value)
tid.value = setTimeout(function () {
let element = proxy!.$refs['xDrawerWrap'] as UniElement | null
let elementWrap = proxy!.$refs['xDrawerWrapContent'] as UniElement | null
if (element== null || elementWrap == null) return;
element!.style.setProperty("transition-duration", _duration.value.toString() + 'ms')
elementWrap!.style.setProperty("transition-duration", _duration.value.toString() + 'ms')
element!.style.setProperty('opacity', 1)
elementWrap!.style.setProperty('transform', `translate(0%,0%)`)
}, watiDuration);
} else if (status.value == 'close') {
let element = proxy!.$refs['xDrawerWrap'] as UniElement | null
let elementWrap = proxy!.$refs['xDrawerWrapContent'] as UniElement | null
if (element== null || elementWrap == null) return;
element.style.setProperty("transition-duration", _duration.value.toString() + 'ms')
elementWrap.style.setProperty("transition-duration", _duration.value.toString() + 'ms')
element.style.setProperty('opacity', 0)
if (_position.value == 'bottom') {
elementWrap.style.setProperty('transform', `translate(0%,100%)`)
} else if (_position.value == 'top') {
elementWrap.style.setProperty('transform', `translate(0%,-100%)`)
} else if (_position.value == 'left') {
elementWrap.style.setProperty('transform', `translate(-100%,0%)`)
} else if (_position.value == 'right') {
elementWrap.style.setProperty('transform', `translate(100%,0%)`)
}
}
} catch (e) {
//TODO handle the exception
}
}
function overflayMoveTouch(evt : TouchEvent) {
evt.preventDefault();
}
function closeAlert() {
if (actioning.value) return;
if (status.value == 'close') return;
actioning.value = true;
status.value = 'close'
/**
* 关闭前执行
*/
emits('beforeClose')
setStyleAni();
anitid.value = setTimeout(function() {
onEnd()
}, _duration.value);
}
function showAlert() {
if (actioning.value) return;
if (status.value == 'open') return;
showOverflay.value = true;
actioning.value = true;
status.value = 'open'
// #ifdef WEB
teleportTarget.value = getTeleportTarget()
// #endif
/**
* 打开前执行
*/
emits('beforeOpen')
setStyleAni();
anitid.value = setTimeout(function() {
onEnd()
}, _duration.value+60);
}
function onClickOverflowy(evt : Event) {
evt.stopPropagation()
/**
* 点击遮罩事件
*/
emits("click")
if (!props.overlayClick||isLoading.value) return;
closeAlert();
}
// 方法
function cancelEvt() {
// ios渲染有时会造成无法触发onEnd事件,导致无法关闭。这是ios渲染的bug造成,无力修复,已向官方反馈
// 但这牵涉到底层问题。一时无法修复,故在ios特殊处理。后期修复,需要删除此值。
// #ifdef APP-IOS
actioning.value = false;
// #endif
/**
* 取消时触发
*/
emits('cancel')
closeAlert()
}
async function confirmEvt(): Promise<any> {
isLoading.value = true;
let isCanClose = await props.beforeClose()
isLoading.value = false;
if(!isCanClose){
return Promise.resolve(true)
}
// ios渲染有时会造成无法触发onEnd事件,导致无法关闭。这是ios渲染的bug造成,无力修复,已向官方反馈
// 但这牵涉到底层问题。一时无法修复,故在ios特殊处理。后期修复,需要删除此值。
// #ifdef APP-IOS
actioning.value = false;
// #endif
/**
* 确认时触发
*/
emits('confirm')
closeAlert()
return Promise.resolve(false)
}
function openDrawer() {
showAlert();
}
function swiperClose() {
let offsetX = move_end_x.value - start_move_x.value
let offsetY = move_end_y.value - start_move_y.value
if (props.swiperLenClose == 0 || (actioning.value && status.value == 'close')) return;
if (props.position == 'left' && offsetX < props.swiperLenClose * -1 && Math.abs(offsetX) >= Math.abs(offsetY)) {
closeAlert()
}
if (props.position == 'right' && offsetX > props.swiperLenClose && Math.abs(offsetX) >= Math.abs(offsetY)) {
closeAlert()
}
if (props.position == 'top' && offsetY < props.swiperLenClose * -1 && Math.abs(offsetY) >= Math.abs(offsetX)) {
closeAlert()
}
if (props.position == 'bottom' && offsetY > props.swiperLenClose && Math.abs(offsetY) >= Math.abs(offsetX)) {
closeAlert()
}
}
function maskerMove(evt : UniTouchEvent) {
// #ifdef WEB
evt.preventDefault()
// #endif
}
function mStart(evt : UniTouchEvent) {
if (props.swiperLenClose == 0) return;
start_move_x.value = evt.changedTouches[0].clientX
start_move_y.value = evt.changedTouches[0].clientY
}
function mMove(evt : UniTouchEvent) {
if (props.swiperLenClose == 0) return;
// #ifdef WEB
evt.preventDefault()
// #endif
if (evt.changedTouches.length == 0) return;
move_x.value = evt.changedTouches[0].clientX
move_y.value = evt.changedTouches[0].clientY
}
function mEnd(evt : UniTouchEvent) {
if (props.swiperLenClose == 0) return;
if (evt.changedTouches.length == 0) return;
let x = evt.changedTouches[0].clientX
let y = evt.changedTouches[0].clientY
move_end_x.value = x
move_end_y.value = y;
swiperClose();
}
function onScroll(evt : UniScrollEvent) {
if (props.position == 'bottom') {
if (evt.detail.scrollTop > 0) {
start_move_x.value = move_x.value
start_move_y.value = move_y.value
}
}
if (props.position == 'top') {
let ele = proxy!.$refs['xDrawerContent'] as UniElement | null;
if (ele == null) return;
let height = ele.getBoundingClientRect().height;
let maxheight = evt.detail.scrollHeight - evt.detail.scrollTop
if (evt.detail.scrollTop < maxheight - 1) {
start_move_x.value = move_x.value
start_move_y.value = move_y.value
}
}
}
function onScrollTop(evt : UniScrollToUpperEvent) {
start_move_x.value = move_x.value
start_move_y.value = move_y.value
}
function onScrollBottom(evt : UniScrollToLowerEvent) {
start_move_x.value = move_x.value
start_move_y.value = move_y.value
}
// 监听器
watch(():boolean => props.show, (newval : boolean) => {
if (newval) {
showAlert()
} else {
closeAlert()
}
})
// 生命周期
onMounted(() => {
lezyShowModal.value = _lazy.value ? false : true;
// #ifdef H5
nextTick(() => {
teleportTarget.value = getTeleportTarget()
})
// #endif
function oninitready(){
isOpenedDefault.value = true;
let sys = uni.getWindowInfo()
// #ifdef WEB
_width.value = sys.windowWidth
_height.value = sys.windowHeight;
windtop.value = sys.windowTop + _offset.value;
// #endif
// #ifdef APP|| MP-WEIXIN
_width.value = sys.windowWidth
_height.value = sys.windowHeight + 44;
windtop.value = _offset.value;
// #endif
safeFooterHeight.value = sys.safeAreaInsets.bottom == 0 ? 16 : sys.safeAreaInsets.bottom
if (_show.value) {
showAlert();
}
}
oninitready()
})
// 监听页面变化,重新获取teleport目标
onUpdated(() => {
// #ifdef H5
if (teleportTarget.value && !isTeleportTargetValid(teleportTarget.value)) {
teleportTarget.value = getTeleportTarget()
}
// #endif
})
onBeforeUnmount(() => {
clearTimeout(tid.value)
})
defineExpose({
/** 打开 **/
open:()=>showAlert(),
/** 关闭 **/
close:()=>closeAlert()
})
</script>
<template>
<view>
<view @click="openDrawer">
<!--
@slot 标签触发显示遮罩,免于使用变量控制
@prop {Boolean} show - 当前是否已显示
-->
<slot name="trigger" :show="show"></slot>
</view>
<!-- #ifdef H5 -->
<teleport :to="teleportTarget || teleportElH5" :disabled="!teleportTarget">
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<root-portal >
<!-- #endif -->
<view @click="onClickOverflowy" @touchmove="maskerMove" v-if="showOverflay" :id="id" ref="xDrawerWrap"
class="xDrawerWrap" :class="[
(_position=='top'||_position=='bottom')&&_widthCoverCenter?'xDrawerWrapContentMinwidthWrapDir':'',
'xDrawerWrap_'+_position
]" :style="[{backgroundColor:overflayBgColor,width:'100%',
top:windtop+'px',
height:__height,
zIndex:zIndex,
'transition-timing-function':_animationFun},_customStyle]">
<view v-if="showOverflay&&_position == 'bottom'&&!actioning">
<!--
@slot 内容顶部的额外插槽,仅为下向上弹出(position=bottom)下才会显示。
-->
<slot name="contentTop"></slot>
</view>
<!-- @transitionend="onEnd" -->
<view @click.stop="" ref="xDrawerWrapContent" class="xDrawerWrapContent"
@touchstart="mStart" @touchmove="mMove" @touchend="mEnd" :class="[
(_position=='top'||_position=='bottom')&&_widthCoverCenter?'xDrawerWrapContentMinwidth':'',
'xDrawerWrapContent_'+_position
]" :id="wrapId"
:style="[
{
width:_position=='left'||_position=='right'?_size:'100%',
height:_position=='left'||_position=='right'?'100%':_size,
borderRadius:_round,
maxHeight:_maxHeight!=''?_maxHeight:'100%',
'transition-timing-function':_animationFun,
backgroundColor:_bgColor,
},
customWrapStyle
]">
<view v-if="_showClose" class="xDrawerXclose">
<x-icon @click="cancelEvt" :color="closeColor" :dark-color="closeDarkColor" font-size="24px"
:name="_closeIcon"></x-icon>
</view>
<view v-if="_showTitle">
<!--
@slot 标题插槽
@prop {Boolean} show - 当前是否已显示
-->
<slot name="title" :show="show">
<view class="xDrawerTitleBox">
<text :style="{fontSize:_titleFontSize,color:_isDark?'white':'black',opacity:'0.64'}"
class="xDrawertitleBox">{{_title}}</text>
</view>
</slot>
</view>
<x-loading v-if="!lezyShowModal"><text></text></x-loading>
<view ref="xDrawerContent" class="xDrawerContent"
:style="{flex:'1',margin:`0px 0px ${_contentMargin} 0px`}">
<scroll-view v-if="!disabledScroll&&lezyShowModal" @scroll="onScroll"
@scrolltoupper="onScrollTop" @scrolltolower="onScrollBottom"
<!-- #ifdef APP||WEB -->
:style="{flex:'1'}"
<!-- #endif -->
<!-- #ifdef MP -->
:style="{position:'absolute',width:'100%',height:'100%'}"
<!-- #endif -->
:scroll-y="true" :rebound="false">
<view :style="{padding:`0px ${_contentMargin} 0px ${_contentMargin}`}">
<!--
@slot 默认插槽
-->
<slot name="default"></slot>
</view>
</scroll-view>
<view v-if="disabledScroll&&lezyShowModal"
:style="{flex:'1',padding:`0px ${_contentMargin} 0px ${_contentMargin}`}">
<!--
默认插槽
-->
<slot name="default"></slot>
</view>
</view>
<view v-if="showFooter&&lezyShowModal" class="xDrawerFooter" :style="{backgroundColor:_bgColor}">
<!--
@slot 底部操作栏
-->
<slot name="footer">
<view style="flex-direction: row;align-items: center;justify-content: center;display: flex;">
<x-button :disabled="isLoading" :color="_btnColor" @click="cancelEvt" v-if="_showCancel" skin="thin" width="0px" :block="true"
style="margin-right: 16rpx;flex:1">{{_cancelText}}</x-button>
<x-button :loading="isLoading" :color="_btnColor" @click="confirmEvt" width="0px" :disabled="_disabledConfirm" :block="true"
style="flex:1">{{_confirmText}}</x-button>
</view>
</slot>
<view :style="{height:safeFooterHeight+'px'}"></view>
</view>
</view>
</view>
<!-- #ifdef MP-WEIXIN -->
</root-portal>
<!-- #endif -->
<!-- #ifdef H5 -->
</teleport>
<!-- #endif -->
</view>
</template>
<style>
.xDrawerContent{
position: relative;
}
.xDrawerFooter {
width: 100%;
/* background-color: white; */
padding: 0 16px 0px 16px;
/* #ifdef MP-WEIXIN||WEB */
box-sizing: border-box;
/* #endif */
}
.xDrawerXclose {
position: absolute;
right: 16px;
top: 9px;
z-index: 100;
}
.xDrawerXcloseOutter {
padding-right: 16px;
}
.xDrawerTitleBox {
height: 50px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.xDrawertitleBox {
max-width: 175px;
overflow: hidden;
lines: 1;
text-overflow: ellipsis;
}
.xDrawerWrap_bottom {
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.xDrawerWrap_top {
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.xDrawerWrap_left {
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.xDrawerWrap_right {
display: flex;
flex-direction: row;
justify-content: flex-end;
}
.xDrawerWrapContent {
transition-duration: 350ms;
transition-property: transform;
/* background-color: white; */
display: flex;
flex-direction: column;
position: relative;
}
.xDrawerWrapContentMinwidth {
max-width: 500px;
}
.xDrawerWrapContentMinwidthWrapDir {
align-items: center;
}
.xDrawerWrapContent_bottom {
transform: translate(0%, 100%);
}
.xDrawerWrapContent_top {
transform: translate(0%, -100%);
}
.xDrawerWrapContent_left {
transform: translate(-100%, 0%);
}
.xDrawerWrapContent_right {
transform: translate(100%, 0%);
}
.xDrawerWrap {
/* background: rgba(0, 0, 0, 0.4); */
opacity: 0;
position: fixed;
left: 0;
top: 0px;
/* #ifdef APP-HARMONY */
transition-duration: 0ms;
/* #endif */
/* #ifndef APP-HARMONY */
transition-duration: 350ms;
/* #endif */
transition-property: opacity;
}
</style>