前端之美-VueJsvue. js资料集vue

Vue实例:医院统一信息平台(构建项目)

2018-06-22  本文已影响510人  碧波之心

前言

前面一段时间公司里忙得很晚,睡觉时间都不太够了,就隔了几天。服务端的用户系统已经完成。现在来一点前端的内容。我觉得前后端分离就可以各开发各的。相互之间用某种协议进行交互,整合。我的理解整个平台会由N个服务端项目、N个前端项目、加一些中间件构成。前端可能有WEB页面、手机APP、微信公众号页面、H5页面等等。
这里我们开始做PC端的后台管理页面。我把PC端分为前台、后台、登录三个项目。目前先这样,后续可能还会根据实际情况细分。几个项目都是Vue项目,就把它们合到一个工程中进行,省去共用模块、组件的重复管理。这个工程就需要多入口的Vue工程。

创建项目

因为我已经写过Vue多入口项目创建的文章,这里就不再重复了。移步vue多入口项目。项目名称huip。git地址:https://gitee.com/biboheart/huip-vue.git
创建完成后的目录结构:

目录结构

引入图标

在应用型前端中,图标会用的比较多。fontawesome图标库中的免费图标是比较丰富的。只是npm中最高版本到4.7.2就没有再更新。而官网已经到了5.1.0 。所以我选择下载到本地静态引入的方法使用5.1.0(哪位朋友有好的方法还忘告知)。
从官网下载,拷贝到static目录中。在index.html文件中引入css

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link href="static/fontawesome/css/fontawesome-all.min.css" rel="stylesheet">
    <title>huip</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

引入前端框架

我采用element-ui作为vue前端框架。里面的组件比较丰富。包含了常用的管理组件了。

cnpm i element-ui -S

在三个main.js加入element-ui库,完成后的main.js内容如下

import Vue from 'vue'
import App from '@/components/App'
import router from './router'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'

Vue.config.productionTip = false
Vue.use(ElementUI)

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})

界面布局

在这个工程中,各项目(除了登录)的布局都是基本相同的。因此,需要一个公共的布局组件。利用element的Container 布局容器进行开发。
element-ui的NavMenu 导航菜单,官方提供的每个菜单项是需要自己去生成的,并不能通过树型结构的数据生成出整个菜单。因此,我们先来写个组件,递归生成树型菜单。
以下是一个菜单项的组件代码。

<template>
  <component v-bind:is="currentItemComponent" :index="menu.path" :key="menu.path">
    <i :class="menu.icon" v-if="menu.icon && !hc"></i><span v-if="!hc">{{menu.text}}</span>
    <template slot="title" v-if="hc">
      <i :class="menu.icon" v-if="menu.icon"></i><span slot="title">{{menu.text}}</span>
    </template>
    <tree-menu-item :menu="child" :key="child.name" v-for="child in menu.children" v-if="hc && child.menu"></tree-menu-item>
  </component>
</template>

<script>
export default {
  name: 'TreeMenuItem',
  props: {
    menu: Object
  },
  data () {
    return {
      hc: false
    }
  },
  computed: {
    currentItemComponent: function () {
      return this.hasChildren() ? 'el-submenu' : 'el-menu-item'
    }
  },
  methods: {
    hasChildren () {
      this.hc = !!this.menu.hasChildren && this.menu.children && this.menu.children.length > 0
      return this.hc
    }
  }
}
</script>

<style scoped>
</style>

在公共布局组件中layout.vue中使用它:

