vue項目,有一個登陸頁面做爲單獨頁面來使用。想要將其改形成 一個modal,而後全局可調用。相似於 mint-ui 的 toast組件這樣。css
要用到的位置主要是:vue頁面內、接口請求的響應數據處理方法內(環境是 沒法拿到當前做用域 this)vue
將登錄頁面modal封裝成一個 插件。web
正常書寫,主要是提供一個組件廣播事件 this.$emit('on-logged')。在 登陸完成後 通知調用者,作一些操做。api
<template> <mt-popup v-model="showValue" :modal="showModal" popup-transition="popup-fade" class="mint-popup-modal"> <div class="zyby-container box-sizing-border"> <mt-header> <by-back slot="left" :specialBack="_hide"></by-back> </mt-header> <div class="welcome-block">歡迎登陸八音!</div> <div class="input-block"> <mt-field class="none-border" placeholder="手機號" type="tel" v-model="pm.telephone" :attr="{ maxlength: 11 }" ></mt-field> <mt-field placeholder="驗證碼" type="tel" v-model="pm.code" :attr="{ maxlength: 6 }"> <count-down ref="countDown" class="count-down" :getcodebefore="_getCode" :model="codeData.model" > <span slot="secUnit">s</span> </count-down> </mt-field> </div> <!--<div class="interpretation">完成「八音App」登陸註冊以便後續獎勵發放</div>--> <mt-button class="login-block" :class="classObject" type="primary" size="large" :disabled="!loginFlag" @click="_goLogin" >登陸</mt-button> <div class="tip-block">未註冊用戶登陸表明已贊成註冊</div> </div> </mt-popup> </template> <script> import { CountDown } from "vue-zyby-ui"; import { addMinutes, format} from 'date-fns' import { sendCode, login } from "@/api/index.js"; import { cellPhoneValidate, numberValidate } from "@/libs/stringUtil.js"; import { LOCALDATA } from '@/libs/constans.js' import storage from '@/libs/storage.js' import { commonValidate } from '@/libs/validateUtil.js' import config from '@/config' export default { name: "Login", components: { CountDown }, data() { return { codeData: { showCountDown: false, model: "validateCode" }, pm: { telephone: null, code: null, }, showValue: false, showModal: false }; }, mounted() { document.querySelectorAll("input").forEach(item => { item.addEventListener("blur", function() { window.scroll(0, 0); }); }); }, computed: { classObject: function() { return { "login-block-active": this.pm.telephone && this.pm.code }; }, loginFlag: function() { return this.pm.telephone && this.pm.code; } }, props: { value: { type: Boolean, default: false }, }, watch: { value (val) { this.showValue = val }, showValue (val) { /*this.$emit('input', val) if (val) { if (this.showInput) { this.msg = '' setTimeout(() => { if (this.$refs.input) { this.setInputFocus() } }, 300) } this.$emit('on-show') // emit just after msg is cleared }*/ } }, methods: { _getCode() { if (this._validatePhone()) { this.$refs.countDown.setEnd(format(addMinutes(new Date(), 1), 'YYYY/MM/DD HH:mm:ss')) return sendCode(this.pm.telephone); } }, _validatePhone() { const validateData = [ { '請輸入聯繫方式': !this.pm.telephone }, { '請輸入正確的手機號': !cellPhoneValidate(this.pm.telephone) } ] return commonValidate(validateData) }, _goLogin() { if (!this._validatePhone()) { return false } const validateData = [ { '請輸入驗證碼': !this.pm.code }, { '請輸入正確的驗證碼': (this.pm.code.length != 6 || !numberValidate(this.pm.code)) }, ] if (commonValidate(validateData)) { login(this.pm).then(res => { // 設置登陸標誌 let user = { telephone: this.pm.telephone, token: res.token, uuid: config.uuid, id: res.id } storage.setToken(user) this.showValue = false this.$emit('on-logged') }) } }, _hide () { this.showValue = false } }, }; </script> <style scoped lang="less" rel="stylesheet/less"> @import "../assets/css/function.less"; @no-left-padding: { padding-left: 0; }; @border-bottom-style: { border-bottom: 1px solid #d3dfef; /*no*/ }; .input-text(@opacity: 0.45) { font-family: PingFangSC-Regular; font-size: 28px; color: rgba(75, 84, 97, @opacity); } .interpretation{ width: 100%; background-color: #EEFAF8; height: 52px; line-height: 52px; font-size: 24px; color: #009F8A; text-align: center; margin-top: 30px; margin-bottom: 180px; } .mint-header { @no-left-padding(); } .zyby-container { padding-top: 88px; position: relative; height: 100%; background-color: #fff; } .welcome-block { font-family: PingFangSC-Semibold; font-size: 50px; color: rgba(62, 74, 89, 0.75); letter-spacing: 2px; /*no*/ text-align: left; line-height: 50px; margin-top: 56px; } .input-block { margin-top: 108px; /*margin-bottom: 211px;*/ @border-bottom-style(); /deep/ .mint-cell-wrapper { padding-left: 0; } .none-border /deep/ .mint-cell-wrapper { background: none; } .mint-field:first-child { @border-bottom-style(); } /deep/ input::-webkit-input-placeholder { .input-text(); } .count-down { .input-text(); border-left: 1px solid rgba(75, 84, 97, 0.45); /*no*/ margin-left: 40px; padding-left: 10px; /deep/ .code-text { color: rgba(75, 84, 97, 0.45); } /deep/ .code-count-down { color: rgba(75, 84, 97, 0.8); } } } .tip-block { opacity: 0.75; font-family: PingFangSC-Regular; font-size: 22px; color: rgba(75, 84, 97, 0.45); line-height: 22px; margin-top: 100px; text-align: center; } .login-block { margin-top: 220px; background-color: rgba(0, 0, 0, 0.1); height: 100px; border-radius: 12px; /*no*/ font-family: PingFangSC-Medium; font-size: 32px; color: #ffffff; letter-spacing: 3px; /*no*/ text-align: center; line-height: 32px; } .login-block-active { background-color: #3ad29f; } </style>
將組件打包成爲一個插件,在此處進行。app
關鍵操做有:less
// plugins/login/index.jside
import LoginComponent from '@/components/Login' import { mergeOptions } from '@/libs/plugin_helper' let $vm const plugin = { install (vue, options = {}) { const Login = vue.extend(LoginComponent) if (!$vm) { $vm = new Login({ el: document.createElement('div') }) document.body.appendChild($vm.$el) } const login = { show (options) { if (typeof options === 'object') { mergeOptions($vm, options) } if (typeof options === 'object' && (options.onShow || options.onHide)) { options.onShow && options.onShow() } this.$watcher && this.$watcher() this.$watcher = $vm.$watch('showValue', (val) => { if (!val && options && options.onHide) { options.onHide() } }) $vm.$off('on-logged') $vm.$on('on-logged', msg => { options && options.onLogged && options.onLogged(msg) }) $vm.showValue = true }, /*setInputValue (val) { vue.nextTick(() => { setTimeout(() => { $vm.setInputValue(val) }, 10) }) },*/ hide () { $vm.showValue = false }, isVisible () { return $vm.showValue } } // all Vux's plugins are included in this.$vux /* if (!vue.$vux) { vue.$vux = { login } } else { vue.$vux.login = login } */ vue.mixin({ created: function () { this.$login = login } }) } } export default plugin export const install = plugin.install
// plugin_helper.jsui
import objectAssign from 'object-assign' const mergeOptions = function ($vm, options) { const defaults = {} for (let i in $vm.$options.props) { if (i !== 'value') { defaults[i] = $vm.$options.props[i].default } } const _options = objectAssign({}, defaults, options) for (let i in _options) { $vm[i] = _options[i] } } export { mergeOptions }
一、安裝登陸modal插件.this
// main.jsspa
import loginPlugin from '@/plugins/Login' // 注意 因爲登陸組件裏,用到了別的一些組件,因此 use 應該在 最下面,即保證 用到的組件已存在 Vue.use(loginPlugin)
二、使用
普通頁面裏:
this.$login.show({ onLogged: () => this.reload() // 登陸完成後的回調方法 })
其餘位置(沒法容易拿到 vue this):
window.$vue = new Vue({ // 將vue實例 綁定到 window el: '#app', router, components: { App }, template: '<App/>' }) window.$vue.$login.show(); // 基於window 來使用