Vue實例:醫(yī)院統(tǒng)一信息平臺(構建項目)

前言

前面一段時間公司里忙得很晚痊剖,睡覺時間都不太夠了韩玩,就隔了幾天。服務端的用戶系統(tǒng)已經(jīng)完成÷侥伲現(xiàn)在來一點前端的內(nèi)容找颓。我覺得前后端分離就可以各開發(fā)各的。相互之間用某種協(xié)議進行交互叮贩,整合击狮。我的理解整個平臺會由N個服務端項目、N個前端項目益老、加一些中間件構成彪蓬。前端可能有WEB頁面、手機APP杨箭、微信公眾號頁面寞焙、H5頁面等等。
這里我們開始做PC端的后臺管理頁面互婿。我把PC端分為前臺捣郊、后臺、登錄三個項目慈参。目前先這樣呛牲,后續(xù)可能還會根據(jù)實際情況細分。幾個項目都是Vue項目驮配,就把它們合到一個工程中進行娘扩,省去共用模塊、組件的重復管理壮锻。這個工程就需要多入口的Vue工程琐旁。

創(chuàng)建項目

因為我已經(jīng)寫過Vue多入口項目創(chuàng)建的文章,這里就不再重復了猜绣。移步vue多入口項目灰殴。項目名稱huip。git地址:https://gitee.com/biboheart/huip-vue.git
創(chuàng)建完成后的目錄結(jié)構:

目錄結(jié)構

引入圖標

在應用型前端中掰邢,圖標會用的比較多牺陶。fontawesome圖標庫中的免費圖標是比較豐富的伟阔。只是npm中最高版本到4.7.2就沒有再更新。而官網(wǎng)已經(jīng)到了5.1.0 掰伸。所以我選擇下載到本地靜態(tài)引入的方法使用5.1.0(哪位朋友有好的方法還忘告知)皱炉。
從官網(wǎng)下載,拷貝到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內(nèi)容如下

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 布局容器進行開發(fā)蒸痹。
element-ui的NavMenu 導航菜單,官方提供的每個菜單項是需要自己去生成的呛哟,并不能通過樹型結(jié)構的數(shù)據(jù)生成出整個菜單叠荠。因此,我們先來寫個組件扫责,遞歸生成樹型菜單榛鼎。
以下是一個菜單項的組件代碼。

<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>

在公共布局組件中l(wèi)ayout.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>
      <!-- 右側(cè)菜單 -->
      <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>
    <!-- 如果提供了左側(cè)菜單的數(shù)據(jù)則顯示左側(cè)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下創(chuàng)建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: '系統(tǒng)配置',
        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: '用戶系統(tǒng)',
        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: '系統(tǒng)權限',
            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)中也同樣創(chuàng)建一個主體頁面組件者娱。
這里的菜單數(shù)據(jù)是臨時數(shù)據(jù),為了看布局效果苏揣,最終是根據(jù)路由文件生成菜單的黄鳍。

當前效果

前臺頁面:


前臺頁面效果圖1

前臺頁面效果圖2

后臺頁面:


效果圖1

效果圖2

總結(jié)

完成前端頁面總體布局,公共組件開發(fā)平匈。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末框沟,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子增炭,更是在濱河造成了極大的恐慌忍燥,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件隙姿,死亡現(xiàn)場離奇詭異梅垄,居然都是意外死亡,警方通過查閱死者的電腦和手機孟辑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門哎甲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蔫敲,“玉大人,你說我怎么就攤上這事炭玫∧魏伲” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵吞加,是天一觀的道長裙犹。 經(jīng)常有香客問我,道長衔憨,這世上最難降的妖魔是什么叶圃? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮践图,結(jié)果婚禮上掺冠,老公的妹妹穿的比我還像新娘。我一直安慰自己码党,他們只是感情好德崭,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著揖盘,像睡著了一般眉厨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上兽狭,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天憾股,我揣著相機與錄音,去河邊找鬼箕慧。 笑死服球,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的颠焦。 我是一名探鬼主播有咨,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蒸健!你這毒婦竟也來了座享?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤似忧,失蹤者是張志新(化名)和其女友劉穎渣叛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盯捌,經(jīng)...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡淮韭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年斩启,在試婚紗的時候發(fā)現(xiàn)自己被綠了澜倦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片欠痴。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡仪召,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤缀雳,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站梢睛,受9級特大地震影響肥印,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜绝葡,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一深碱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧藏畅,春花似錦敷硅、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至诫硕,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間刊侯,已是汗流浹背章办。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留滨彻,地道東北人藕届。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像亭饵,于是被迫代替她去往敵國和親休偶。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354

推薦閱讀更多精彩內(nèi)容