<script>
import TreeMenuItem from '../packages/tree-menu/index.js'
export default {
  name: 'HuipLayout',
  components: {
    TreeMenuItem
  },
  props: {
    hmenus: Array,
    vmenus: Array,
    tips: Array,
    logoSrc: String,
    badgeValue: [String, Number]
  },
  data: function () {
    return {
      isCollapse: false,
      asideWidth: '230px',
      vDefActive: this.activePath(3),
      hDefActive: this.activePath(2),
      defaultLogo: require('@/assets/logo.png')
    }
  },
  watch: {
    '$route': function (val) {
      this.vDefActive = this.activePath(3)
      this.hDefActive = this.activePath(2)
    }
  },
  methods: {
    collapseChange: function () {
      this.isCollapse = !this.isCollapse
      this.$emit('changeCollapse', this.isCollapse)
      if (this.isCollapse) {
        this.asideWidth = '65px'
      } else {
        this.asideWidth = '230px'
      }
    },
    activePath: function (max) {
      let pathArr = this.$route.path.split('/')
      let def = ''
      if (max < 2) {
        def = this.$route.path
      } else {
        if (pathArr && pathArr.length > max) {
          for (let i = 1; i < max; i++) {
            def += '/' + pathArr[i]
          }
        } else {
          def = this.$route.path
        }
      }
      return def
    },
    clickLogo: function (...args) {
      this.$emit('logo-click', ...args)
    }
  }
}
</script>

<template>
  <el-container>
    <el-header class="layout-main-header clear">
      <!-- logo -->
      <div class="logo" @click="clickLogo">
        <img :src="logoSrc" v-if="logoSrc"/>
        <img :src="defaultLogo" v-else/>
      </div>
      <!-- 右侧菜单 -->
      <div class="tool-body">
        <div class="user-info-cell">
          <el-badge :value="badgeValue" class="user-info-badge" v-if="badgeValue"></el-badge>
          <slot name="user-info"></slot>
        </div>
        <ul class="tips-box clearfix">
          <li class="tips-item" v-for="item in tips" :key="item.name" v-if="!!tips && tips.length > 0">
            <a :href="item.url" :target="item.target || '_self'">{{item.text}}</a>
          </li>
        </ul>
      </div>
      <!-- 如果有顶菜单则显示顶菜单 -->
      <div class="hmenu-wrap" v-if="hmenus && hmenus.length > 0">
        <el-menu
          :default-active="hDefActive"
          unique-opened
          router
          mode="horizontal"
          background-color="#545c64"
          text-color="#fff"
          active-text-color="#ffd04b">
          <tree-menu-item :menu="item" :key="item.name" v-for="item in hmenus" v-if="item.menu"></tree-menu-item>
        </el-menu>
      </div>
    </el-header>
    <!-- 如果提供了左侧菜单的数据则显示左侧aside -->
    <el-container v-if="vmenus && vmenus.length > 0">
      <el-aside class="layout-main-aside" :width="asideWidth">
        <div class="aside-container">
          <div class="aside-header">
            <div class="tools" @click.prevent="collapseChange">
              <i :class="isCollapse ? 'el-icon-d-arrow-right' : 'el-icon-d-arrow-left'"></i>
            </div>
          </div>
          <div class="aside-main aside-main-hasheader">
            <el-scrollbar class="full-scrollbar">
              <div :class="isCollapse ? 'menu-collapsed' : 'menu-expanded'">
                <el-menu
                  :default-active="vDefActive"
                  class="el-menu-vertical-demo vmenu"
                  unique-opened
                  router
                  :collapse="isCollapse">
                  <tree-menu-item :menu="item" :key="item.name" v-for="item in vmenus" v-if="item.menu"></tree-menu-item>
                </el-menu>
              </div>
            </el-scrollbar>
          </div>
        </div>
      </el-aside>
      <el-container>
        <slot></slot>
      </el-container>
    </el-container>
    <slot v-else></slot>
  </el-container>
