init
This commit is contained in:
90
uni_modules/nutui-uni/components/dialog/dialog.ts
Normal file
90
uni_modules/nutui-uni/components/dialog/dialog.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import type { CSSProperties, ExtractPropTypes, PropType } from 'vue'
|
||||
import { CANCEL_EVENT, CLOSED_EVENT, OPENED_EVENT, UPDATE_VISIBLE_EVENT } from '../_constants'
|
||||
import type { Interceptor } from '../_utils'
|
||||
import { commonProps, isBoolean, makeStringProp, truthProp } from '../_utils'
|
||||
import { popupProps } from '../popup/popup'
|
||||
import type { FooterDirection, TextAlign } from './type'
|
||||
|
||||
export const dialogProps = {
|
||||
...popupProps,
|
||||
...commonProps,
|
||||
/**
|
||||
* @description 点击蒙层是否关闭对话框
|
||||
*/
|
||||
closeOnClickOverlay: truthProp,
|
||||
/**
|
||||
* @description 标题
|
||||
*/
|
||||
title: makeStringProp(''),
|
||||
/**
|
||||
* @description 内容,支持 HTML
|
||||
*/
|
||||
content: makeStringProp(''),
|
||||
/**
|
||||
* @description 是否隐藏底部按钮栏
|
||||
*/
|
||||
noFooter: Boolean,
|
||||
/**
|
||||
* @description 是否隐藏确定按钮
|
||||
*/
|
||||
noOkBtn: Boolean,
|
||||
/**
|
||||
* @description 是否隐藏取消按钮
|
||||
*/
|
||||
noCancelBtn: Boolean,
|
||||
/**
|
||||
* @description 取消按钮文案
|
||||
*/
|
||||
cancelText: makeStringProp(''),
|
||||
/**
|
||||
* @description 确定按钮文案
|
||||
*/
|
||||
okText: makeStringProp(''),
|
||||
/**
|
||||
* @description 确认按钮是否默认关闭弹窗
|
||||
*/
|
||||
okAutoClose: truthProp,
|
||||
/**
|
||||
* @description 取消按钮是否默认关闭弹窗
|
||||
*/
|
||||
cancelAutoClose: truthProp,
|
||||
/**
|
||||
* @description 文字对齐方向,可选值同 css 的 text-align
|
||||
*/
|
||||
textAlign: makeStringProp<TextAlign>('center'),
|
||||
/**
|
||||
* @description 是否在页面回退时自动关闭
|
||||
*/
|
||||
closeOnPopstate: Boolean,
|
||||
/**
|
||||
* @description 使用横纵方向,可选值`horizontal`、`vertical`
|
||||
*/
|
||||
footerDirection: makeStringProp<FooterDirection>('horizontal'),
|
||||
/**
|
||||
* @description 自定义类名
|
||||
*/
|
||||
customClass: makeStringProp(''),
|
||||
/**
|
||||
* @description 自定义 popup 弹框样式
|
||||
*/
|
||||
popStyle: {
|
||||
type: Object as PropType<CSSProperties>,
|
||||
},
|
||||
/**
|
||||
* @description 是否在页面回退时自动关闭
|
||||
*/
|
||||
beforeClose: Function as PropType<Interceptor>,
|
||||
}
|
||||
|
||||
export type DialogProps = ExtractPropTypes<typeof dialogProps>
|
||||
|
||||
export const dialogEmits = {
|
||||
update: (val: boolean) => isBoolean(val),
|
||||
[UPDATE_VISIBLE_EVENT]: (val: boolean) => isBoolean(val),
|
||||
ok: () => true,
|
||||
[CANCEL_EVENT]: () => true,
|
||||
[OPENED_EVENT]: () => true,
|
||||
[CLOSED_EVENT]: () => true,
|
||||
}
|
||||
|
||||
export type DialogEmits = typeof dialogEmits
|
||||
100
uni_modules/nutui-uni/components/dialog/dialog.vue
Normal file
100
uni_modules/nutui-uni/components/dialog/dialog.vue
Normal file
@@ -0,0 +1,100 @@
|
||||
<script setup lang="ts">
|
||||
import { defineComponent } from 'vue'
|
||||
import { PREFIX } from '../_constants'
|
||||
import { useTranslate } from '../../locale'
|
||||
import NutButton from '../button/button.vue'
|
||||
import NutPopup from '../popup/popup.vue'
|
||||
import { dialogEmits, dialogProps } from './dialog'
|
||||
import { useDialog } from './use-dialog'
|
||||
|
||||
const props = defineProps(dialogProps)
|
||||
const emit = defineEmits(dialogEmits)
|
||||
const {
|
||||
contentStyle,
|
||||
showPopup,
|
||||
onClickOverlay,
|
||||
onCancel,
|
||||
onOk,
|
||||
classes,
|
||||
closed,
|
||||
dialogStatus,
|
||||
showDialog,
|
||||
} = useDialog(props, emit)
|
||||
|
||||
defineExpose({ showDialog, onOk, onCancel })
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
const componentName = `${PREFIX}-dialog`
|
||||
export default defineComponent({
|
||||
name: componentName,
|
||||
inheritAttrs: false,
|
||||
options: {
|
||||
virtualHost: true,
|
||||
addGlobalClass: true,
|
||||
styleIsolation: 'shared',
|
||||
},
|
||||
})
|
||||
const { translate } = useTranslate(componentName)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<NutPopup
|
||||
v-model:visible="showPopup"
|
||||
:close-on-click-overlay="false"
|
||||
:lock-scroll="lockScroll"
|
||||
:pop-class="popClass"
|
||||
:overlay-class="overlayClass"
|
||||
:overlay-style="overlayStyle"
|
||||
:custom-style="popStyle"
|
||||
:z-index="zIndex"
|
||||
round
|
||||
:transition="transition"
|
||||
@click-overlay="onClickOverlay"
|
||||
@click-close-icon="closed"
|
||||
>
|
||||
<view :class="classes" :style="customStyle">
|
||||
<view v-if="$slots.header || dialogStatus.title" class="nut-dialog__header">
|
||||
<slot v-if="$slots.header" name="header" />
|
||||
<template v-else>
|
||||
{{ dialogStatus.title || props.title }}
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<view class="nut-dialog__content" :style="contentStyle">
|
||||
<slot v-if="$slots.default" name="default" />
|
||||
<rich-text v-else-if="typeof content === 'string'" :nodes="dialogStatus.content || props.content" />
|
||||
<!-- <component :is="content" v-else /> -->
|
||||
</view>
|
||||
|
||||
<view v-if="!dialogStatus.noFooter" class="nut-dialog__footer" :class="{ [footerDirection]: dialogStatus.footerDirection }">
|
||||
<slot v-if="$slots.footer" name="footer" />
|
||||
<template v-else>
|
||||
<NutButton
|
||||
v-if="!dialogStatus.noCancelBtn"
|
||||
size="small"
|
||||
plain
|
||||
type="primary"
|
||||
custom-class="nut-dialog__footer-cancel"
|
||||
@click="onCancel"
|
||||
>
|
||||
{{ dialogStatus.cancelText || props.cancelText || translate('cancel') }}
|
||||
</NutButton>
|
||||
<NutButton
|
||||
v-if="!dialogStatus.noOkBtn"
|
||||
size="small"
|
||||
type="primary"
|
||||
custom-class="nut-dialog__footer-ok"
|
||||
@click="onOk"
|
||||
>
|
||||
{{ dialogStatus.okText || props.okText || translate('confirm') }}
|
||||
</NutButton>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</NutPopup>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
@import './index';
|
||||
</style>
|
||||
79
uni_modules/nutui-uni/components/dialog/index.scss
Normal file
79
uni_modules/nutui-uni/components/dialog/index.scss
Normal file
@@ -0,0 +1,79 @@
|
||||
@import '../button/index';
|
||||
@import '../popup/index';
|
||||
|
||||
.nut-theme-dark {
|
||||
.nut-dialog {
|
||||
&__header {
|
||||
color: $dark-color3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-dialog {
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: $dialog-width;
|
||||
min-height: 156px;
|
||||
padding: 28px 24px 16px;
|
||||
|
||||
&__header {
|
||||
display: block;
|
||||
height: 20px;
|
||||
font-size: 16px;
|
||||
font-weight: $dialog-header-font-weight;
|
||||
color: $dialog-header-color;
|
||||
text-align: center;
|
||||
|
||||
@include oneline-ellipsis;
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
max-height: 268px;
|
||||
margin: 20px 0;
|
||||
overflow: auto;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
color: $text-color;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
&__footer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: $dialog-footer-justify-content;
|
||||
width: 100%;
|
||||
|
||||
&.vertical {
|
||||
flex-direction: column;
|
||||
|
||||
.nut-button {
|
||||
min-width: 100%;
|
||||
margin: 0;
|
||||
|
||||
&.nut-dialog__footer-cancel {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
&.nut-dialog__footer-ok {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.nut-button {
|
||||
min-width: 100px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
&-ok {
|
||||
max-width: 128px;
|
||||
}
|
||||
}
|
||||
}
|
||||
3
uni_modules/nutui-uni/components/dialog/index.ts
Normal file
3
uni_modules/nutui-uni/components/dialog/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export type * from './dialog'
|
||||
export type * from './type'
|
||||
export * from './use-dialog'
|
||||
71
uni_modules/nutui-uni/components/dialog/type.ts
Normal file
71
uni_modules/nutui-uni/components/dialog/type.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import type { NutAnimationName } from '../transition'
|
||||
|
||||
export const textAlign = ['left', 'center', 'right', 'top'] as const
|
||||
export type TextAlign = (typeof textAlign)[number]
|
||||
export const footerDirection = ['horizontal', 'vertical'] as const
|
||||
export type FooterDirection = (typeof footerDirection)[number]
|
||||
export interface DialogOptions {
|
||||
/**
|
||||
* @description 标题
|
||||
*/
|
||||
title?: string
|
||||
/**
|
||||
* @description 内容,支持 HTML
|
||||
*/
|
||||
content?: string
|
||||
/**
|
||||
* @description 是否隐藏底部按钮栏
|
||||
*/
|
||||
noFooter?: boolean
|
||||
/**
|
||||
* @description 是否隐藏确定按钮
|
||||
*/
|
||||
noOkBtn?: boolean
|
||||
/**
|
||||
* @description 是否隐藏取消按钮
|
||||
*/
|
||||
noCancelBtn?: boolean
|
||||
/**
|
||||
* @description 取消按钮文案
|
||||
*/
|
||||
cancelText?: string
|
||||
/**
|
||||
* @description 确定按钮文案
|
||||
*/
|
||||
okText?: string
|
||||
/**
|
||||
* @description 文字对齐方向,可选值同 css 的 text-align
|
||||
*/
|
||||
textAlign?: TextAlign
|
||||
/**
|
||||
* @description 使用横纵方向 可选值 horizontal、vertical
|
||||
*/
|
||||
footerDirection?: FooterDirection
|
||||
/**
|
||||
* @description 弹出动画类型
|
||||
*/
|
||||
transition?: NutAnimationName
|
||||
/**
|
||||
* @description 点击蒙层是否关闭对话框
|
||||
*/
|
||||
closeOnClickOverlay?: boolean
|
||||
/**
|
||||
* @description 确认按钮是否默认关闭弹窗
|
||||
*/
|
||||
okAutoClose?: boolean
|
||||
}
|
||||
|
||||
export interface DialogInst {
|
||||
/**
|
||||
* @description 弹出对话框
|
||||
*/
|
||||
showDialog: (options: DialogOptions) => void
|
||||
/**
|
||||
* @description 点击确定
|
||||
*/
|
||||
onOk: () => void
|
||||
/**
|
||||
* @description 点击取消
|
||||
*/
|
||||
onCancel: () => void
|
||||
}
|
||||
123
uni_modules/nutui-uni/components/dialog/use-dialog.ts
Normal file
123
uni_modules/nutui-uni/components/dialog/use-dialog.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { CSSProperties, SetupContext } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { CANCEL_EVENT, CLOSED_EVENT, OPENED_EVENT, PREFIX, UPDATE_VISIBLE_EVENT } from '../_constants'
|
||||
import { funInterceptor, getMainClass } from '../_utils'
|
||||
import type { DialogEmits, DialogProps } from './dialog'
|
||||
import type { DialogOptions } from './type'
|
||||
|
||||
const componentName = `${PREFIX}-dialog`
|
||||
|
||||
export function useDialog(props: DialogProps, emit: SetupContext<DialogEmits>['emit']) {
|
||||
const showPopup = ref(props.visible)
|
||||
const dialogStatus = ref<DialogOptions>({
|
||||
title: props.title,
|
||||
content: props.content,
|
||||
cancelText: props.cancelText,
|
||||
okText: props.okText,
|
||||
textAlign: props.textAlign,
|
||||
footerDirection: props.footerDirection,
|
||||
noFooter: props.noFooter,
|
||||
noOkBtn: props.noOkBtn,
|
||||
noCancelBtn: props.noCancelBtn,
|
||||
transition: props.transition,
|
||||
closeOnClickOverlay: props.closeOnClickOverlay,
|
||||
okAutoClose: props.okAutoClose,
|
||||
})
|
||||
|
||||
watch(() => props.title, title => dialogStatus.value.title = title)
|
||||
|
||||
const showDialog = (options: DialogOptions) => {
|
||||
dialogStatus.value = {
|
||||
title: options.title || props.title,
|
||||
content: options.content || props.content,
|
||||
cancelText: options.cancelText || props.cancelText,
|
||||
okText: options.okText || props.okText,
|
||||
okAutoClose: options.okAutoClose || props.okAutoClose,
|
||||
textAlign: options.textAlign || props.textAlign,
|
||||
footerDirection: options.footerDirection || props.footerDirection,
|
||||
noFooter: options.noFooter || props.noFooter,
|
||||
noOkBtn: options.noOkBtn || props.noOkBtn,
|
||||
transition: options.transition || props.transition,
|
||||
noCancelBtn: options.noCancelBtn || props.noCancelBtn,
|
||||
closeOnClickOverlay: options.closeOnClickOverlay || props.closeOnClickOverlay,
|
||||
}
|
||||
showPopup.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (props.closeOnPopstate) {
|
||||
// #ifdef H5
|
||||
window.addEventListener('popstate', () => {
|
||||
closed('page')
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
})
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(value) => {
|
||||
showPopup.value = value
|
||||
|
||||
if (value)
|
||||
emit(OPENED_EVENT)
|
||||
},
|
||||
)
|
||||
|
||||
const classes = computed(() => {
|
||||
return getMainClass(props, componentName)
|
||||
})
|
||||
|
||||
function update(val: boolean) {
|
||||
emit('update', val)
|
||||
emit(UPDATE_VISIBLE_EVENT, val)
|
||||
}
|
||||
|
||||
function closed(action?: string) {
|
||||
funInterceptor(props.beforeClose, {
|
||||
args: [action],
|
||||
done: () => {
|
||||
showPopup.value = false
|
||||
update(false)
|
||||
emit(CLOSED_EVENT)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
function onCancel() {
|
||||
emit(CANCEL_EVENT)
|
||||
if (props.cancelAutoClose) {
|
||||
showPopup.value = false
|
||||
closed(CANCEL_EVENT)
|
||||
}
|
||||
}
|
||||
|
||||
function onOk() {
|
||||
emit('ok')
|
||||
if (props.okAutoClose)
|
||||
closed('ok')
|
||||
}
|
||||
|
||||
function onClickOverlay() {
|
||||
if (props.closeOnClickOverlay)
|
||||
closed('')
|
||||
}
|
||||
|
||||
const contentStyle = computed(() => {
|
||||
return {
|
||||
textAlign: dialogStatus.value.textAlign,
|
||||
} as CSSProperties
|
||||
})
|
||||
|
||||
return {
|
||||
contentStyle,
|
||||
showPopup,
|
||||
onClickOverlay,
|
||||
onCancel,
|
||||
onOk,
|
||||
closed,
|
||||
classes,
|
||||
showDialog,
|
||||
dialogStatus,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user