我的vue學習日記(二)——後臺管理系統(vue+vuex+vue-cli+element+axios+router)

GitHub: https://github.com/liubaichao/vue-vuex-vue-cli3.0-element-axios-router.git

效果如圖:
PC
在這裏插入圖片描述
賬號:admin 密碼:123456
在這裏插入圖片描述
MOBILE
在這裏插入圖片描述
在這裏插入圖片描述

介紹:

一、權限系統
二、基礎頁
三、動態表單及其驗證
四、圖片預覽
五、導出

一、權限系統
  • 動態路由 回到菜單
    備註:
      起初,我想使用跟ant design pro權限類似做法,前端寫死,根據後臺返回字段(例:1,2,3)去判斷加載對應的路由,同時生成左側菜單。這樣的話,考慮到一個問題,如果低權限,直接在地址欄輸入高權限的路由,會直接打開頁面。要處理這個就要在路由守衛中去處理,如此一來就很繁瑣。而後,決定使用後臺返回路由,這樣這個問題就不用考慮,壓根沒有這個路由,也不會跳轉了。

mock數據,模擬後臺返回路由樹 || 權限樹(使用的easy mock)

{
  "success": false,
  "message": "成功!",
  "code": 200,
  "redirectUrl": null,
  "date": 1563265902609,
  "data": [{
    "name": "線上審覈", //左側菜單名
    "path": "OnlineAudit", //路由
    "icon": "el-icon-finished", //icon
    "showLeftMenu": true, //是否顯示左側菜單
    "requireAuth": true, //是否路由守衛驗證權限
    "children": []
  }, {
    "name": "回執單",
    "path": "ReceiptList",
    "icon": "el-icon-s-claim",
    "showLeftMenu": true,
    "requireAuth": true,
    "children": []
  }, {
    "name": "患者管理",
    "path": "PatientList",
    "icon": "el-icon-user",
    "showLeftMenu": true,
    "requireAuth": true,
    "children": []
  }, {
    "name": "領藥機構",
    "path": "DrugPoint",
    "icon": "el-icon-s-home",
    "showLeftMenu": true,
    "requireAuth": true,
    "children": []
  }, {
    "name": "志願者管理",
    "path": "VolunteerList",
    "icon": "el-icon-s-custom",
    "showLeftMenu": true,
    "requireAuth": true,
    "children": []
  }, {
    "name": "快遞管理",
    "path": "ExpressList",
    "icon": "el-icon-truck",
    "showLeftMenu": true,
    "requireAuth": true,
    "children": []
  }]
}

說明:以下幾個頁面爲權限控制

1. 涉及login.vue:
  因爲首次登陸,會不加載組件,原因是router.js初始化就加載了,router.js執行時,緩存不存在,不會加載路由。所以要在login成功後執行loadRouter方法加載路由。
2. 涉及router.js:
  因爲用戶刷新,store會清空,需要重新設置store中存權限樹(後臺返回路由樹)。起初我放在app.vue中加載路由,考慮放到router中比較方便。

router.js

//動態加載路由 start
let userInfo = localStorage.getItem('userInfo')?JSON.parse(localStorage.getItem('userInfo')):''
if(userInfo){ //此處:用戶手動清除緩存後跳轉login||刷新頁面(store刷新就會清空),重新獲取路由樹
  let userId = JSON.parse(localStorage.getItem('userInfo')).id
  store.dispatch('getRoleArr',{id:userId}).then(res=>{ //獲取路由樹,生成路由
    my.loadRouter(res ,router)
  })
}else{
  router.push('/')
}
//動態加載路由 end

//路由鉤子函數 start
router.beforeEach((to, from, next) => {
  if (to.meta.title) {
    document.title = to.meta.title
  }
  if(to.matched.some(record => record.meta.requireAuth)){
    if(localStorage.getItem('userInfo')){ //是否登陸
      next()
    }else{
      next('/')
    }
  }else{
    next()
  }
})
//路由鉤子函數 end

common.js

const loadRouter = (roleArr = [] ,router={})=>{ //動態加載路由
  let arr = []
  roleArr.map((item)=>{
    arr.push({
        path: '/'+item.path,
        name: item.path,
        component:resolve => require([`../../views/admin/${item.path}`], resolve),//動態生成路由(江湖人稱懶加載,此步) 非常重要!!!
        meta: {
          title: item.name,
          requireAuth: item.requireAuth
        }
    })
  })
  //未定義路由重定向到logon頁面
  arr.push({ 
    path:'*',
    redirect:'/'
  })
  router.addRoutes(arr)
}

store.js

//actions
getRoleArr ({commit},playload) {//獲取權限樹
      return new Promise((resolve,reject)=>{
        axios.get(my.api+"/getRoleArr?id="+playload.id)
        .then((res) => {
          if (res.data.code == 200) {
          let userInfo=JSON.parse(localStorage.getItem('userInfo'))?JSON.parse(localStorage.getItem('userInfo')):{}
            localStorage.setItem("userInfo", JSON.stringify({...userInfo,roleArr:res.data.data})) //讀到最新數據後,重新錄入權限樹
            commit('setLoginInfo', {roleArr:res.data.data}) //重新錄入權限樹
            resolve(res.data.data)
          }else{
            Message({
                message: res.data.message,
                type: 'success',
                duration: 1500
            })
          }
        })
        .catch((error) => {
          Message({
              message: error,
              type: 'success',
              duration: 1500
          })
          reject(error)
        });   
      })
    },

