公司有一个采用uni-app框架写的app应用,里面的弹层基本是使用官方的uni.showModal之类的api实现弹层,在设备上表现就是原生的弹层,在客户的要求下,需要更换成设计的样式,所以就开始实现这样一个组件。
根据弹层经常使用的方法和方式可以大致列出他需要的属性和方法:
类型:
alert/confirm
等展示图标 icon
展示内容 content
可以api调用
支持
promise
,可以使用$api.xx().then
前几项就很好做,就在data
中定义好字段,外层直接拿官方的轮子uni-popup
,这样少写一些控制弹出的逻辑(懒的),这样大致结构就写好了
// template部分 <uni-popup ref="popup" :maskClick="maskClick"> <view class="st-layer" :style="{ width: width }"> <view class="st-layer__content"> <!-- #ifndef APP-NVUE --> <text class="st-layer__icon" :class="option.iconClass || getIconClass()" v-if="option.type !== 'none' && option.showIcon"></text> <!-- #endif --> <view class="st-layer__msg" v-if="option.msg"> <text>{{ option.msg }}</text> </view> </view> <view class="st-layer__footer" :class="{'is-reverse-cofirmcancel' : isReverseConfirmCancel}" v-if="option.showConfirmButton || option.showCancelButton"> <view class="st-layer__footer__btn st-layer__footer__btn--confirm" @tap.stop="confirmClick" v-if="option.showConfirmButton"><text>确认</text></view> <view class="st-layer__footer__btn st-layer__footer__btn--cancel" @tap.stop="cancelClick" v-if="option.showCancelButton"><text>取消</text></view> </view> </view> </uni-popup>
然后js部分先简单实现了一些open和close方法
data() { return { option: {} } }, methods: { open(option) { let defaultOption = { showCancelButton: false, // 是否显示取消按钮 cancelButtonText: '取消', // 取消按钮文字 showConfirmButton: true, // 是否显示确认按钮 confirmButtonText: '取消', // 确认按钮文字 showIcon: true, // 是否显示图标 iconClass: null, // 图标class自定义 type: 'none', // 类型 confirm: null, // 点击确认后的逻辑 cancel: null, // 点击取消后的逻辑 msg: '' } this.option = Object.assign({}, defaultOption, option) this.$refs.popup.open() }, close() { this.$refs.popup.close() }, confirmClick() { const confirmHandler = this.option.confirm if (confirmHandler && typeof confirmHandler === 'function') { confirmHandler() } this.close() this.$emit('confirm') }, cancelClick() { const cancelHandler = this.option.cancel if (cancelHandler && typeof cancelHandler === 'function') { cancelHandler() } this.close() this.$emit('cancel') } }
目前在其他页面已经可以使用
// test.vue 可以使用uni-app的 [easycom组件规范](https://uniapp.dcloud.io/component/README?id=easycom%e7%bb%84%e4%bb%b6%e8%a7%84%e8%8c%83),不用写import语句 <st-layer ref="stLayer"></st-layer>
// js部分 this.$refs.stLayer.open({ msg: '测试', confirm: () => { console.log('点击了确认') }, cancel: () => { console.log('点击了取消') } })
现在基本功能已经实现,但是有人要说了,这样调用不方便,我想这样调用
open(msg).then(() => { console.log('点击了确认') }).catch(() => { console.log('点击了取消') })
那如何实现promise
化呢?最简单的方法就是让open方法返回一个promise
。如何点击确认或取消的时候进入then
方法呢,看下面的写法
... open() { return new promise((reoslve, reject) => { ... this.option.confirm = this.option.confirm || function confirmResolve () { resolve() } this.option.cancel = this.option.cancel || function cancelReject () { reject() } }) } ...
如果要封装其他单独的方法,比如confirm
之类,可以在open基础上扩展:
confirm(msg, option = {}) { if (typeof msg === 'object') { option = msg } else { option.msg = msg } return this.open({ ...option, showCancelButton: true, type: 'confirm' }) } // 调用方式 this.$refs.stLayer.confirm('是否确认?').then().catch()
这样基本的弹层组件已经实现。下面也就是最后一步全局使用
原有vue项目写的layer组件要全局使用通常是采用下面的方法注入到页面中
import main from './main.vue' const LayerConstructor = vue.extend(main) const initInstance = () => { instance = new LayerConstructor({ el: document.createElement('div') }) instance.callback = defaultCallback document.getElementById('app').appendChild(instance.$el) }
直接拉过来用,结果报错,提示error: document is undefined
,才想起uni-app
跟普通vue项目的有一个很大的区别,在它的运行原理中有介绍:
uni-app
逻辑层和视图层分离,在非H5端运行时,从架构上分为逻辑层和视图层两个部分。逻辑层负责执行业务逻辑,也就是运行js代码,视图层负责页面渲染。虽然开发者在一个vue页面里写js和css,但其实,编译时就已经将它们拆分了。逻辑层是运行在一个独立的jscore里的,它不依赖于本机的webview,所以一方面它没有浏览器兼容问题,可以在Android4.4上跑es6代码,另一方面,它无法运行window、document、navigator、localstorage等浏览器专用的js API。
所以这种注册全局的方法已经不可用。那该如何在uni-app
中实现呢? 翻看官方论坛,找到了一个实现loadervue-inset-loader
,实现原理就是获取sfc模板内容,在指定位置插入自定义内容(也就是需要全局的组件),使用方式如下:
// 第一步 npm install vue-inset-loader --save-dev // 第二步 在vue.config.js(hbuilderx创建的项目没有的话新建一个)中注入loader module.export = { chainWebpack: config => { // 超级全局组件 config.module .rule('vue') .test(/\.vue$/) .use() .loader(path.resolve(__dirname, "./node_modules/vue-inset-loader")) .end() } } // 支持自定义pages.json文件路径 // options: { // pagesPath: path.resolve(__dirname,'./src/pages.json') // } // 第三步 pages.json配置文件中添加insetLoader "insetLoader": { "config":{ "confirm": "<BaseConfirm ref='confirm'></BaseConfirm>", "abc": "<BaseAbc ref='BaseAbc'></BaseAbc>" }, // 全局配置 "label":["confirm"], "rootEle":"div" }
配置说明
config
(default:{}
)定义标签名称和内容的键值对
label
(default:[]
)需要全局引入的标签,打包后会在所有页面引入此标签
rootEle
(default:"div"
)根元素的标签类型,缺省值为div,支持正则,比如匹配任意标签 “.*”
label
和rootEle
支持在单独页面的style里配置,优先级高于全局配置
到这,该组件就可以全局使用了,不需要在每个页面写标签使用,只需要调用api就可以。
后面可以再根据使用情况进行优化处理。水平有限,欢迎各位大佬指点。
2.分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3.不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4.本站提供的源码、模板、插件等其他资源,都不包含技术服务请大家谅解!
5.如有链接无法下载或失效,请联系管理员处理!
6.本站资源售价只是赞助,收取费用仅维持本站的日常运营所需!