</template>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.layout-main-header {
  background-color: #545c64;
  color: #333;
  line-height: 60px;
  text-align: left;
  font-size: 12px;
}
.layout-main-aside {
  color: #333;
  background-color: rgb(238, 241, 246);
}
.logo {
  height:60px;
  width:auto;
  font-size: 12px;
  padding-right:20px;
  border-color: rgba(238,238,238,0.3);
  border-right-width: 0px;
  border-right-style: solid;
  color: #fff;
  float: left;
  box-sizing: border-box;
  cursor: pointer;
}
.logo img {
  width: 40px;
  height: 40px;
  margin: 10px 10px 10px 10px;
}
.tool-body {
  height: 60px;
  width: auto;
  float: right;
  box-sizing: border-box;
  text-align: right;
  padding: 0;
}
.user-info-cell {
  position: relative;
  height:60px;
  width:auto;
  font-size: 12px;
  border-color: rgba(238,238,238,0.3);
  border-style: solid;
  border-width: 0px;
  border-left-width: 1px;
  color: #fff;
  float: right;
  box-sizing: border-box;
}
.user-info-badge {
  position: absolute;
  top: -10px;
  right: -10px;
}
.tool-body ul,
.tool-body li {
  padding: 0;
  margin: 0;
  list-style: outside none none;
}
.tips-box {
  float: right;
  display: inline;
}
.tips-box .tips-item {
  box-sizing: border-box;
  float: left;
  height: 60px;
  line-height: 60px;
  margin-right: 22px;
  text-align: left;
}
.tips-box .tips-item a {
  font-size: 14px;
  text-decoration: none;
  color: #bfcbd9;
}
.tips-box .tips-item a:hover {
  color: #00a2ca;
}
.hmenu-wrap {
  display: block;
  float: left;
  height: 60px;
  line-height: 60px;
}
.tools{
  width: 100%;
  line-height: 44px;
  cursor: pointer;
  text-align: center;
  font-size: 14px;
  background: #e4e8f1;
}
.menu-expanded .vmenu {
  position: relative;
}
.menu-collapsed .vmenu {
  position: fixed;
  z-index: 8888;
}
</style>

完成布局

/src/project/index/pages下创建index.vue文件,这是前台页面的主体组件

<script>
import HuipLayout from '@/components/HuipLayout'
export default {
  name: 'AdminMain',
  components: {
    HuipLayout
  },
  data () {
    return {
      defaultLogo: require('@/assets/logo.png'),
      user: {
        name: 'admin'
      },
      tips: [{
        text: '控制台',
        name: 'admin',
        url: '/admin.html'
      }],
      hmenus: [{
        path: '/dashboard',
        name: 'dashboard',
        text: '仪表盘',
        menu: true,
        icon: 'fas fa-tachometer-alt'
      }, {
        path: '/core',
        name: 'core',
        text: '系统配置',
        menu: true,
        hasChildren: true,
        icon: 'fa fa-cogs',
        children: [{
          path: '/core/app',
          name: 'core_app',
          text: '版本管理',
          menu: true
        }, {
          path: '/core/reedback',
          name: 'core_reedback',
          text: '用户反馈',
          menu: true
        }]
      }, {
        path: '/user',
        name: 'user',
        text: '用户系统',
        menu: true,
        hasChildren: true,
        icon: 'fa fa-users',
        children: [{
          path: '/user/client',
          name: 'user_client',
          text: '客户端管理',
          menu: true
        }, {
          path: '/user/resource',
          name: 'user_resource',
          text: '资源管理',
          menu: true
        }, {
          path: '/user/auth',
          name: 'user_auth',
          text: '权限管理',
          menu: true,
          hasChildren: true,
          children: [{
            path: '/user/auth/system',
            name: 'user_auth_system',
            text: '系统权限',
            menu: true
          }, {
            path: '/user/auth/operation',
            name: 'user_auth_operation',
            text: '操作权限',
            menu: true
          }]
        }, {
          path: '/user/role',
          name: 'user_role',
          text: '角色管理',
          menu: true
        }, {
          path: '/user/user',
          name: 'user_user',
          text: '用户管理',
          menu: true
        }]
      }]
    }
  }
}
</script>