login.js

submitLogin(formName){
        let that = this
        this.$refs[formName].validate((valid) => {
          if (valid) {
            this.loading = true
            this.$axios.post(that.$my.api + '/bms/login/login', that.form).then(res => { // 登錄
                if(res.data.code === 200){
                    localStorage.setItem("userInfo", JSON.stringify(res.data.data))//重要
                    that.setLoginInfo(res.data.data)//很難受,一刷新就沒了,還要緩存輔助它
                    that.$my.loadRouter(res.data.data.roleArr ,that.$router) //重要
                    that.loading = false
                    that.$message({
                        message: res.data.message,
                        type: 'success',
                        duration: 1500
                    })
                    that.$router.push('/OnlineAudit')      
                }else{
                    that.loading = false
                    that.$message({
                        message: res.data.message,
                        type: 'error',
                        duration: 1500
                    })
                    return false
                }
            })
          } else {
            return false
          }
        })
    },
  • 動態左側菜單欄 回到菜單
    說明:以下頁面爲左側菜單欄
1. 涉及app.vue:
  兩種情況:一、用戶刷新,二、路由跳轉。

app.vue

<el-menu
 router
  ref='menu'
  :collapse="collapse"
  :default-active='$route.path'
  class="el-menu-vertical-demo"
  background-color="#002140"
  text-color="#fff"
  active-text-color="#ffd04b">

  <!-- <el-submenu index="1"> //這個動態側邊欄,我沒有考慮這種結構,二級路由我沒有做,有時間再說,喫飯了。
    <template slot="title">
      <i class="el-icon-cold-drink"></i>
      <span>米粒姐</span>
    </template>
      <el-menu-item index="/OnlineAudit">通話回推記錄</el-menu-item>
      <el-menu-item index="/ReceiptList">通話記錄</el-menu-item>
  </el-submenu>
   -->

  <el-menu-item :index="'/'+item.path" v-for="(item,i) in loginInfo.roleArr" :key='i'>
    <i :class="item.icon"></i>
    <span slot="title">{{item.name}}</span>
  </el-menu-item>
</el-menu>

mounted () { //處理用戶刷新,菜單欄顯示和選中問題
    let userInfo = localStorage.getItem('userInfo')?JSON.parse(localStorage.getItem('userInfo')):{name:'',roleArr:[]}
    let currentRoute = userInfo.roleArr.find( item => `/${item.path}` == this.$route.path )

    this.userInfo = userInfo   
    this.isShow = currentRoute?currentRoute.showLeftMenu:false

    setTimeout(() => {
        this.$refs.menu.defaultActive = localStorage.getItem('index')
      }, 100)
  },
  watch: {//處理路由跳轉,菜單欄顯示和選中問題
    $route() {
      let i = this.$route.path;
      let userInfo = localStorage.getItem('userInfo')?JSON.parse(localStorage.getItem('userInfo')):{name:'',roleArr:[]}
      let currentRoute = userInfo.roleArr.find( item => `/${item.path}`== this.$route.path )

      localStorage.setItem('index',i)
      this.userInfo = userInfo
      this.isShow = currentRoute?currentRoute.showLeftMenu:false

      setTimeout(() => {
        this.$refs.menu.activeIndex = i
      }, 100)
    }
  }
二、基礎頁
  • 頂部搜索 / 列表 / 底部分頁 回到菜單
    說明:以領藥機構頁爲例,這是一個典型的後臺頁面,頂部搜索 / 下載 / 新增 / 編輯 / 列表 / 詳情 / 底部分頁,刷新保留當前頁,搜索自動第一頁(太長了,此處記錄爲了方便我日後copy用的,clone項目慢慢看代碼吧)。
1. 頂部搜索:
  頂部搜索完成,自動到第一頁,展示結果。
2. 列表 :
  顯示數據的table,搜索+分頁控制數據。
3. 底部分頁:
  根據用戶選擇的頁數行數,展示結果。
