Vue.js专区Vue单页应用我爱编程

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

2018-04-08  本文已影响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' });
    },
  },
};

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

上一篇 下一篇

猜你喜欢

热点阅读