feat: migrate Dialog component

This commit is contained in:
chenjiahan 2020-07-26 16:35:08 +08:00
parent 47ad40794e
commit f3ff931ebf
9 changed files with 86 additions and 299 deletions

View File

@ -7,6 +7,7 @@
- Tabs: `v-model` 重命名为 `v-model:active` - Tabs: `v-model` 重命名为 `v-model:active`
- Popup: `v-model` 重命名为 `v-model:show` - Popup: `v-model` 重命名为 `v-model:show`
- Circle: `v-model` 重命名为 `v-model:currentRate` - Circle: `v-model` 重命名为 `v-model:currentRate`
- Dialog: `v-model` 重命名为 `v-model:show`
- ShareSheet: `v-model` 重命名为 `v-model:show` - ShareSheet: `v-model` 重命名为 `v-model:show`
- ActionSheet: `v-model` 重命名为 `v-model:show` - ActionSheet: `v-model` 重命名为 `v-model:show`
- List: `v-model` 重命名为 `v-model:loading``error.sync` 重命名为 `v-model:error` - List: `v-model` 重命名为 `v-model:loading``error.sync` 重命名为 `v-model:error`

View File

@ -44,4 +44,5 @@ module.exports = [
'tabs', 'tabs',
'sticky', 'sticky',
'picker', 'picker',
'dialog',
]; ];

View File