<template>
<!-- 領藥機構列表 -->
  <div class="DrugPoint" >
    <el-row :gutter="12" class="mt10 mlr0">
      <el-col :span="24">
        <el-card shadow="always" class='ml20 mr20' v-if="showTab">
          <el-form :inline="true" :model="formInline" label-width="100px" class="demo-form-inline mt20">
            <el-form-item label="省份">
              <el-select v-model="formInline.provinceCode" @change="provincesOnChange($event,true)" size="small" clearable>
                <el-option :label="item.name" :value="item.code" v-for="(item,i) in provincesArr" :key='i'></el-option>
              </el-select>
            </el-form-item>
            <el-form-item label="城市">
              <el-select v-model="formInline.cityId" size="small" clearable>
                <el-option :label="item.name" :value="item.id" v-for="(item,i) in searchCityArr" :key='i'></el-option>
              </el-select>
            </el-form-item>
            <br/>
            <el-form-item label="機構名稱">
              <el-input v-model="formInline.orgName" size="small" clearable></el-input>
            </el-form-item>
            <el-form-item label="狀態">
              <el-select v-model="formInline.status" size="small" clearable>
                <el-option label="正常" :value="1"></el-option>
                <el-option label="停用" :value="2"></el-option>
                <el-option label="全部" :value="null"></el-option>
              </el-select>
            </el-form-item>

            <el-form-item>
              <el-button type="primary" @click="onSubmit" size="small">查詢</el-button>
            </el-form-item>
            <el-form-item>
              <el-button type="primary" @click="newOne" size="small">新增</el-button>
            </el-form-item>
            <!-- <el-form-item>
              <el-button type="primary" @click="downExcel" size="small">導出</el-button>
            </el-form-item> -->
          </el-form>
           <el-table
            v-loading="loading"
            size='small'
            :data="tableData.rows"
            style="width: 100%">
            <el-table-column prop="provinceName" label="省份"></el-table-column>
            <el-table-column prop="cityName" label="城市"></el-table-column>
            <el-table-column prop="orgName" label="機構名稱"></el-table-column>
            <el-table-column prop="address" width="150" show-overflow-tooltip label="機構地址" ></el-table-column>
            <el-table-column prop="orgMobile" label="機構電話"></el-table-column>
            <el-table-column prop="manager" label="負責人"></el-table-column>
            <el-table-column prop="managerMobile" label="負責人電話"></el-table-column>
            <el-table-column prop="quanty" label="庫存"></el-table-column>
            <el-table-column prop="status" label="狀態" :formatter='setStatus'></el-table-column>
            <el-table-column label="操作" width="180">
              <template slot-scope="scope">
                <el-button
                  @click.native.prevent="editOne(scope.$index, tableData)"
                  type="text"
                  size="small">
                  編輯
                </el-button>
                <el-button
                  @click.native.prevent="changeStatus(scope.$index, tableData)"
                  type="text"
                  size="small">
                  {{scope.row.status == 1?'禁用':'啓用'}}
                </el-button>
                <el-button
                  @click.native.prevent="insto(scope.$index, tableData)"
                  type="text"
                  size="small">
                  入庫
                </el-button>
                <el-button
                  @click.native.prevent="detailRow(scope.$index, tableData)"
                  type="text"
                  size="small">
                  查看
                </el-button>
              </template>
            </el-table-column>
          </el-table>
          <el-pagination
            @size-change="handleSizeChange"
            @current-change="handleCurrentChange"
            :current-page.sync="currentPage4"
            :page-sizes="[10,20,100]"
            :page-size="10"
            layout="total, sizes, prev, pager, next, jumper"
            :total="tableData.total"
            class="mt20 tr"
            >
          </el-pagination>
        </el-card>
        <!-- 詳情 -->
        <el-card shadow="always" class='ml20 mr20 mb70' v-else>
          <div class="fz14">
            <el-divider content-position="left">基礎信息</el-divider>
            <el-row class="ml20 lh40">
              <el-col :span="12"><span class="fw w100px">機構名稱:</span><span>{{detailData?detailData.orgName:''}}</span></el-col>
              <el-col :span="12"><span class="fw w100px">省市:</span><span>{{detailData?detailData.provinceName+detailData.cityName:''}}</span></el-col>
            </el-row>
            <el-row class="ml20 lh30">
              <el-col :span="12"><span class="fw w100px">地址:</span><span>{{detailData?detailData.address:''}}</span></el-col>
              <el-col :span="12"><span class="fw w100px">藥店電話:</span><span>{{detailData?detailData.orgMobile:''}}</span></el-col>
            </el-row>
            <el-row class="ml20 lh30">
              <el-col :span="12"><span class="fw w100px">負責人:</span><span>{{detailData?detailData.manager:''}}</span></el-col>
              <el-col :span="12"><span class="fw w100px">負責人手機:</span><span>{{detailData?detailData.managerMobile:''}}</span></el-col>
            </el-row>
            <el-row class="ml20 lh30 mb20">
              <el-col :span="12"><span class="fw w100px">機構狀態:</span><span>{{detailData?(detailData.status==1?'啓用':'禁用'):''}}</span></el-col>
            </el-row>

            <el-table
            v-loading="loading"
            size='small'
            :data="detailDataList.rows"
            style="width: 100%">
            <el-table-column prop="name" label="援助藥品" :formatter='getName'></el-table-column>
            <el-table-column prop="spec" label="規格" :formatter='setSpec'></el-table-column>
            <el-table-column prop="type" label="進出類型" :formatter='setType'></el-table-column>>
            <el-table-column prop="qty" label="進出量"></el-table-column>        
            <el-table-column prop="createTime" label="進出時間" :formatter='setTime'></el-table-column>
            <el-table-column prop="status" label="當前狀態" :formatter='instoStatus'></el-table-column>
          </el-table>
          <el-pagination
            @size-change="handleSizeChange2"
            @current-change="handleCurrentChange2"
            :current-page.sync="currentPage2"
            :page-sizes="[10,20,100]"
            :page-size="10"
            layout="total, sizes, prev, pager, next, jumper"
            :total="detailDataList.total"
            class="mt20 tr"
            >
          </el-pagination>

 
            <el-footer class="posFix bottom0 left0 bgf foot flex alic juste">
              <el-button type="primary" size="small" @click="showTab = true" class="ml20 mr20">返回</el-button>
            </el-footer>
          </div>
        </el-card>
      </el-col>
    </el-row>

    <!-- 入庫 start-->
    <el-dialog
      title="入庫"
      :visible.sync="dialogVisible"
      width="40%"
       v-loading='loading'
      >
      <el-form :model="instoForm" status-icon :rules="rules" ref="instoForm" label-width="100px" class="demo-ruleForm">
        <el-form-item label="規格" prop="spec" size="small">
          <el-radio-group v-model="instoForm.spec">
            <el-radio :label="1">12.5MG</el-radio>
            <el-radio :label="2">25MG</el-radio>
          </el-radio-group>
        </el-form-item>

        <el-form-item label="入庫" prop="qty" size="small">
          <el-input-number v-model="instoForm.qty" placeholder='數量' clearable></el-input-number>
        </el-form-item>
        
        <el-form-item class="tr">
          <el-button @click="dialogVisible = false" size="small">取 消</el-button>
          <el-button type="primary" @click="submitInstoForm('instoForm')"  size="small">提交</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
     <!--出庫 end-->

     <!-- 新增 start-->
    <el-dialog
      :title="newForm.id?'編輯機構':'新增機構'"
      :visible.sync="dialogNewVisible"
      width="60%"
       v-loading='loading'
      >
      <el-form :model="newForm" status-icon :rules="newRules" ref="newForm" label-width="100px" class="demo-ruleForm">
        
        <el-Row>
          <el-Col :span='12'>
            <el-form-item label="省份" prop="provinceCode" size="small">
              <el-select v-model="newForm.provinceCode" @change="provincesOnChange">
                <el-option :label="item.name" :value="item.code" v-for="(item,i) in provincesArr" :key='i'></el-option>
              </el-select>
            </el-form-item>
          </el-Col>
          <el-Col :span='12'>
            <el-form-item label="城市" prop="cityId" size="small">
              <el-select v-model="newForm.cityId" @change='cityOnChange'>
                <el-option :label="item.name" :value="item.id" v-for="(item,i) in cityArr" :key='i'></el-option>
              </el-select>
            </el-form-item>
          </el-Col>
          <el-Col :span='24'>
            <el-form-item label="機構電話" prop="orgMobile" size="small">
              <el-input v-model.number="newForm.orgMobile" style="width:208px" clearable></el-input>
            </el-form-item>
          </el-Col>
          <el-Col :span='24'>
            <el-form-item label="機構名稱" prop="orgName" size="small">
              <el-input v-model="newForm.orgName"  style="width:500px" clearable></el-input>
            </el-form-item>
          </el-Col>
          <el-Col :span='24'>
            <el-form-item label="機構地址" prop="address" size="small">
              <el-input v-model="newForm.address" style="width:500px" clearable></el-input>
            </el-form-item>
          </el-Col>
          <el-Col :span='12'>
            <el-form-item label="負責人" prop="manager" size="small">
              <el-input v-model="newForm.manager" clearable></el-input>
            </el-form-item>
          </el-Col>
          <el-Col :span='12'>
            <el-form-item label="負責人電話" prop="managerMobile" size="small">
              <el-input v-model.number="newForm.managerMobile" clearable></el-input>
            </el-form-item>
          </el-Col>
        </el-Row>

        <el-form-item class="tr">
          <el-button @click="dialogNewVisible = false" size="small">取 消</el-button>
          <el-button type="primary" @click="submitNewForm('newForm')" size="small">提交</el-button>
        </el-form-item>
      </el-form>
    </el-dialog>
     <!--新增 end-->
  </div>
