我的vue学习日记(二)——后台管理系统(vue+vuex+vue-cli+element+axios+router)

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

效果如图:
PC
我的vue学习日记(二)——后台管理系统(vue+vuex+vue-cli+element+axios+router)_第1张图片
账号:admin 密码:123456
我的vue学习日记(二)——后台管理系统(vue+vuex+vue-cli+element+axios+router)_第2张图片
MOBILE
我的vue学习日记(二)——后台管理系统(vue+vuex+vue-cli+element+axios+router)_第3张图片
我的vue学习日记(二)——后台管理系统(vue+vuex+vue-cli+element+axios+router)_第4张图片

介绍:

一、权限系统
  • 动态路由
  • 动态左侧菜单栏
二、基础页
  • 顶部搜索 / 列表 / 底部分页
三、动态表单及其验证
  • 多个input循环加载
  • 单个input循环加载
四、图片预览
  • 使用方法
五、导出
  • 使用方法

一、权限系统
  • 动态路由 回到菜单
    备注:
      起初,我想使用跟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中的表单绑定不上。
    我的vue学习日记(二)——后台管理系统(vue+vuex+vue-cli+element+axios+router)_第5张图片

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。

四、图片预览
  • 使用方法 回到菜单
    说明:使用的 viewer 预览。

安装

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了

你可能感兴趣的:(Element)