更新:登录功能
This commit is contained in:
204
uni_modules/wot-design-uni/components/wd-action-sheet/index.scss
Normal file
204
uni_modules/wot-design-uni/components/wd-action-sheet/index.scss
Normal file
@@ -0,0 +1,204 @@
|
||||
@import '../common/abstracts/variable';
|
||||
@import '../common/abstracts/mixin';
|
||||
|
||||
.wot-theme-dark {
|
||||
@include b(action-sheet) {
|
||||
background-color: $-dark-background2;
|
||||
color: $-dark-color;
|
||||
|
||||
@include e(action) {
|
||||
color: $-dark-color;
|
||||
background: $-dark-background2;
|
||||
|
||||
&:not(.wd-action-sheet__action--disabled):not(.wd-action-sheet__action--loading):active {
|
||||
background: $-dark-background4;
|
||||
}
|
||||
|
||||
@include m(disabled) {
|
||||
color: $-dark-color-gray;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(subname) {
|
||||
color: $-dark-color3;
|
||||
}
|
||||
|
||||
@include e(cancel) {
|
||||
color: $-dark-color;
|
||||
background: $-dark-background4;
|
||||
|
||||
&:active {
|
||||
background: $-dark-background5;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.wd-action-sheet__close) {
|
||||
color: $-dark-color3;
|
||||
}
|
||||
|
||||
@include e(panel-title) {
|
||||
color: $-dark-color;
|
||||
}
|
||||
|
||||
@include e(header) {
|
||||
color: $-dark-color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.wd-action-sheet__popup) {
|
||||
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
|
||||
}
|
||||
|
||||
@include b(action-sheet) {
|
||||
background-color: $-color-white;
|
||||
padding-bottom: 1px;
|
||||
|
||||
@include edeep(popup) {
|
||||
border-radius: $-action-sheet-radius $-action-sheet-radius 0 0;
|
||||
}
|
||||
|
||||
@include e(actions) {
|
||||
padding: 8px 0;
|
||||
max-height: 50vh;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
@include e(action) {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: $-action-sheet-action-height;
|
||||
line-height: $-action-sheet-action-height;
|
||||
color: $-action-sheet-color;
|
||||
font-size: $-action-sheet-fs;
|
||||
text-align: center;
|
||||
border: none;
|
||||
background: $-action-sheet-bg;
|
||||
outline: none;
|
||||
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&:not(&--disabled):not(&--loading):active {
|
||||
background: $-action-sheet-active-color;
|
||||
}
|
||||
|
||||
@include m(disabled) {
|
||||
color: $-action-sheet-disabled-color;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
@include m(loading) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
line-height: initial;
|
||||
}
|
||||
}
|
||||
|
||||
@include edeep(action-loading){
|
||||
width: $-action-sheet-loading-size;
|
||||
height: $-action-sheet-loading-size;
|
||||
}
|
||||
|
||||
@include e(name) {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
@include e(subname) {
|
||||
display: inline-block;
|
||||
margin-left: 4px;
|
||||
font-size: $-action-sheet-subname-fs;
|
||||
color: $-action-sheet-subname-color;
|
||||
}
|
||||
|
||||
@include e(cancel) {
|
||||
display: block;
|
||||
width: calc(100% - 48px);
|
||||
line-height: $-action-sheet-cancel-height;
|
||||
padding: 0;
|
||||
color: $-action-sheet-cancel-color;
|
||||
font-size: $-action-sheet-fs;
|
||||
text-align: center;
|
||||
border-radius: $-action-sheet-cancel-radius;
|
||||
border: none;
|
||||
background: $-action-sheet-cancel-bg;
|
||||
outline: none;
|
||||
margin: 0 auto 24px;
|
||||
font-weight: $-action-sheet-weight;
|
||||
|
||||
&:active {
|
||||
background: $-action-sheet-active-color;
|
||||
}
|
||||
|
||||
&:after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(header) {
|
||||
color: $-action-sheet-color;
|
||||
position: relative;
|
||||
height: $-action-sheet-title-height;
|
||||
line-height: $-action-sheet-title-height;
|
||||
text-align: center;
|
||||
font-size: $-action-sheet-title-fs;
|
||||
font-weight: $-action-sheet-weight;
|
||||
}
|
||||
|
||||
@include edeep(close) {
|
||||
position: absolute;
|
||||
top: $-action-sheet-close-top;
|
||||
right: $-action-sheet-close-right;
|
||||
color: $-action-sheet-close-color;
|
||||
font-size: $-action-sheet-close-fs;
|
||||
transform: rotate(-45deg);
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
@include e(panels) {
|
||||
height: 84px;
|
||||
overflow-y: hidden;
|
||||
|
||||
&:first-of-type {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
@include e(panels-content) {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
@include e(panel) {
|
||||
width: 88px;
|
||||
flex: 0 0 auto;
|
||||
display: inline-block;
|
||||
padding: $-action-sheet-panel-padding;
|
||||
}
|
||||
|
||||
@include e(panel-img) {
|
||||
display: block;
|
||||
width: $-action-sheet-panel-img-fs;
|
||||
height: $-action-sheet-panel-img-fs;
|
||||
margin: 0 auto;
|
||||
margin-bottom: 7px;
|
||||
border-radius: $-action-sheet-panel-img-radius;
|
||||
}
|
||||
|
||||
@include e(panel-title) {
|
||||
font-size: $-action-sheet-subname-fs;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
color: $-action-sheet-color;
|
||||
@include lineEllipsis;
|
||||
}
|
||||
}
|
||||
118
uni_modules/wot-design-uni/components/wd-action-sheet/types.ts
Normal file
118
uni_modules/wot-design-uni/components/wd-action-sheet/types.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import type { ExtractPropTypes } from 'vue'
|
||||
import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeRequiredProp, makeStringProp } from '../common/props'
|
||||
|
||||
export type Action = {
|
||||
/**
|
||||
* 选项名称
|
||||
*/
|
||||
name: string
|
||||
/**
|
||||
* 描述信息
|
||||
*/
|
||||
subname?: string
|
||||
/**
|
||||
* 颜色
|
||||
*/
|
||||
color?: string
|
||||
/**
|
||||
* 禁用
|
||||
*/
|
||||
disabled?: boolean
|
||||
/**
|
||||
* 加载中状态
|
||||
*/
|
||||
loading?: boolean
|
||||
}
|
||||
|
||||
export type Panel = {
|
||||
/**
|
||||
* 图片地址
|
||||
*/
|
||||
iconUrl: string
|
||||
/**
|
||||
* 标题内容
|
||||
*/
|
||||
title: string
|
||||
}
|
||||
|
||||
export const actionSheetProps = {
|
||||
...baseProps,
|
||||
/**
|
||||
* header 头部样式
|
||||
* @default ''
|
||||
* @type {string}
|
||||
*/
|
||||
customHeaderClass: makeStringProp(''),
|
||||
/**
|
||||
* 设置菜单显示隐藏
|
||||
* @default false
|
||||
* @type {boolean}
|
||||
*/
|
||||
modelValue: { ...makeBooleanProp(false), ...makeRequiredProp(Boolean) },
|
||||
/**
|
||||
* 菜单选项
|
||||
* @default []
|
||||
* @type {Action[]}
|
||||
*/
|
||||
actions: makeArrayProp<Action>(),
|
||||
/**
|
||||
* 自定义面板项,可以为字符串数组,也可以为对象数组,如果为二维数组,则为多行展示
|
||||
* @default []
|
||||
* @type {Array<Panel | Panel[]>}
|
||||
*/
|
||||
panels: makeArrayProp<Panel | Panel[]>(),
|
||||
/**
|
||||
* 标题
|
||||
* @type {string}
|
||||
*/
|
||||
title: String,
|
||||
/**
|
||||
* 取消按钮文案
|
||||
* @type {string}
|
||||
*/
|
||||
cancelText: String,
|
||||
/**
|
||||
* 点击选项后是否关闭菜单
|
||||
* @default true
|
||||
* @type {boolean}
|
||||
*/
|
||||
closeOnClickAction: makeBooleanProp(true),
|
||||
/**
|
||||
* 点击遮罩是否关闭
|
||||
* @default true
|
||||
* @type {boolean}
|
||||
*/
|
||||
closeOnClickModal: makeBooleanProp(true),
|
||||
/**
|
||||
* 弹框动画持续时间
|
||||
* @default 200
|
||||
* @type {number}
|
||||
*/
|
||||
duration: makeNumberProp(200),
|
||||
/**
|
||||
* 菜单层级
|
||||
* @default 10
|
||||
* @type {number}
|
||||
*/
|
||||
zIndex: makeNumberProp(10),
|
||||
/**
|
||||
* 弹层内容懒渲染,触发展示时才渲染内容
|
||||
* @default true
|
||||
* @type {boolean}
|
||||
*/
|
||||
lazyRender: makeBooleanProp(true),
|
||||
/**
|
||||
* 弹出面板是否设置底部安全距离(iphone X 类型的机型)
|
||||
* @default true
|
||||
* @type {boolean}
|
||||
*/
|
||||
safeAreaInsetBottom: makeBooleanProp(true),
|
||||
/**
|
||||
* 是否从页面中脱离出来,用于解决各种 fixed 失效问题 (H5: teleport, APP: renderjs, 小程序: root-portal)
|
||||
* 类型:boolean
|
||||
* 默认值:false
|
||||
*/
|
||||
rootPortal: makeBooleanProp(false)
|
||||
}
|
||||
|
||||
export type ActionSheetProps = ExtractPropTypes<typeof actionSheetProps>
|
||||
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<view>
|
||||
<wd-popup
|
||||
custom-class="wd-action-sheet__popup"
|
||||
:custom-style="`${(actions && actions.length) || (panels && panels.length) ? 'background: transparent;' : ''}`"
|
||||
v-model="showPopup"
|
||||
:duration="duration"
|
||||
position="bottom"
|
||||
:close-on-click-modal="closeOnClickModal"
|
||||
:safe-area-inset-bottom="safeAreaInsetBottom"
|
||||
:lazy-render="lazyRender"
|
||||
:root-portal="rootPortal"
|
||||
@enter="handleOpen"
|
||||
@close="close"
|
||||
@after-enter="handleOpened"
|
||||
@after-leave="handleClosed"
|
||||
@click-modal="handleClickModal"
|
||||
:z-index="zIndex"
|
||||
>
|
||||
<view
|
||||
:class="`wd-action-sheet ${customClass}`"
|
||||
:style="`${
|
||||
(actions && actions.length) || (panels && panels.length)
|
||||
? 'margin: 0 10px calc(var(--window-bottom) + 10px) 10px; border-radius: 16px;'
|
||||
: 'margin-bottom: var(--window-bottom);'
|
||||
} ${customStyle}`"
|
||||
>
|
||||
<view v-if="title" :class="`wd-action-sheet__header ${customHeaderClass}`">
|
||||
{{ title }}
|
||||
<wd-icon custom-class="wd-action-sheet__close" name="add" @click="close" />
|
||||
</view>
|
||||
<view class="wd-action-sheet__actions" v-if="actions && actions.length">
|
||||
<button
|
||||
v-for="(action, rowIndex) in actions"
|
||||
:key="rowIndex"
|
||||
:class="`wd-action-sheet__action ${action.disabled ? 'wd-action-sheet__action--disabled' : ''} ${
|
||||
action.loading ? 'wd-action-sheet__action--loading' : ''
|
||||
}`"
|
||||
:style="`color: ${action.color}`"
|
||||
@click="select(rowIndex, 'action')"
|
||||
>
|
||||
<wd-loading custom-class="`wd-action-sheet__action-loading" v-if="action.loading" />
|
||||
<view v-else class="wd-action-sheet__name">{{ action.name }}</view>
|
||||
<view v-if="!action.loading && action.subname" class="wd-action-sheet__subname">{{ action.subname }}</view>
|
||||
</button>
|
||||
</view>
|
||||
<view v-if="formatPanels && formatPanels.length">
|
||||
<view v-for="(panel, rowIndex) in formatPanels" :key="rowIndex" class="wd-action-sheet__panels">
|
||||
<view class="wd-action-sheet__panels-content">
|
||||
<view v-for="(col, colIndex) in panel" :key="colIndex" class="wd-action-sheet__panel" @click="select(rowIndex, 'panels', colIndex)">
|
||||
<image class="wd-action-sheet__panel-img" :src="(col as any).iconUrl" />
|
||||
<view class="wd-action-sheet__panel-title">{{ (col as any).title }}</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<slot />
|
||||
<button v-if="cancelText" class="wd-action-sheet__cancel" @click="handleCancel">{{ cancelText }}</button>
|
||||
</view>
|
||||
</wd-popup>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'wd-action-sheet',
|
||||
options: {
|
||||
addGlobalClass: true,
|
||||
virtualHost: true,
|
||||
styleIsolation: 'shared'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import wdPopup from '../wd-popup/wd-popup.vue'
|
||||
import wdIcon from '../wd-icon/wd-icon.vue'
|
||||
import wdLoading from '../wd-loading/wd-loading.vue'
|
||||
import { watch, ref } from 'vue'
|
||||
import { actionSheetProps, type Panel } from './types'
|
||||
import { isArray } from '../common/util'
|
||||
|
||||
const props = defineProps(actionSheetProps)
|
||||
const emit = defineEmits(['select', 'click-modal', 'cancel', 'closed', 'close', 'open', 'opened', 'update:modelValue'])
|
||||
|
||||
const formatPanels = ref<Array<Panel> | Array<Panel[]>>([])
|
||||
|
||||
const showPopup = ref<boolean>(false)
|
||||
|
||||
watch(() => props.panels, computedValue, { deep: true, immediate: true })
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newValue) => {
|
||||
showPopup.value = newValue
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
function isPanelArray() {
|
||||
return props.panels.length && !isArray(props.panels[0])
|
||||
}
|
||||
function computedValue() {
|
||||
formatPanels.value = isPanelArray() ? [props.panels as Panel[]] : (props.panels as Panel[][])
|
||||
}
|
||||
|
||||
function select(rowIndex: number, type: 'action' | 'panels', colIndex?: number) {
|
||||
if (type === 'action') {
|
||||
if (props.actions[rowIndex].disabled || props.actions[rowIndex].loading) {
|
||||
return
|
||||
}
|
||||
emit('select', {
|
||||
item: props.actions[rowIndex],
|
||||
index: rowIndex
|
||||
})
|
||||
} else if (isPanelArray()) {
|
||||
emit('select', {
|
||||
item: props.panels[Number(colIndex)],
|
||||
index: colIndex
|
||||
})
|
||||
} else {
|
||||
emit('select', {
|
||||
item: (props.panels as Panel[][])[rowIndex][Number(colIndex)],
|
||||
rowIndex,
|
||||
colIndex
|
||||
})
|
||||
}
|
||||
if (props.closeOnClickAction) {
|
||||
close()
|
||||
}
|
||||
}
|
||||
function handleClickModal() {
|
||||
emit('click-modal')
|
||||
}
|
||||
function handleCancel() {
|
||||
emit('cancel')
|
||||
close()
|
||||
}
|
||||
function close() {
|
||||
emit('update:modelValue', false)
|
||||
emit('close')
|
||||
}
|
||||
function handleOpen() {
|
||||
emit('open')
|
||||
}
|
||||
function handleOpened() {
|
||||
emit('opened')
|
||||
}
|
||||
function handleClosed() {
|
||||
emit('closed')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import './index.scss';
|
||||
</style>
|
||||
Reference in New Issue
Block a user