</template>

<script>
import Vue from 'vue'
import { 
  Form,FormItem,Row,Col,Button ,Loading ,
  Pagination,Table,TableColumn,Select,Option,Radio,RadioGroup,
  Card,Input ,DatePicker ,Footer,Divider,InputNumber, Dialog
  } from 'element-ui';

const arr = [
  Form,FormItem,Row,Col,Button ,Pagination,
  Table,TableColumn,Select,Option,Card,Radio,RadioGroup,
  Input,DatePicker ,Footer,Divider,InputNumber,Dialog
  ] 
arr.map((e)=>{  //動態生成全局組件
   //Vue.use(e);
   Vue.component(e.name, e)
})
Vue.use(Loading.directive);


var checkTel = (rule, value, callback) => {
    if (!value) {
      return callback(new Error('電話不能爲空'));
    }
    setTimeout(() => {
      if (!Number.isInteger(value)) {
        callback(new Error('請輸入數字值'));
      } else {
        callback();
      }
    }, 1000);
  };
 var checkPhone = (rule, value, callback) => {
    if (!value) {
      return callback(new Error('手機不能爲空'));
    }
    let regIdPhone = /^1\d{10}$/; 
    setTimeout(()=>{
      if(!regIdPhone.test(value)){
        callback(new Error('手機格式錯誤'));
      }else{
        callback()
      }
    },1000)
  };