<template>
  <huip-layout :hmenus="hmenus" :tips="tips">
    <div v-popover:userInfoPopover class="user-info clear" slot="user-info">
      <img v-if="user && user.pic" :src="user.pic" class="headimg">
      <img v-else :src="defaultLogo" class="headimg">
    </div>
    <el-popover
      ref="userInfoPopover"
      placement="bottom-start"
      trigger="hover"
      width="240"
      :visible-arrow="false"
      popper-class="user-info-popover">
      <div class="topbar-info-dropdown-memu">
        <div class="topbar-user-info">
          <img v-if="user && user.pic" :src="user.pic" class="topbar-user-avatar">
          <img v-else :src="defaultLogo" class="topbar-user-avatar">
          <p class="topbar-user-name">{{user ? user.name : ''}}</p>
        </div>
        <div class="topbar-user-entrance-list">
          <div class="topbar-user-entrance clear">
            <i class="fas fa-clipboard-list info-prefix-icon"></i>
            <span class="left-text">个人中心</span>
          </div>
          <div class="topbar-user-entrance clear">
            <i class="fas fa-comment-dots info-prefix-icon"></i>
            <span class="left-text">消息中心</span>
            <span class="right-text">20</span>
          </div>
        </div>
        <div>
          <div class="user-btn-list">
            <span class="user-btn-link">退出登录</span>
          </div>
        </div>
      </div>
    </el-popover>
    <router-view class="content-wrap"></router-view>
  </huip-layout>
</template>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.user-info {
  cursor: pointer;
  color: #bfcbd9;
}
.user-info .headimg {
  width: 40px;
  height: 40px;
  border-radius: 20px;
  margin: 10px;
  float: left;
}
.topbar-info-dropdown-memu {
  padding: 0;
  list-style: none;
  background-color: #fff;
  background-clip: padding-box;
  font-size: 12px;
  min-width: 100%;
  margin: 0;
  border: none;
  -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.1);
  box-shadow: 0 1px 3px rgba(0,0,0,.2);
  white-space: nowrap;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
}
.topbar-info-dropdown-memu a,
.topbar-info-dropdown-memu li,
.topbar-info-dropdown-memu p,
.topbar-info-dropdown-memu span {
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  letter-spacing: .02em;
  text-decoration: none;
}
.topbar-info-dropdown-memu .topbar-user-info {
  text-align: center;
  padding-top: 16px;
  border-bottom: 1px solid #eaeaea;
}
.topbar-info-dropdown-memu .topbar-user-info .topbar-user-avatar {
  width: 36px;
  height: 36px;
  border-radius: 18px;
  vertical-align: middle;
}
.topbar-info-dropdown-memu .topbar-user-info .topbar-user-name {
  margin: 8px 0;
}
.topbar-info-dropdown-memu .topbar-user-entrance-list {
  overflow: hidden;
  width: 240px;
}
.topbar-info-dropdown-memu .topbar-user-entrance {
  cursor: pointer;
  height: 20px;
  line-height: 20px;
  padding: 0 16px;
  margin: 12px 0;
  font-size: 12px;
  line-height: 16px;
  position: relative;
}
.topbar-info-dropdown-memu .topbar-user-entrance .info-prefix-icon {
  width: 16px;
  height: 16px;
  vertical-align: text-bottom;
  margin-right: 8px;
  color: #333;
}
.topbar-info-dropdown-memu .topbar-user-entrance .right-text {
  float: right;
  font-size: 10px;
}
.topbar-info-dropdown-memu .user-btn-link {
  cursor: pointer;
  height: 50px;
  line-height: 50px;
  display: block;
  -webkit-transition: all .15s;
  transition: all .15s;
  text-align: center;
  color: #333;
  background-color: #f5f5f6;
  border-top: #eaeaea;
}
</style>

后台(admin)中也同样创建一个主体页面组件。
这里的菜单数据是临时数据,为了看布局效果,最终是根据路由文件生成菜单的。

当前效果

前台页面:


前台页面效果图1
前台页面效果图2

后台页面:


效果图1
效果图2

总结

完成前端页面总体布局,公共组件开发。

上一篇下一篇

猜你喜欢

热点阅读