美文网首页Vue.js专区Vue单页应用我爱编程
组件继承----基于Vue和PHP打造前后端分离的通用管理系统(

组件继承----基于Vue和PHP打造前后端分离的通用管理系统(

作者: 天渡云 | 来源:发表于2018-04-08 22:16 被阅读65次

    不知不觉已写到第7篇。前面6篇都是探索性的实现,是学习理论的过程,从这篇开始,开始用理论来指导实践了。
    接上篇,我们现在通过组件继承实现Admin页面内导航。

    动态导航

    我们要的导航,是根据登录用户的权限,显示不同的导航菜单。根据职能分工,菜单内容的确定由后端来做更符合实践,前端只需要实现动态渲染菜单即可。好在这是Vue擅长的。


    页内跳转.PNG

    在components目录下新建Aside.vue。在Aside.vue中放置一个el-menu,我们先用一个静态的占个位,然后实现成动态的。静态的我们直接用ElemenUI自己的例子好了。

    <template>
      <el-row>
        <el-col :span="24">
          <h5>管理后台</h5>
          <el-menu
            default-active="2"
            class="el-menu-vertical-demo"
            background-color="#545c64"
            text-color="#fff"
            active-text-color="#ffd04b">
            <el-submenu index="1">
              <template slot="title">
                <i class="el-icon-location"></i>
                <span>导航一</span>
              </template>
              <el-menu-item-group>
                <template slot="title">分组一</template>
                <el-menu-item index="1-1">选项1</el-menu-item>
                <el-menu-item index="1-2">选项2</el-menu-item>
              </el-menu-item-group>
              <el-menu-item-group title="分组2">
                <el-menu-item index="1-3">选项3</el-menu-item>
              </el-menu-item-group>
              <el-submenu index="1-4">
                <template slot="title">选项4</template>
                <el-menu-item index="1-4-1">选项1</el-menu-item>
              </el-submenu>
            </el-submenu>
            <el-menu-item index="2">
              <i class="el-icon-menu"></i>
              <span slot="title">导航二</span>
            </el-menu-item>
            <el-menu-item index="3" disabled>
              <i class="el-icon-document"></i>
              <span slot="title">导航三</span>
            </el-menu-item>
            <el-menu-item index="4">
              <i class="el-icon-setting"></i>
              <span slot="title">导航四</span>
            </el-menu-item>
          </el-menu>
        </el-col>
      </el-row>
    </template>
    
    <script>
    export default {
    };
    </script>
    
    <style scoped>
    /*去掉最右边的白框,个人习惯*/
    .el-menu {
      border-right: 0;
    }
    </style>
    

    Admin.vue

    <el-aside width="300px"><Aside /></el-aside>
    
    name: 'App',
      components: {
        Aside,
      },
    

    修改下Admin.vue中的背景色,否则太难看

    .el-aside {
      background-color: #545c64;/*这里*/
      color: #333;
      text-align: center;
    }
    

    现在,npm run dev,正常的话,显示效果已经出来了!
    下面来实现动态效果。
    现在修改Aside.vue来实现自定义菜单

    <template>
      <el-row>
        <el-col :span="24">
          <h5>管理后台</h5>
          <el-menu
            default-active="2"
            class="el-menu-vertical-demo"
            background-color="#545c64"
            text-color="#fff"
            active-text-color="#ffd04b">
            <template v-for="menu in menus">
            <el-submenu :index="menu.index" v-if="menu.items" :key="menu.index">
              <template slot="title"><i :class="getIcon(menu.icon)"></i>{{menu.label}}</template>
              <el-menu-item v-for="item in menu.items"
               :index="item.index" @click="send(item)" :key="item.index">
                <i :class="getIcon(item.icon)"></i>{{item.label}}
              </el-menu-item>
            </el-submenu>
            <el-menu-item :index="menu.index" @click="send(menu)" :key="menu.index" v-else>
              <i :class="getIcon(menu.icon)"></i>{{menu.label}}
            </el-menu-item>
          </template>
          </el-menu>
        </el-col>
      </el-row>
    </template>
    
    <script>
    /**
     * 定义一个menus,体验菜单的动态渲染
     * 将来会通过props传进来
     */
    const menus = [
      {
        index: '1',
        label: '配置管理',
        icon: 'document',
        items: [
          {
            index: '11',
            label: '网站配置',
            url: 'web.json',
          },
          {
            index: '12',
            label: '数据库配置',
            url: 'core,json',
          },
        ],
      },
      {
        index: '2',
        label: '用户管理',
        url: 'login.json',
        redirect: true,
      },
      {
        index: '3',
        label: '统计信息',
        url: 'status.json',
        icon: 'location',
      },
    ];
    
    export default {
      data() {
        return { menus };
      },
      methods: {
        send(item) {
          this.$emit('redirect', item);
        },
        getIcon(icon = 'menu') {
          return `el-icon-${icon}`;
        },
      },
    };
    </script>
    
    <style scoped>
    /*去掉菜单最右边的白框,个人习惯*/
    .el-menu {
      border-right: 0;
      text-align: left;
    }
    </style>
    

    我们的菜单已经是通过一个数组menus来自定义了,根据vue特性,修改menus菜单就会变化哦,现在我们先不测试这个,留待将来在测试。我们这个有更重要的,菜单点击后会向Admin组件发布redirect消息,我们修改Admin组件,处理这个消息。
    Admin组件添加响应消息的地方

    <el-aside width="240px"><Aside @redirect="onRedirect"/></el-aside>
    ...
    <el-main><component :config="view" v-bind:is="view.name" /></el-main>
    ...
    <script>
    export default {
      data() {
        return {
          view: {
          },
        };
      },
      methods: {
        submit() {
          this.$emit('redirect', { url: '/login.json' });
        },
        onRedirect(action) {
          // 冒泡
          if (this.passUp(action)) return this.$emit('redirect', action);
          // 本地处理
          return this.$HttpSend(action).then((response) => {
            this.view = response.view;
            throw new Error('route');
          }).catch(() => {});
        },
        passUp(action) {
          return action && action.redirect;
        },
      },
    };
    </script>
    

    Admin组件添加几个临时组件方便效果展示

    
    const Table = {
      template: '<H1>Table</H1>',
    };
    
    const Form = {
      template: '<H1>Form</H1>',
    };
    
    const Chart = {
      template: '<H1>Chart</H1>',
    };
    
    export default {
      components: {
        Aside,
        Table,
        Form,
        Chart,
      },
    data() {...
    

    突发奇想,就想修改下Http.js(HttpSend参数格式变了)

    /**
       * 远程调用的核心方法
       * @returns {Promise<T>}
       */
      static HttpSend = (action = {}) => {
        /** @type {boolean} */
        const withValue = action && action.value;
        const option = {
          url: Http.createUrl((action) ? action.url : undefined),
        };
        if (action && action.post) {
          option.method = 'post';
          if (withValue) option.data = Qs.stringify(action.value);
          option.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
        } else {
          if (withValue) option.params = JSON.parse(JSON.stringify(action.value));
          option.method = 'get';
        }
        // 开启跨域cookie
        option.withCredentials = true;
        option.paramsSerializer = params => Qs.stringify(params, { arrayFormat: 'brackets' });
        return $http(option).then((response) => {
          const data = response.data;
          // 判断返回结果信息
          if ((function isError(v) {
            if (typeof v !== 'object') return false;
            if (v.status === false) return true;
            return (v.status && Number(v.status) <= 0);
          })(data)) return Http.onError(data, '操作无效');
          return Http.onReceive(data);
        }, error => Http.onError(error, '数据获取失败'))
          .catch(error => Http.onError(error, '内部错误'));
      };
    
      /**
       * 触发钩子函数
       * @type {Function}
       * @param {Object}  data
       * @returns {Object}
       */
      static onReceive = (data) => {
        Http.receivers.forEach(receiver => receiver(data));
        return data;
      }
    
      /**
       * 插件安装函数
       */
      static install(Vue) {
        this.vue = Vue;
    
        this.createUrl = this.vue.createUrl || (url => url);
    
        this.vue.prototype.$httpGet = (url, value = null) => this.HttpSend({ url, value });
        this.vue.prototype.$HttpPost = (url, value = null) => this.HttpSend({ url, value, post: true });
        this.vue.prototype.$HttpSend = Http.HttpSend;
    
        this.vue.prototype.$addReceiver = receiver => this.receivers.push(receiver);
    
        this.vue.prototype.$removeReceiver = id => this.receivers.splice(id, 1);
      }
    

    看看神奇的效果吧。原来的几个按钮,因为Http.js的修改,失效了,改起来很简单,不会就问我。

    这样呢,App和Admin有一部分重复的功能,我们提取出来

    <script>
    export default {
      data() {
        return {
          view: {
          },
        };
      },
      methods: {
        onRedirect(action) {
          // 冒泡
          if (this.passUp(action)) return this.$emit('redirect', action);
          // 本地处理
          return this.$HttpSend(action).then((response) => {
            this.view = response.view;
            throw new Error('route');
          }).catch(() => {});
        },
        passUp(action) {
          return action && action.redirect;
        },
      },
    };
    </script>
    

    取名叫Container.vue放在components下。
    修改App.vue

    import Father from './components/Container';
    ...
    export default {
      name: 'App',
      extends: Father,
      data() {
        return {
          trace: {
            rows: [],
          },
        };
      },
      ...
      methods: {
        passUp() {
          return false;
        },
      },
    };
    </script>
    

    修改Admin.vue

    export default {
      extends: Father,
       ...
      methods: {
        submit() {
          this.$emit('redirect', { url: '/login.json' });
        },
      },
    };
    

    看看效果,妈呀,组件继承就这么简单!!!

    相关文章

      网友评论

      • 复弹哒:战略马克,谢谢大佬分享。

      本文标题:组件继承----基于Vue和PHP打造前后端分离的通用管理系统(

      本文链接:https://www.haomeiwen.com/subject/pvdshftx.html