export default {
  name: 'DrugPoint',
  data() {
    return {
      loading:false,
      showTab:true,
      selectRows:'',
      tableData: {
          rows:[],
          total:0
          },
      currentPage4: 1,
      formInline: {
        provinceCode:'',
        cityId:'',
        provinceId:'',
        orgName:'',
        status:'',
      },
      editId:'',
      detailData:{},//詳情
      detailDataList:{
        rows:[],
        total:0
      },
      detailSelectRows:'',
      detailCurrentPage4: 1,
      currentPage2: 1,

      dialogVisible:false,//入庫
      instoForm:{
        spec:'',
        qty:'',
        orgId:'',//機構id
        orgName:'',
      },
      rules: {
        spec: [{ required: true, message: '請選擇', trigger: 'change'}],
        qty: [{ required: true, message: '請輸入', trigger: 'blur'}],
      },
      
      dialogNewVisible:false,//新增
      newForm:{
        provinceId:'',
        provinceName:'',
        provinceCode:'',
        cityId:'',
        cityName:'',
        id:'',//機構id
        orgName:'',
        orgMobile:'',
        address:'',
        manager:'',
        managerMobile:'',

      },
      newRules: {
        provinceCode: [{ required: true, message: '請選擇', trigger: 'change'}],
        cityId: [{ required: true, message: '請選擇', trigger: 'change'}],
        orgName:[{ required: true, message: '請輸入', trigger: 'blur'}],
        orgMobile:[{ required: true,validator:checkTel, trigger: 'blur'}],
        address:[{ required: true, message: '請輸入', trigger: 'blur'}],
        manager:[{ required: true, message: '請輸入', trigger: 'blur'}],
        managerMobile:[{ required: true,validator:checkPhone, trigger: 'blur'}]
      },
      provincesArr:[],
      cityArr:[],//編輯城市
      searchCityArr:[]//搜索城市
    }
  },
  created () {
   this.getList({...this.formInline})
   this.getPro()
  },
  methods: {
    handleSizeChange(val) {
      this.selectRows = val //用戶控制rows
      this.getList({...this.formInline}) //查詢
    },
    handleCurrentChange(val) {
      this.getList({...this.formInline ,page:val}) //用戶選擇頁數
    },
    handleSizeChange2(val) {
      this.detailSelectRows = val 
      this.getDetailInfoList() 
    },
    handleCurrentChange2(val) {
      this.getDetailInfoList({page:val}) //用戶選擇頁數
    },
    changeStatus(index,data){//是否禁用
      let that = this
      this.$axios.get(that.$my.api + '/bms/org/changeStatus?id='+data.rows[index].id,{headers:{token:JSON.parse(localStorage.getItem('userInfo')).token}}).then(res => { 
            if(res.data&&res.data.code === 200){    
              that.$message({
                    message: res.data.message,
                    type: 'success',
                    duration: 1500
                })
              that.getList({...this.formInline})
            }else{
                that.$message({
                    message: res.data.message,
                    type: 'error',
                    duration: 1500
                })
                return false
            } 
        }).catch(function (error) {})
    },
    insto(index,data){ //入庫
      this.dialogVisible = true
      this.instoForm.orgId = data.rows[index].id
      this.instoForm.orgName = data.rows[index].orgName
    },
    submitInstoForm(formName){ //入庫提交
      let that = this
      let userInfo = JSON.parse(localStorage.getItem('userInfo'))
      if(this.instoForm.qty == 0){
        that.$message({
            message: '入庫不能爲零',
            type: 'warning',
            duration: 1500
        })
        return false
      }
      this.$refs[formName].validate((valid) => {
        if (valid) {
          let data ={
            ...this.instoForm,
            employeeId:userInfo.id,
            employeeName:userInfo.loginName
          }
          this.$axios.post(that.$my.api + '/bms/org/insto', data,{headers:{token:userInfo.token}}).then(res => { 
              if(res.data&&res.data.code === 200){       
                  that.loading = false;
                  that.dialogVisible = false
                  that.$message({
                      message: res.data.message,
                      type: 'success',
                      duration: 1500
                  })
                  that.getList({...that.formInline})
                  that.$refs[formName].resetFields();
              }else{
                  that.loading = false
                  that.dialogVisible = false
                  that.$message({
                      message: res.data.message,
                      type: 'error',
                      duration: 1500
                  })
                  return false
              } 
          }).catch(function (error) {
            that.loading = false
          })
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    provincesOnChange(values ,isSearch) {//省市
      this.provincesArr.map((item)=>{
        if(item.code == values){
          if(isSearch){ //搜索省市
            this.formInline.provinceId = item.id
            this.formInline.provinceName = item.name
            this.formInline.cityId = ''
          }else{//編輯省市
            this.newForm.provinceId = item.id
            this.newForm.provinceName = item.name
            this.newForm.cityId = ''
          }
        }
      })          
      //根據code請求數據
      this.getCityList(values ,isSearch) //根據省code獲取城市
    },
    cityOnChange(values) {//省市
      this.cityArr.map((item)=>{
        if(item.id == values){
          this.newForm.cityName = item.name
        }
      })          
    },
    getPro(){
      let that = this
      this.$axios.get(this.$my.api+'/wet/base/getProList')
        .then(res => {
          if(res.data&&res.data.code == 200){
            this.provincesArr= res.data.data
            that.getCityList(res.data.data[0].code)
          }else{
            that.$message({
                message: res.data.message,
                type: 'error',
                duration: 1500
            })
          }
        }, response => {
            console.log("error");
        })
    },
    getCityList(code,isSearch){
      let that = this
      this.$axios.get(this.$my.api+'/wet/base/getCityList?code='+code)
        .then(res => {
          if(res.data&&res.data.code == 200){
            if(isSearch){
              this.searchCityArr = res.data.data
            }else{
              this.cityArr= res.data.data
            }
          }else{
            that.$message({
                message: res.data.message,
                type: 'error',
                duration: 1500
            })
          }
        }, response => {
            console.log("error");
        });
    },
    newOne(){//新增機構
      this.dialogNewVisible = true
      this.newForm.id = '',
      this.$nextTick(()=>{
        this.$refs['newForm'].resetFields();
      })
    },
    editOne(index,data){//編輯機構
      this.dialogNewVisible = true
      this.newForm.id = data.rows[index].id
      this.getDetail(data.rows[index].id).then((res)=>{       
        let province = this.provincesArr.find((item)=>item.id == res.provinceId)   
        this.getCityList(province.code)
        this.$nextTick(()=>{
          this.newForm = {
            provinceId:res.provinceId,
            provinceName:res.provinceName,
            provinceCode:province.code,
            cityId:res.cityId,
            cityName:res.cityName,
            id:res.id,//機構id
            orgName:res.orgName,
            orgMobile:parseInt(res.orgMobile),
            address:res.address,
            manager:res.manager,
            managerMobile:res.managerMobile,
          }
        })
        
      })
    },
    submitNewForm(formName){ //新增提交
      let that = this
      let userInfo = JSON.parse(localStorage.getItem('userInfo'))
      this.$refs[formName].validate((valid) => {
        if (valid) {
          let data ={
            ...this.newForm,
            employeeId:userInfo.id,
            employeeName:userInfo.loginName
          }
          this.$axios.post(that.$my.api + '/bms/org/savaOrModify', data,{headers:{token:userInfo.token}}).then(res => { 
              if(res.data&&res.data.code === 200){       
                  that.loading = false;
                  that.dialogNewVisible = false
                  that.$message({
                      message: res.data.message,
                      type: 'success',
                      duration: 1500
                  })
                  that.getList({...that.formInline})
                  that.$refs[formName].resetFields();
              }else{
                  that.loading = false
                  that.dialogNewVisible = false
                  that.$message({
                      message: res.data.message,
                      type: 'error',
                      duration: 1500
                  })
                  return false
              } 
          }).catch(function (error) {
            that.loading = false
          })
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },
    detailRow(index,data){ //查看詳情
        this.editId = data.rows[index].id
        this.getDetail(data.rows[index].id).then(()=>{
          this.showTab = false
        })
        this.getDetailInfoList()
    },
    onSubmit() {
        console.log('submit!',this.formInline);
        this.currentPage4 = 1
        this.getList({...this.formInline}) //查詢
      },
    getDetail(id){//機構詳情
      return new Promise((rel,err)=>{
        this.$axios.get(this.$my.api + '/bms/org/getInfo?id='+id,{headers:{token:JSON.parse(localStorage.getItem('userInfo')).token}}).then(res => { 
            if(res.data&&res.data.code === 200){    
              this.detailData = res.data.data
              rel(res.data.data)
            }else{
                this.$message({
                    message: res.data.message,
                    type: 'error',
                    duration: 1500
                })
                return false
            } 
        }).catch(function (error) {})
      })
      
    },
    getDetailInfoList(val={}){    //患者贈藥列表
        let that = this
        this.loading = true
        let data = {
              id:that.editId,
              page:1,
              rows:that.detailSelectRows?that.detailSelectRows:that.$my.rows,
              ...val
            }
        this.$axios.post(that.$my.api + '/bms/org/getOrgInsto', data,{headers:{token:JSON.parse(localStorage.getItem('userInfo')).token}}).then(res => { 
            if(res.data&&res.data.code === 200){       
                that.loading = false;
                that.detailDataList = res.data.data
            }else{
                that.loading = false
                that.$message({
                    message: res.data.message,
                    type: 'error',
                    duration: 1500
                })
                return false
            } 
        }).catch(function (error) {
          that.loading = false
        })
      },
    getList(val={}){    
        let that = this
        this.loading = true
        let data = {
              page:1,
              rows:that.selectRows?that.selectRows:that.$my.rows,
              flag:'online',
              giveStatus:0,
              ...val
            }
            data.number = data.number?data.number.join(','):''
        this.$axios.post(that.$my.api + '/bms/org/getList', data,{headers:{token:JSON.parse(localStorage.getItem('userInfo')).token}}).then(res => { 
            if(res.data&&res.data.code === 200){       
                that.loading = false;
                that.tableData = res.data.data
            }else{
                that.loading = false
                that.$message({
                    message: res.data.message,
                    type: 'error',
                    duration: 1500
                })
                return false
            } 
        }).catch(function (error) {
          that.loading = false
        })
      },
      downExcel(){ //準備把下載寫成公共函數
        let that = this
        let oReq = new XMLHttpRequest();        
            oReq.open("POST", that.$my.api+'callBack/exportRecordData', true);        
            oReq.responseType = "blob";       
             oReq.setRequestHeader("Content-Type","application/json");        
             oReq.onload = function (oEvent) {            
              var content = oReq.response;            
              var elink = document.createElement('a');            
              elink.download = '回推excel.xls';    
              elink.style.display = 'none';               
               var blob = new Blob([content], { type: 'application/vnd.ms-excel'}) 
               elink.href = URL.createObjectURL(blob);            
               document.body.appendChild(elink);            
               elink.click();            
               document.body.removeChild(elink);        
        };       
              oReq.send(JSON.stringify({...this.formInline}));
      },
      setSpec(row, column, cellValue, index){
        return cellValue||cellValue==0?this.$my.spec[cellValue]:''
      },
      setStatus(row, column, cellValue, index){
        return cellValue == 1?'啓用':'禁用'
      },
      setTime(row, column, cellValue, index){
        return cellValue?this.$my.timestampToTime(cellValue,'YMDHMS'):''
      },
      setType(row, column, cellValue, index){
        return cellValue ? this.$my.insto[cellValue]:''
      },
      instoStatus(row, column, cellValue, index){
        let arr = ['','待審覈','通過','駁回','自動審覈']
        return cellValue?arr[cellValue]:''
      },
      getName(){
        return '阿莫西林'
      }
  },
}
</script>

<style scoped>
.foot{margin-left: 200px;width:calc(100% - 200px)}
.w100px{width:100px;display: inline-block;}
.mb70{margin-bottom:70px}
.mlr0{margin-left:-4px !important;margin-right: 0 !important}
</style>
三、動態表單及其驗證
  • 多個input循環加載 回到菜單
    說明:多個輸入框爲整體進行循環,如下圖所示,切記!!!動態表單不要用v-if會出現表單不驗證,因爲rules在頁面加載完執行,v-if中的表單綁定不上。
    在這裏插入圖片描述

html

<el-divider content-position="left">購藥發票</el-divider>
<el-row class="ml20 lh30">
  <el-form-item
    label='審覈'
    prop="invoiceAud"
    size="small"
    :rules="[{ required: true, message: '請選擇'}]"
  >
    <el-select v-model="audForm.invoiceAud" placeholder="請選擇" :disabled='detailData.visit&&detailData.visit.status != 11'>
      <el-option label="通過" :value="1"></el-option>
      <el-option label="不通過" :value="2"></el-option>
    </el-select>
  </el-form-item>
  <el-form-item
    v-if="audForm.invoiceAud == 2"
    label='原因'
    prop="invoiceRemarks"
    size="small"
    :rules="[{ required: true, message: '請填寫'}]"
  >
    <el-input v-model="audForm.invoiceRemarks" style="width:500px" clearable :disabled='detailData.visit&&detailData.visit.status != 11'></el-input>
  </el-form-item>
  <div v-for="(item,index) in audForm.invoiceList" :key='index' class="flex alic">
    <el-form-item>
      <viewer :images='[item.url]'><img :src='item.url' width="50" height="50"/></viewer>
    </el-form-item>
    <el-form-item
      :prop="'invoiceList.' + index + '.invoiceDate'"
      :rules="{
        required: true, message: '日期不能爲空', trigger: 'blur'
      }"
    >
      <el-date-picker
        v-model="item.invoiceDate"
        type="date"
        value-format='yyyy-MM-dd'
        size="small"
        :disabled='detailData.visit&&detailData.visit.status != 11'
        placeholder="選擇日期">
      </el-date-picker>
    </el-form-item>
    <el-form-item
      :prop="'invoiceList.' + index + '.invoiceQty'"
      :rules="{
        required: true, message: '購藥支數不能爲空', trigger: 'blur'
      }"
    >
      <el-input-number v-model="item.invoiceQty" :min='1' placeholder='購藥支數' size="small" :disabled='detailData.visit&&detailData.visit.status != 11'></el-input-number>
    </el-form-item>
    <el-form-item
      :prop="'invoiceList.' + index + '.invoiceNo'"
      :rules="{
        required: true, message: '發票號不能爲空', trigger: 'blur'
      }"
    >
      <el-input v-model="item.invoiceNo" placeholder='發票號' size="small" :disabled='detailData.visit&&detailData.visit.status != 11'></el-input>
    </el-form-item>
    <el-form-item
      :prop="'invoiceList.' + index + '.source'"
      :rules="{
        required: true, message: '來源不能爲空', trigger: 'blur'
      }"
    >
      <el-select v-model="item.source" placeholder="請選擇" size="small" :disabled='detailData.visit&&detailData.visit.status != 11'>
        <el-option label="藥店" :value="1"></el-option>
        <el-option label="門診" :value="2"></el-option>
        <el-option label="醫院" :value="3"></el-option>
      </el-select>
    </el-form-item>
  </div>
</el-row>

data

audForm:{//審覈表單
        invoiceList:[],//{invoiceDate:'',invoiceQty:'',invoiceNo:'',source:''}
        invoiceAud:'',
        invoiceRemarks:''
      },

methods

 getDetail(id){
      let that = this
      this.$axios.get(that.$my.api + '/bms/visit/getDetail?id='+id,{headers:{token:JSON.parse(localStorage.getItem('userInfo')).token}}).then(res => { 
            if(res.data&&res.data.code === 200){    
              let data =  res.data.data  
              let invoice = []

              data.invoiceList.map((item)=>{ //發票,循環數據源
                invoice.push({
	                invoiceDate:that.$my.timestampToTime(item.invoiceDate,'YMD'),
	                invoiceQty:item.invoiceQty,
	                invoiceNo:item.invoiceNo,
	                source:item.source,url:item.invoiceUrl,id:item.id})
	              })            
             
              that.audForm = {
                invoiceList:invoice,//發票,循環數據源
                invoiceAud:data.visit.invoiceAud?data.visit.invoiceAud:"",
                invoiceRemarks:data.visit.invoiceRemarks,
              }
            }else{

                that.$message({
                    message: res.data.msg,
                    type: 'error',
                    duration: 1500
                })
                return false
            } 
        }).catch(function (error) {})
    },
  • 單個input循環加載 回到菜單
    說明:單個輸入框進行循環,如下圖所示,切記!!!動態表單不要用v-if會出現表單不驗證,因爲rules在頁面加載完執行,v-if中的表單綁定不上。

官網有我就不寫了:動態增減

區別:單個與多個的區別在於,用一個標籤包裹;單個直接循環el-form-item標籤,多個循環div,div中可以寫多個el-form-item。

四、圖片預覽

安裝

npm install v-viewer //我用yarn add 不支持

配置-main.js

import Viewer from 'v-viewer'//圖片查看器
import 'viewerjs/dist/viewer.css'
Vue.use(Viewer, {
  defaultOptions: {
      zIndex: 9999
  }
})

使用

//正式使用
<viewer :images='detailData.visit?detailData.visit.prescriptionUrl:[]' class='flex'>
  <img :src='item' v-for="(item,i) in detailData.visit?detailData.visit.prescriptionUrl:[]" :key='i' class='ml20' width="50" height="50"/>
</viewer>

//基礎結構
<viewer :images='arrry'>
	<img :src='url' />
	<img :src='url' />
	<img :src='url' />
</viewer>
五、導出
  • 使用方法 回到菜單
    說明:打算封裝一下導出,因爲使用頻率有點高。
1. 未封裝版:
html
<el-form-item>
 <el-button type="primary" @click="downExcel" size="small">導出</el-button>
</el-form-item>

methods

downExcel(){
        let that = this
        let oReq = new XMLHttpRequest();        
            oReq.open("POST", that.$my.api+'callBack/exportRecordData', true);        
            oReq.responseType = "blob";       
             oReq.setRequestHeader("Content-Type","application/json");        
             oReq.onload = function (oEvent) {            
              var content = oReq.response;            
              var elink = document.createElement('a');            
              elink.download = '回推excel.xls';    
              elink.style.display = 'none';               
               var blob = new Blob([content], { type: 'application/vnd.ms-excel'}) 
               elink.href = URL.createObjectURL(blob);            
               document.body.appendChild(elink);            
               elink.click();            
               document.body.removeChild(elink);        
        };       
              oReq.send(JSON.stringify({...this.formInline}));//參數{name:'',sex:''}
      },
2. 封裝版:

method //其他同上

downExcel(){
	this.$my.downExcel('/down',{name:''},'數據','POST')
},

assets/js/common.js

const downExcel=(url='',data={},name='excel',method='POST')=>{ //導出
  let oReq = new XMLHttpRequest();        
      oReq.open(method, api+'/'+url, true);        
      oReq.responseType = "blob";   
      oReq.withCredentials = true;    
       oReq.setRequestHeader("Content-Type","application/json");        
       oReq.onload = function (oEvent) {            
        var content = oReq.response;            
        var elink = document.createElement('a');            
        elink.download = name+'.xls';    
        elink.style.display = 'none';               
         var blob = new Blob([content], { type: 'application/vnd.ms-excel'}) 
         elink.href = URL.createObjectURL(blob);            
         document.body.appendChild(elink);            
         elink.click();            
         document.body.removeChild(elink);        
  };       
        oReq.send(JSON.stringify({...data}));
}

遊戲篇:我的喫雞玩的太菜了,打不住人。鑽石局都這麼強嗎,花裏胡哨的,所以我撿了7手雷,三個燃燒瓶,讓你跳,地雷戰怕不怕,炸死你???
備註:easy-mock數據經常爆炸,登錄都進不去,免費的好難呀;2019/12/13easy-mock服務恢復,我趁機換了它,改爲fast-mock了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章