@ -1,19 +1,19 @@
import { createNamespace, addUnit } from '../utils'; import { createNamespace, addUnit } from '../utils';
import { BORDER_TOP, BORDER_LEFT } from '../utils/constant'; import { BORDER_TOP, BORDER_LEFT } from '../utils/constant';
import { PopupMixin } from '../mixins/popup'; import Popup from '../popup';
import Button from '../button'; import Button from '../button';
const [createComponent, bem, t] = createNamespace('dialog'); const [createComponent, bem, t] = createNamespace('dialog');
export default createComponent({ export default createComponent({
mixins: [PopupMixin()],
props: { props: {
show: Boolean,
title: String, title: String,
width: [Number, String], width: [Number, String],
message: String, message: String,
className: null, className: null,
callback: Function, callback: Function,
lazyRender: Boolean,
beforeClose: Function, beforeClose: Function,
messageAlign: String, messageAlign: String,
cancelButtonText: String, cancelButtonText: String,
@ -65,7 +65,7 @@ export default createComponent({
this.$emit(action); this.$emit(action);
// show not trigger close event when hidden // show not trigger close event when hidden
if (!this.value) { if (!this.show) {
return; return;
} }
@ -85,7 +85,7 @@ export default createComponent({
}, },
onClose(action) { onClose(action) {
this.close(); this.$emit('update:show', false);
if (this.callback) { if (this.callback) {
this.callback(action); this.callback(action);
@ -133,9 +133,9 @@ export default createComponent({
); );
}, },
genContent(hasTitle, messageSlot) { genContent(hasTitle) {
if (messageSlot) { if (this.$slots.default) {
return <div class={bem('content')}>{messageSlot}</div>; return <div class={bem('content')}>{this.$slots.default()}</div>;
} }
const { message, messageAlign } = this; const { message, messageAlign } = this;
@ -145,9 +145,7 @@ export default createComponent({
'has-title': hasTitle, 'has-title': hasTitle,
[messageAlign]: messageAlign, [messageAlign]: messageAlign,
}), }),
domProps: { [this.allowHtml ? 'innerHTML' : 'textContent']: message,
[this.allowHtml ? 'innerHTML' : 'textContent']: message,
},
}; };
return ( return (
@ -160,37 +158,32 @@ export default createComponent({
}, },
render() { render() {
if (!this.shouldRender) {
return;
}
const { message } = this; const { message } = this;
const messageSlot = this.slots(); const title = this.$slots.title ? this.$slots.title() : this.title;
const title = this.slots('title') || this.title;
const Title = title && ( const Title = title && (
<div class={bem('header', { isolated: !message && !messageSlot })}> <div
class={bem('header', { isolated: !message && !this.$slots.default })}
>
{title} {title}
</div> </div>
); );
return ( return (
<transition <Popup
name={this.transition} show={this.show}
onAfterEnter={this.onOpened} role="dialog"
onAfterLeave={this.onClosed} class={[bem(), this.className]}
style={{ width: addUnit(this.width) }}
transition={this.transition}
lazyRender={this.lazyRender}
aria-labelledby={this.title || message}
onOpened={this.onOpened}
onClosed={this.onClosed}
> >
<div {Title}
vShow={this.value} {this.genContent(title)}
role="dialog" {this.genButtons()}
aria-labelledby={this.title || message} </Popup>
class={[bem(), this.className]}
style={{ width: addUnit(this.width) }}
>
{Title}
{this.genContent(title, messageSlot)}
{this.genButtons()}
</div>
</transition>
); );
}, },
}); });

View File

@ -84,7 +84,7 @@ export default {
If you need to render vue components within a dialog, you can use dialog component. If you need to render vue components within a dialog, you can use dialog component.
```html ```html
<van-dialog v-model="show" title="Title" show-cancel-button> <van-dialog v-model:show="show" title="Title" show-cancel-button>
<img src="https://img.yzcdn.cn/vant/apple-3.jpg" /> <img src="https://img.yzcdn.cn/vant/apple-3.jpg" />
</van-dialog> </van-dialog>
``` ```
@ -142,7 +142,7 @@ export default {
| Attribute | Description | Type | Default | | Attribute | Description | Type | Default |
| --- | --- | --- | --- | | --- | --- | --- | --- |
| v-model | Whether to show dialog | _boolean_ | - | | v-model:show | Whether to show dialog | _boolean_ | - |
| title | Title | _string_ | - | | title | Title | _string_ | - |
| width `v2.2.7` | Width | _number \| string_ | `320px` | | width `v2.2.7` | Width | _number \| string_ | `320px` |
| message | Message | _string_ | - | | message | Message | _string_ | - |

View File

@ -1,220 +0,0 @@
# Dialog 弹出框
### 介绍
弹出模态框,常用于消息提示、消息确认,或在当前页面内完成特定的交互操作。
弹出框组件支持函数调用和组件调用两种方式。
### 函数调用
Dialog 是一个函数,调用后会直接在页面中弹出相应的模态框。
```js
import { Dialog } from 'vant';
Dialog({ message: '提示' });
```
### 组件调用
通过组件调用 Dialog 时,可以通过下面的方式进行注册:
```js
import Vue from 'vue';
import { Dialog } from 'vant';
// 全局注册
Vue.use(Dialog);
// 局部注册
export default {
components: {
[Dialog.Component.name]: Dialog.Component,
},
};
```
## 代码演示
### 消息提示
用于提示一些消息,只包含一个确认按钮。
```js
Dialog.alert({
title: '标题',
message: '弹窗内容',
}).then(() => {
// on close
});
Dialog.alert({
message: '弹窗内容',
}).then(() => {
// on close
});
```
### 消息确认
用于确认消息,包含取消和确认按钮。
```js
Dialog.confirm({
title: '标题',
message: '弹窗内容',
})
.then(() => {
// on confirm
})
.catch(() => {
// on cancel
});
```
### 异步关闭
通过 `beforeClose` 属性可以传入一个回调函数,在弹窗关闭前进行特定操作。
```js
function beforeClose(action, done) {
if (action === 'confirm') {
setTimeout(done, 1000);
} else {
done();
}
}
Dialog.confirm({
title: '标题',
message: '弹窗内容',
beforeClose,
});
```
### 全局方法
引入 Dialog 组件后,会自动在 Vue 的 prototype 上挂载 `$dialog` 方法,在所有组件内部都可以直接调用此方法。
```js
export default {
mounted() {
this.$dialog.alert({
message: '弹窗内容',
});
},
};
```
### 组件调用
如果需要在弹窗内嵌入组件或其他自定义内容,可以使用组件调用的方式。
```html
<van-dialog v-model="show" title="标题" show-cancel-button>
<img src="https://img.yzcdn.cn/vant/apple-3.jpg" />
</van-dialog>
```
```js
export default {
data() {
return {
show: false,
};
},
};
```
## API
### 方法
| 方法名 | 说明 | 参数 | 返回值 |
| --- | --- | --- | --- |
| Dialog | 展示弹窗 | `options` | `Promise` |
| Dialog.alert | 展示消息提示弹窗 | `options` | `Promise` |
| Dialog.confirm | 展示消息确认弹窗 | `options` | `Promise` |
| Dialog.setDefaultOptions | 修改默认配置,对所有 Dialog 生效 | `options` | `void` |
| Dialog.resetDefaultOptions | 重置默认配置,对所有 Dialog 生效 | - | `void` |
| Dialog.close | 关闭弹窗 | - | `void` |
### Options
通过函数调用 `Dialog` 时,支持传入以下选项:
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| title | 标题 | _string_ | - |
| width `v2.2.7` | 弹窗宽度,默认单位为`px` | _number \| string_ | `320px` |
| message | 文本内容,支持通过`\n`换行 | _string_ | - |
| messageAlign | 内容对齐方式,可选值为`left` `right` | _string_ | `center` |
| className | 自定义类名 | _any_ | - |
| showConfirmButton | 是否展示确认按钮 | _boolean_ | `true` |
| showCancelButton | 是否展示取消按钮 | _boolean_ | `false` |
| confirmButtonText | 确认按钮文案 | _string_ | `确认` |
| confirmButtonColor | 确认按钮颜色 | _string_ | `#1989fa` |
| cancelButtonText | 取消按钮文案 | _string_ | `取消` |
| cancelButtonColor | 取消按钮颜色 | _string_ | `black` |
| overlay | 是否展示遮罩层 | _boolean_ | `true` |
| overlayClass `v2.2.7` | 自定义遮罩层类名 | _string_ | - |
| overlayStyle `v2.2.7` | 自定义遮罩层样式 | _object_ | - |
| closeOnPopstate `v2.0.5` | 是否在页面回退时自动关闭 | _boolean_ | `true` |
| closeOnClickOverlay | 是否在点击遮罩层后关闭弹窗 | _boolean_ | `false` |
| lockScroll | 是否锁定背景滚动 | _boolean_ | `true` |
| allowHtml `v2.8.7` | 是否允许 message 内容中渲染 HTML | _boolean_ | `true` |
| beforeClose | 关闭前的回调函数,<br>调用 done() 后关闭弹窗,<br>调用 done(false) 阻止弹窗关闭 | _(action, done) => void_ | - |
| transition `v2.2.6` | 动画类名,等价于 [transtion](https://cn.vuejs.org/v2/api/index.html#transition) 的`name`属性 | _string_ | - |
| getContainer | 指定挂载的节点,[用法示例](#/zh-CN/popup#zhi-ding-gua-zai-wei-zhi) | _string \| () => Element_ | `body` |
### Props
通过组件调用 `Dialog` 时,支持以下 Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| v-model | 是否显示弹窗 | _boolean_ | - |
| title | 标题 | _string_ | - |
| width `v2.2.7` | 弹窗宽度,默认单位为`px` | _number \| string_ | `320px` |
| message | 文本内容,支持通过`\n`换行 | _string_ | - |
| message-align | 内容对齐方式,可选值为`left` `right` | _string_ | `center` |
| show-confirm-button | 是否展示确认按钮 | _boolean_ | `true` |
| show-cancel-button | 是否展示取消按钮 | _boolean_ | `false` |
| confirm-button-text | 确认按钮文案 | _string_ | `确认` |
| confirm-button-color | 确认按钮颜色 | _string_ | `#1989fa` |
| cancel-button-text | 取消按钮文案 | _string_ | `取消` |
| cancel-button-color | 取消按钮颜色 | _string_ | `black` |
| overlay | 是否展示遮罩层 | _boolean_ | `true` |
| overlay-class `v2.2.7` | 自定义遮罩层类名 | _string_ | - |
| overlay-style `v2.2.7` | 自定义遮罩层样式 | _object_ | - |
| close-on-popstate `v2.0.5` | 是否在页面回退时自动关闭 | _boolean_ | `true` |
| close-on-click-overlay | 是否在点击遮罩层后关闭弹窗 | _boolean_ | `false` |
| lazy-render | 是否在显示弹层时才渲染节点 | _boolean_ | `true` |
| lock-scroll | 是否锁定背景滚动 | _boolean_ | `true` |
| allow-html `v2.8.7` | 是否允许 message 内容中渲染 HTML | _boolean_ | `true` |
| before-close | 关闭前的回调函数,<br>调用 done() 后关闭弹窗,<br>调用 done(false) 阻止弹窗关闭 | _(action, done) => void_ | - |
| transition `v2.2.6` | 动画类名,等价于 [transtion](https://cn.vuejs.org/v2/api/index.html#transition) 的`name`属性 | _string_ | - |
| get-container | 指定挂载的节点,[用法示例](#/zh-CN/popup#zhi-ding-gua-zai-wei-zhi) | _string \| () => Element_ | - |
### Events
通过组件调用 `Dialog` 时,支持以下事件:
| 事件 | 说明 | 回调参数 |
| ------- | ------------------------ | -------- |
| confirm | 点击确认按钮时触发 | - |
| cancel | 点击取消按钮时触发 | - |
| open | 打开弹窗时触发 | - |
| close | 关闭弹窗时触发 | - |
| opened | 打开弹窗且动画结束后触发 | - |
| closed | 关闭弹窗且动画结束后触发 | - |
### Slots
通过组件调用 `Dialog` 时,支持以下插槽:
| 名称 | 说明 |
| ------- | ---------- |
| default | 自定义内容 |
| title | 自定义标题 |

View File

@ -26,7 +26,7 @@
{{ t('componentCall') }} {{ t('componentCall') }}
</van-button> </van-button>
<van-dialog <van-dialog
v-model="show" v-model:show="show"
:title="t('title')" :title="t('title')"
show-cancel-button show-cancel-button
:lazy-render="false" :lazy-render="false"

View File

@ -1,29 +1,38 @@
// import Vue from 'vue'; import { createApp, nextTick } from 'vue';
import VanDialog from './Dialog'; import VanDialog from './Dialog';
import { inBrowser } from '../utils'; import { inBrowser } from '../utils';
let instance; let instance;
function isInDocument(element) {
return document.body.contains(element);
}
function initInstance() { function initInstance() {
if (instance) { const root = document.createElement('div');
instance.$destroy(); document.body.appendChild(root);
}
instance = new (Vue.extend(VanDialog))({ instance = createApp({
el: document.createElement('div'), data() {
// avoid missing animation when first rendered return {
propsData: { dialogProps: {
lazyRender: false, show: false,
},
};
}, },
}); methods: {
onToggle(show) {
instance.$on('input', (value) => { this.dialogProps.show = show;
instance.value = value; },
}); },
render() {
return (
<VanDialog
lazyRender={false}
{...{
...this.dialogProps,
'onUpdate:show': this.onToggle,
}}
/>
);
},
}).mount(root);
} }
function Dialog(options) { function Dialog(options) {
@ -33,23 +42,31 @@ function Dialog(options) {
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!instance || !isInDocument(instance.$el)) { if (!instance) {
initInstance(); initInstance();
} }
Object.assign(instance, Dialog.currentOptions, options, { instance.dialogProps = {
resolve, ...Dialog.currentOptions,
reject, ...options,
}); callback: (action) => {
(action === 'confirm' ? resolve : reject)(action);
},
};
nextTick(() => {
instance.dialogProps.show = true;
})
}); });
} }
Dialog.defaultOptions = { Dialog.defaultOptions = {
value: true, show: false,
title: '', title: '',
width: '', width: '',
message: '', message: '',
overlay: true, overlay: true,
callback: null,
className: '', className: '',
allowHtml: true, allowHtml: true,
lockScroll: true, lockScroll: true,
@ -67,9 +84,6 @@ Dialog.defaultOptions = {
showCancelButton: false, showCancelButton: false,
closeOnPopstate: true, closeOnPopstate: true,
closeOnClickOverlay: false, closeOnClickOverlay: false,
callback: (action) => {
instance[action === 'confirm' ? 'resolve' : 'reject'](action);
},
}; };
Dialog.alert = Dialog; Dialog.alert = Dialog;
@ -96,12 +110,12 @@ Dialog.resetDefaultOptions = () => {
Dialog.resetDefaultOptions(); Dialog.resetDefaultOptions();
Dialog.install = () => { Dialog.install = (app) => {
Vue.use(VanDialog); console.log('use VanDialog', VanDialog);
app.use(VanDialog);
app.config.globalProperties.$dialog = Dialog;
}; };
Dialog.Component = VanDialog; Dialog.Component = VanDialog;
Vue.prototype.$dialog = Dialog;
export default Dialog; export default Dialog;

View File

@ -1,7 +1,6 @@
@import '../style/var'; @import '../style/var';
.van-dialog { .van-dialog {
position: fixed;
top: 45%; top: 45%;
left: 50%; left: 50%;
width: @dialog-width; width: @dialog-width;
@ -9,7 +8,6 @@
font-size: @dialog-font-size; font-size: @dialog-font-size;
background-color: @dialog-background-color; background-color: @dialog-background-color;
border-radius: @dialog-border-radius; border-radius: @dialog-border-radius;
transform: translate3d(-50%, -50%, 0);
backface-visibility: hidden; // avoid blurry text after scale animation backface-visibility: hidden; // avoid blurry text after scale animation
transition: @dialog-transition; transition: @dialog-transition;
transition-property: transform, opacity; transition-property: transform, opacity;
@ -80,7 +78,7 @@
} }
} }
&-bounce-enter { &-bounce-enter-from {
transform: translate3d(-50%, -50%, 0) scale(0.7); transform: translate3d(-50%, -50%, 0) scale(0.7);
opacity: 0; opacity: 0;
} }

View File

@ -184,10 +184,10 @@ module.exports = {
path: 'action-sheet', path: 'action-sheet',
title: 'ActionSheet 动作面板', title: 'ActionSheet 动作面板',
}, },
// { {
// path: 'dialog', path: 'dialog',
// title: 'Dialog 弹出框', title: 'Dialog 弹出框',
// }, },
// { // {
// path: 'dropdown-menu', // path: 'dropdown-menu',
// title: 'DropdownMenu 下拉菜单', // title: 'DropdownMenu 下拉菜单',
@ -518,10 +518,10 @@ module.exports = {
path: 'action-sheet', path: 'action-sheet',
title: 'ActionSheet', title: 'ActionSheet',
}, },
// { {
// path: 'dialog', path: 'dialog',
// title: 'Dialog', title: 'Dialog',
// }, },
// { // {
// path: 'dropdown-menu', // path: 'dropdown-menu',
// title: 'DropdownMenu', // title: 'DropdownMenu',