1. Vue-admin-template

1. 简介

vueAdmin-template是基于vue-element-admin的一套后台管理系统基础模板(最少精简版),可作为模板进行二次开发。

GitHub地址https://github.com/PanJiaChen/vue-admin-template

根据用户角色来动态生成侧边栏的分支https://github.com/PanJiaChen/vue-admin-template/tree/permission-control

2. 安装和运行

# 首先使用git下载源码
git clone https://github.com/PanJiaChen/vue-admin-template.git
# 将文件夹修改成我们自己的项目名称 如 vue-demo 
# 使用git窗口修改,强化linux命令
mv vue-admin-template vue-demo
# 使用cmd或者IDE打开文件夹  如 vscode webstorm
# 下载安装项目所需的依赖 此步需要有node
npm install
#运行
npm run dev
#前端打包
npm run build:prod

2. 前端相关配置

1. 禁用EsLint

vue.config.js第30行禁用ESLint语法检查

lintOnSave: false,

2. 修改页面标题

src/setting.js第三行处修改页面标题

module.exports = {
    
    title: 'vue测试系统',
}

3. 国际化设置

src/main.js 第7行处修改语言

import locale from 'element-ui/lib/locale/lang/zh-CN' //lang i18n

4. 下拉菜单修改

修改菜单

5. 登录页修改

src/views/login/index.vue 修改登录页面标题和登录按钮等

3. 定义api

src/api下创建文件夹report

1. 导出整个对象

此方法只导出一个default对象,使用的时候可以将一阵个文件导入成为一个对象来使用。

report文件夹下创建reportApi.js

import request from '@/utils/request'

export default {
  /**
   * 分页查询
   * @param data  查询参数
   * @returns {AxiosPromise}
   */
  fetchReportList(data) {
    return request({
      url: `/manager/report/page`,
      method: 'get',
      params: data
    })
  },
  /**
   * 根据id获取数据
   * @param data    要查询的id
   * @returns {AxiosPromise}
   */
  getReportById(data) {
    return request({
      url: `/manager/report/${data}`,
      method: 'get'
    })
  },
  /**
   * 新增或更新方法, 后端判断,如果有id则更新没有id则创建,
   * 也可以直接使用MybatisPlus的saveOrUpdate方法简化操作
   * @param data        表单数据
   * @returns {AxiosPromise}
   */
  saveOrUpdateReport(data) {
    return request({
      url: '/manager/report/saveOrUpdate',
      method: 'post',
      data
    })
  },
  /**
   * 根据id删除数据,逻辑删除
   * 实体类删除属性上添加 @TableLogic即可标记 使用MybatisPlus的removeById即可
   * @param data
   * @returns {AxiosPromise}
   */
  removeReportById(data) {
    return request({
      url: `/manager/report/${data}`,
      method: 'delete'
    })
  },

  getReportErrorPage() {
    return request({
      url: '/manager/report/errorPage',
      method: 'get'
    })
  },
  /**
   * 文件上传 批量
   * @param url     要上传的文件地址
   * @param file    文件数组
   * @returns {AxiosPromise}
   */
  upload(url, file) {
    // 创建 FormData对象
    let data = new FormData()
    // 将文件放置到data里,files对应后端MultipartFile 的变量名
    file.forEach(everyFile => {
      data.append("files", everyFile.raw)
    })
    return request({
      //这里可以直接写url,
      url: url,
      method: 'post',
      data
    })
  }
}

使用的时候直接导出整个对象

<template></template>
<script>
  import reportApi from "@/api/report/reportApi";
  export default {
    name: 'reportList',
    data(){
      return{
        
      }
    },
    created() {
    },
    methods:{
      fetchList(data){
        reportApi.fetchReportList(data)
          .then(resp => {
    
          })
      }  
    }
    
  }
</script>

2. 单独导出每个方法

src/api/report下创建report.js

import request from '@/utils/request'

export function fetchReportList(data){
  return request({
    url: `/manager/report/page`,
    method: 'get',
    params:data
  })
}

export function getReportById(data){
  return request({
    url: `/manager/report/${data}`,
    method:'get'
  })
}

export function importExcel(data){
  return request({
    url: '/manager/report/importBatch',
    method:'post',
    data
  })
}

export function export2Excel(data){
  return request({
    url: '/manager/report/export',
    method:'get'
  })
}

export function saveOrUpdateReport(data){
  return request({
    url: '/manager/report/saveOrUpdate',
    method:'post',
    data
  })
}

export function removeReportById(data){
  return request({
    url : `/manager/report/${data}`,
    method:'delete'
  })
}

export function getReportErrorPage(){
  return request({
    url: '/manager/report/errorPage',
    method:'get'
  })
}

export function upload(url,file){
  let data = new FormData()
  file.forEach(everyFile => {
    data.append("files",everyFile.raw)
  })
  return request({
    url:url,
    method:'post',
    data
  })
}

使用的时候按需引入即可,不需要都引入

<srcipt>
import {fetchReportList,removeReportById,upload} from '@/api/report/report'
  export default {
    name: 'reportList',
    data(){
      return{
        reportList:[],
       
      }
    },
    created(){
      this.fetchList();
    },
    methods:{
      clearSearchForm(){
        this.searchForm = {}
        this.fetchList()
      },
      fetchList() {
        let data = {
          name:this.searchForm.name,
          phone:this.searchForm.phone,
          idCard:this.searchForm.idCard,
          testingResult:this.searchForm.testingResult,
          queryStartTime:this.searchForm.queryStartTime,
          queryEndTime:this.searchForm.queryEndTime,
          pageNum:this.pageNum,
          pageSize:this.pageSize
        }
        fetchReportList(data).then(resp => {
          this.reportList = resp.data.list
          this.total = resp.data.total
          this.pageSize = resp.data.pageSize
          this.pageNum = resp.data.pageNum
        })
	  }
    }
</srcipt>    

3. 总结

我自己是使用第二种方式的,可能也是受到了身边前端同时的影响,第一种可以有效的避免方法名重复我觉得哈哈哈🤤

4. 配置项目路由

src/router/index.js中的constatnRoutes修改

{
    path: '/report',
    component: Layout,
    redirect: '/report/list',
    name: 'report',
    meta:{title: '核酸检测管理',icon: 'el-icon-search'},
    alwaysShow:true,
    children: [
      {
        path: 'list',
        name: 'reportList',
        component: () => import('@/views/report/list'),
        meta: { title: '统计结果', icon: 'form' }
      },
      {
        path: 'save',
        name: 'saveReport',
        component: () => import('@/views/report/form'),
        meta: { title: '添加核酸检测数据', icon: 'form' }
      },
      {
        //相当于占位符,这个id可以用this.$route.params.id取出来  
        path: 'edit/:id',
        name: 'editReport',
        component: () => import('@/views/report/form'),
        meta: { title: '修改核酸检测结果', icon: 'form' },
        hidden:true
      },
    ]
  },
序号属性子属性描述
1path路由的路径
2name路由的名称,不能重复
3redirect重定向的地址
4component引用的组件
5meta组件的描述
6title组件的标题
7icon组件的标签
8alwaysShow是否永远展示,
false时 有且仅有一个子组件时不展示
9hidden是否隐藏
10children子组件

抽离出来的数据库实体类

create teble sys_menu(
	id int primary key auto_increment comment '',
    parent_id int comment '',
    path varchar(50) not null unique comment '',
    name varchar(50) not null unique comment '',
    redirect varchar(100) comment '',
    component varchar(50) comment '',
    title varchar(50) comment '',
    icon varchar(50) comment '',
    always_show bit(1) comment '',
    hidden bit(1) comment '',
    create_by varchar(50) comment '',
    create_time datetime default now() comment '',
    update_by varchar(50) comment '',
    update_time datetime comment '',
    del_flag tinyint default 0 comment '0未删除 1已删除'
)comment '系统菜单表';

5. 创建组件(使用两个vue页面)

src/views下创建文件夹report

1. 创建列表组件 list.vue

1. 基础的表格展示

report文件夹下创建list.vue,使用elementuiel-table组件,elementui提供了一个slot-scope可以用来将后端传过来的数据转换成我们自己想要的数据,scope.row就是用户当前在操作的那一行

<template>
  <div class="app-container">
    <!-- 表格 -->
    <el-table :data="reportList" border stripe>
      <el-table-column type="index" width="50" label="序号"/>
      <el-table-column prop="name" width="70" label="姓名"/>
      <el-table-column prop="phone" width="110" label="联系方式"/>
      <el-table-column prop="idType" width="80" label="证件类型">
        <template slot-scope="scope">
          <span v-if="scope.row.idType == '1'">身份证</span>
          <span v-if="scope.row.idType == '2'">护 照</span>
          <span v-if="scope.row.idType == '3'">其 他</span>
        </template>
      </el-table-column>
      <el-table-column prop="idCard" width="150" label="证件号"/>
      <el-table-column prop="address" label="受检者地址"/>
      <el-table-column prop="samplingTime" label="采样时间"/>
      <el-table-column prop="testingResult" label="核酸检测结果">
        <template slot-scope="scope">
          <span v-if="scope.row.testingResult == '1'">阳性</span>
          <span v-if="scope.row.testingResult == '2'">阴性</span>
          <span v-else>暂无结果</span>
        </template>
      </el-table-column>
      <el-table-column prop="testingInstitutionName" label="检测机构名称"/>
    </el-table>
  </div>

</template>
<script>
  import {fetchReportList} from '@/api/report/report'

  export default {
    data() {
      return {
        reportList: []
      }
    }
    created() {
      this.fetchList()
    },
    methods: {
      fetchList() {
        fetchReportList().then(resp => {
          this.reportList = resp.data
        })
      }
    }
  }
</script>

2. 创建表单组件form.vue

src/view/report下创建form.vue,此页面并没有添加表单校验,如有需要请参考表单验证

为了更好的控制每行的展示,可以使用栅格组件中的el-row来实现。一行 24列, 在el-col中使用:span控制占用多少。

在生命周期created函数中,判断当前路由中的id是不是空值,这个值可以在ver devtools中查看到。如果存在id的话,则调用查询的接口,将获取到的数据展示在表单中

<template>
  <div class="app-container"  >
    <el-form :model="report" label-width="260px" label-position="right" >
      <el-row>
        <el-col :span="12">
          <el-form-item label="姓名">
            <el-input v-model="report.name" placeholder="请填写姓名" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="联系方式">
            <el-input v-model="report.phone" placeholder="请填写联系方式" min="3" max="11"/>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="证件类型">
            <el-select v-model="report.idType" placeholder="请选择证件类型">
              <el-option label="身份证" value="1" />
              <el-option label="护照" value="2" />
              <el-option label="其他" value="3" />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="证件号">
            <el-input v-model="report.idCard" placeholder="请填写证件号" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="受检者地址">
            <el-input v-model="report.address" placeholder="请填写受检者地址" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="采样时间">
            <el-date-picker
              v-model="report.samplingTime"
              align="right"
              type="datetime"
              placeholder="请选择采样时间"
              :picker-options="pickerOptions">
            </el-date-picker>
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="检测结果">
            <el-select v-model="report.testingResult" placeholder="请选择核酸检测结果" >
              <el-option label="无" value="" />
              <el-option label="阳性" value="1" />
              <el-option label="阴性" value="2" />
            </el-select>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="检测机构">
            <el-input v-model="report.testingInstitutionName" placeholder="请填写核酸检测机构名称" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="样品管编号">
            <el-input v-model="report.tubeNo" placeholder="请填写样品管编号" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="包号">
            <el-input v-model="report.packageNo" placeholder="请填写包号" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="检测时间">
            <el-date-picker
              v-model="report.testingTime"
              align="right"
              type="datetime"
              placeholder="请选择检测时间"
              :picker-options="pickerOptions"
            >
            </el-date-picker>
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="采样机构">
            <el-input v-model="report.testingInstitution" placeholder="请填写采样机构" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-col :span="12">
          <el-form-item label="类别编码">
            <el-input v-model="report.type" placeholder="请填写类别编码" />
          </el-form-item>
        </el-col>
        <el-col :span="12">
          <el-form-item label="用人单位代码">
            <el-input v-model="report.succ" placeholder="请填写用人社会统一信用代码" />
          </el-form-item>
        </el-col>
      </el-row>
      <el-row>
        <el-form-item label="备注说明" style="width: 80%;">
          <el-input type="textarea" v-model="report.intro" placeholder="请填写备注说明" />
        </el-form-item>
      </el-row>
      <el-divider/>
      <el-row>
        <el-form-item>
          <el-button icon="el-icon-loading"
                     type="primary"
                     @click="saveOrUpdate"
          >{{loadingText}}</el-button>
        </el-form-item>
      </el-row>
    </el-form>
  </div>
</template>
<script>
  import {getReportById, saveOrUpdateReport} from "@/api/report/report";

  export default {
    data(){
      return{
        loadingText:'保存',
        report:{

        },
        pickerOptions: {
          disabledDate(time) {
            return time.getTime() > Date.now();
          },
          shortcuts: [{
            text: '今天',
            onClick(picker) {
              picker.$emit('pick', new Date());
            }
          }, {
            text: '昨天',
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() - 3600 * 1000 * 24);
              picker.$emit('pick', date);
            }
          }, {
            text: '一周前',
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
              picker.$emit('pick', date);
            }
          }]
        }
      }
    },
    created() {
      let id = this.$route.params.id
      if(id){
        this.getById(id)
      }
    },
    methods:{
      getById(id){
        getReportById(id).then(resp => {
          this.report = resp.data
        })
      },
      saveOrUpdate(){
        saveOrUpdateReport(this.report).then(resp => {
          this.$message.success(resp.message)
          this.$router.push('/report/list')
        })
      }
    }
  }
</script>

当然,也可以给最下方的保存按钮添加加载中这种特效

<template>
  <div class="app-container">
    <el-row>
      <el-form-item>
        <el-button :icon="loadingIcon"
                   type="primary"
                   @click="saveOrUpdate"
                   :loading="loading"
        >{{loadingText}}
        </el-button>
      </el-form-item>
    </el-row>
  </div>

</template>
<script>
  import {saveOrUpdateReport} from '@/api/report/report'

  export default {
    data() {
      return {
        loadingIcon: 'el-icon-success',
        loading: false,
        loadingText: '保存',
        form: {}
      }
    },
    methods: {
      saveOrUpdate() {
        this.changeLoadBtn('el-icon-loading', true, '保存中...')
        saveOrUpdateReport(this.form).then(resp => {
          this.$message.success(resp.messge)
          //保存成功跳转到列表页面
          this.$router.push('/report/list')
        }).catch(err => {
          this.changeLoadBtn('el-icon-error', false.
          '保存失败'
        )
        })
      },
      //变更加载按钮
      changeLoadBtn(loadingIcon, loading, loadingText) {
        this.loadingIcon = loadingIcon
        this.loading = loading
        this.loadingText = loadingText
      }
    }
  }
</script>

3. 列表页新增操作列

el-table最后添加一列操作列,用来编辑和删除

<template>
  <div class="app-container">
    <el-table>
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">

          <router-link :to="`/report/edit/${scope.row.id}`" style="margin-right: 5px">
            <el-button type="primary" size="mini">修改</el-button>
          </router-link>
          <el-button type="danger" @click="delReport(scope.row.id)"
                     size="mini">删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
  import {removeReportById} from '@/api/report/report'

  export default {
    data() {
      return {
        form: {}
      }
    },
    methods: {
      delReport(id) {
        removeReportById(id).then(resp => {
          this.$message.success(resp.message)
        })
      }
    }
  }
</script>

因为编辑按钮点击之后就跳转了form.vue,所以直接使用router-link会比较方便,应该相当于下边这种写法,但是我没验证过。

<template>
  <div class="app-container">
    <el-table>
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">
          <el-button type="primary" size="mini" @click="editReport(scope.row.id)">修改</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        form: {}
      }
    },
    methods: {
      editReport(id) {
        this.$router.push(`/report/edit/${id}`)
      }
    }
  }
</script>

4. 新增查询功能

1. 修改页面组件

在页面的最上方添加查询字段,在需要输入框添加@keyup.enter.native 可以使回车键松开的时候调用查询方法,添加@blur是的输入框失去焦点的时候调用查询方法,下拉选择狂可以使用@change来调用

<template>
  <div class="app-container">
    <el-form :inline="true">
      <el-form-item label="姓名">
        <el-input v-model="searchForm.name"
                  placeholder="姓名"
                  clearable @blur="fetchList"
                  @keyup.enter.native="fetchList"/>
      </el-form-item>
      <el-form-item label="手机号">
        <el-input v-model="searchForm.phone"
                  placeholder="手机号"
                  clearable @blur="fetchList"
                  @keyup.enter.native="fetchList"/>
      </el-form-item>
      <el-form-item label="证件号">
        <el-input v-model="searchForm.idCard" placeholder="证件号" clearable @blur="fetchList"
                  @keyup.enter.native="fetchList"/>
      </el-form-item>
      <el-form-item label="检测结果">
        <el-select v-model="searchForm.testingResult" placeholder="请选择" clearable @blur="fetchList"
                   @keyup.enter.native="fetchList">
          <el-option label="阳性" value="1"/>
          <el-option label="阴性" value="2"/>
          <el-option label="暂无结果" value=""/>
        </el-select>
      </el-form-item>
      <el-form-item label="采样时间">
        <el-date-picker
          @blur="fetchList" @keyup.enter.native="fetchList"
          v-model="searchForm.queryStartTime"
          type="date"
          clearable
          format="yyyy-MM-dd"
          value-format="yyyy-MM-dd"
        />
        -
        <el-date-picker
          @blur="fetchList" @keyup.enter.native="fetchList"
          v-model="searchForm.queryEndTime"
          type="date"
          clearable
          format="yyyy-MM-dd"
          value-format="yyyy-MM-dd"
        />
      </el-form-item>
      <el-button type="primary" icon="el-icon-error" @click="clearSearchForm">
        清空条件
      </el-button>
      <el-button type="primary" icon="el-icon-search" @click="fetchList()">
        查询
      </el-button>
      <el-button @click="downloadTemplate" type="primary" icon="el-icon-download">
        下载模板
      </el-button>
      <el-button @click="dialogVisible = true" type="primary" icon="el-icon-upload2">
        导入
      </el-button>
      <el-button type="primary" icon="el-icon-download" @click="export2Excel">
        导出
      </el-button>
      <el-button type="danger" icon="el-icon-error" @click="getErrorPage">
        错误数据
      </el-button>
    </el-form>
    <el-table>...</el-table>
  </div>
</template>   

2. 修改api

将searchForm里边所有的值都赋值给我们要查询的data

<script>
  import {fetchReportList, getReportErrorPage, removeReportById,upload} from '@/api/report/report'
  export default {
    name: 'reportList',
    data(){
      return{
        reportList:[],
        searchForm:{
          name:'',
          phone: '',
          idCard:'',
          testingResult:'',
          queryStartTime:'',
          queryEndTime:''
        },
      }
    },
    methods:{
        fetchList(){
            let data = {
          		name:this.searchForm.name,
                phone:this.searchForm.phone,
                idCard:this.searchForm.idCard,
                testingResult:this.searchForm.testingResult,
                queryStartTime:this.searchForm.queryStartTime,
                queryEndTime:this.searchForm.queryEndTime,      
            } 
            fetchReportList(data).then(resp => {
              this.reportList = resp.data.list
            })
        }
         
    }  
  }
</script> 

当然这种方法不叫繁琐,也可以使用...来进行赋值

<script>
  import {fetchReportList, getReportErrorPage, removeReportById,upload} from '@/api/report/report'
  export default {
    name: 'reportList',
    data(){
      return{
        reportList:[],
        searchForm:{
          name:'',
          phone: '',
          idCard:'',
          testingResult:'',
          queryStartTime:'',
          queryEndTime:''
        },
      }
    },
    methods:{
        fetchList(){
          // 可以将searchForm对象解构赋值,与上边的挨个赋值效果一致
          let data = {
              ...this.searchForm
          } 
          fetchReportList(data).then(resp => {
            this.reportList = resp.data.list
          })
        }
    }  
  }
</script> 

5. 添加分页功能

1. 修改页面

el-table标签下添加分页组件el-pagination

<template>
	<div class="app-container">
        <el-table>...</el-table>
        <el-pagination
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
          :current-page="pageNum"
          :page-sizes="[10,50,100, 200, 300, 400]"
          :page-size="pageSize"
          layout="total, sizes, prev, pager, next, jumper"
          :total="total" />    
    </div>    
</template>

2. 修改api

添加分页相关的方法和属性。

首先data中添加了三个属性total总条数,pageNum查询页数,pageSize每页展示多少条。

其次methods中需要添加pageSizepageNum变化的方法。

fetchList方法查询参数data中添加分页的参数pageNumpageSize即可。

分页可以使用async,这一部分我还没研究到。🤡

<script>
  import {fetchReportList} from '@/api/report/report'
  export default {
    name: 'reportList',
    data(){
      return{
        reportList:[],
        searchForm:{
          name:'',
          phone: '',
          idCard:'',
          testingResult:'',
          queryStartTime:'',
          queryEndTime:''
        },
        total:0,
        pageSize:10,
        pageNum:1
      }
    },
    created(){
      this.fetchList();
    },
    methods:{
      clearSearchForm(){
        this.searchForm = {}
        this.fetchList()
      },
      fetchList() {
        let data = {
		  ...this.searchForm,	
          pageNum:this.pageNum,
          pageSize:this.pageSize
        }
        console.log(data,'data')
        fetchReportList(data).then(resp => {
          this.reportList = resp.data.list
          this.total = resp.data.total
          this.pageSize = resp.data.pageSize
          this.pageNum = resp.data.pageNum
        })
      },
      handleSizeChange(pageSize){
       this.pageSize =pageSize
       this.fetchList();
      },
      handleCurrentChange(pageNum){
        this.pageNum = pageNum
        this.fetchList();
      }
    }
  }
</script>

6. 添加导入导出功能

在搜索的后边添加导入导出功能按钮

1. 修改页面

当然也可以将下载模板放置到弹出的el-dialog

<template>
	<div class="app-container">
        <el-form :inline="true">
          
          <el-button  @click="downloadTemplate" type="primary" icon="el-icon-download">
            下载模板
          </el-button>
          <el-button  @click="dialogVisible = true" type="primary" icon="el-icon-upload2">
            导入
          </el-button>
          <el-button type="primary" icon="el-icon-download" @click="export2Excel" >
            导出
          </el-button>
        </el-form>
         <el-dialog title="核酸检测结果数据导入" 
                    :visible.sync="dialogVisible" 
                    width="30%" 
                    @close="fetchList">
      <el-form>
        <el-form-item label="请选择Excel文件">
          <el-upload
            class="upload-demo"
            drag
            ref="upload"
            action="http://localhost:8090/manager/report/import"
            :auto-upload="autoUpload"
            :on-remove="handleRemove"
            :before-remove="beforeRemove"
            :on-change="beforeUpload"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :data="extraParam"
            :limit="fileSizeLimit"
            :file-list="fileList"
            :on-exceed="handleExceed"
            :accept="fileType"
            multiple>
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
            <div class="el-upload__tip" slot="tip">
              <span style="color: #FE6D68;">*</span>
              请选择 .xls 或 .xlsx后缀的文件
            </div>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="submitUpload" type="primary">
          确认
        </el-button>
        <el-button @click="dialogVisible = false">
          取消
        </el-button>
      </div>
    </el-dialog>
    </div>
</template>

2. 修改api

<script>
  import {fetchReportList, getReportErrorPage, removeReportById,upload} from '@/api/report/report'
  export default {
    name: 'reportList',
    data(){
      return{
        reportList:[],
        searchForm:{
          name:'',
          phone: '',
          idCard:'',
          testingResult:'',
          queryStartTime:'',
          queryEndTime:''
        },
        dialogVisible: false, //文件上传对话框是否显示
        BASE_API: process.env.VUE_APP_BASE_API, //获取后端接口地址
        total:0,
        pageSize:10,
        pageNum:1,
        /* 上传时附带的额外参数,返回是一个对象 */
        extraParam: {},
        /* 已上传的文件列表 */
        fileList: [],
        /* 是否在选取文件后立即进行上传 */
        autoUpload: false,
        fileSizeLimit:100,
        fileType:'.xls,.xlsx',
        uploadAction:'http://localhost:8090/manager/report/import'
      }
    },
    created(){
      this.fetchList();
    },
    methods:{
      clearSearchForm(){
        this.searchForm = {}
        this.fetchList()
      },
      fetchList() {
        let data = {
          ...this.searchForm,  
          pageNum:this.pageNum,
          pageSize:this.pageSize
        }
        console.log(data,'data')
        fetchReportList(data).then(resp => {
          this.reportList = resp.data.list
          this.total = resp.data.total
          this.pageSize = resp.data.pageSize
          this.pageNum = resp.data.pageNum
        })
      },
      handleSizeChange(pageSize){
       this.pageSize =pageSize
       this.fetchList();
      },
      handleCurrentChange(pageNum){
        this.pageNum = pageNum
        this.fetchList();
      },
      export2Excel(){
        window.location.href =this.BASE_API
          + `/manager/report/export?name=${this.searchForm.name}&phone=${this.searchForm.phone}&idCard=${this.searchForm.idCard}&testingResult=${this.searchForm.testingResult}`
      },
      /* 文件列表移除文件成功时的钩子 */
      handleRemove(file, fileList) {
        return this.$message.success(`已成功移除"${file.name}"文件`)
      },
      /* 处理上传失败时的勾子 */
      handleUploadError(err, file, fileList) {
        this.$message.error(`文件上传失败`)
      },
      /* 文件删除前的勾子 */
      beforeRemove(file, fileList) {
        return this.$confirm(`确定移除"${file.name}"文件吗?`)
      },
      /* 上传文件之前的钩子 因设置了auto-upload为false,如果使用before-upload,虽有提示,但是还是会请求服务器*/
      beforeUpload(file,fileList) {
        // 1、判断文件名是否重复,不允许上传相同文件
        let existFile = fileList.slice(0, fileList.length - 1).find(f => f.name === file.name)
        if(existFile){
          fileList.pop()
          this.$message.error(file.name+" 文件已存在!")
        }
        // 2、获取文件后缀
        fileList.forEach(everyFile => {
          const fileType = everyFile.name.substring(everyFile.name.lastIndexOf('.'))
          if(this.fileType.search(fileType) === -1){
            fileList.pop()
            this.$message.error("上传文件的类型不正确"+"文件类型必须为" + this.fileType + '')
          }
        })
        this.fileList = fileList;
      },
      /* 文件超出个数限制时的钩子 */
      handleExceed(files, fileList) {
        this.$message.error(`当前限制选择`+ this.fileSizeLimit +`个文件,本次选择了 ${files.length} 个文件,已超出了文件最大上传个数`)
      },
      /* 文件上传成功时的钩子 */
      handleUploadSuccess(res, file, fileList) {
        console.info(JSON.stringify(res))
      },
      /* 确定上传 */
      submitUpload() {
        upload(this.uploadAction,this.fileList).then(res => {
          this.$message.success(res.message)
          this.fileList = []
          this.dialogVisible = false
        });
      },
      downloadTemplate(){
        window.location.href = this.BASE_API + '/manager/report/downloadTemplate'
      }
    }
  }
</script>

7. 最终的list.vue

<template>
  <div class="app-container">
    <el-form :inline="true">
      <el-form-item label="姓名">
        <el-input v-model="searchForm.name" placeholder="姓名" clearable @blur="fetchList" @keyup.enter.native="fetchList"/>
      </el-form-item>
      <el-form-item label="手机号">
        <el-input v-model="searchForm.phone" placeholder="手机号" clearable @blur="fetchList" @keyup.enter.native="fetchList"/>
      </el-form-item>
      <el-form-item label="证件号">
        <el-input v-model="searchForm.idCard" placeholder="证件号" clearable @blur="fetchList" @keyup.enter.native="fetchList"/>
      </el-form-item>
      <el-form-item label="检测结果">
        <el-select v-model="searchForm.testingResult" placeholder="请选择" clearable @blur="fetchList" @keyup.enter.native="fetchList">
          <el-option label="阳性" value="1" />
          <el-option label="阴性" value="2" />
          <el-option label="暂无结果" value="" />
        </el-select>
      </el-form-item>
      <el-form-item label="采样时间" >
        <el-date-picker
          @blur="fetchList" @keyup.enter.native="fetchList"
          v-model="searchForm.queryStartTime"
          type="date"
          clearable
          format="yyyy-MM-dd"
          value-format="yyyy-MM-dd"
        />
        -
        <el-date-picker
          @blur="fetchList" @keyup.enter.native="fetchList"
          v-model="searchForm.queryEndTime"
          type="date"
          clearable
          format="yyyy-MM-dd"
          value-format="yyyy-MM-dd"
        />
      </el-form-item>
      <el-button type="primary" icon="el-icon-error" @click="clearSearchForm">
        清空条件
      </el-button>
      <el-button type="primary" icon="el-icon-search" @click="fetchList()">
        查询
      </el-button>
      <el-button  @click="downloadTemplate" type="primary" icon="el-icon-download">
        下载模板
      </el-button>
      <el-button  @click="dialogVisible = true" type="primary" icon="el-icon-upload2">
        导入
      </el-button>
      <el-button type="primary" icon="el-icon-download" @click="export2Excel" >
        导出
      </el-button>
      <el-button type="danger" icon="el-icon-error" @click="getErrorPage">
        错误数据
      </el-button>
    </el-form>
    <el-divider />
    <!-- 表格 -->
    <el-table :data="reportList" border stripe>
      <el-table-column type="index" width="50" label="序号"/>
      <el-table-column prop="name" width="70" label="姓名" />
      <el-table-column prop="phone" width="110" label="联系方式" />
      <el-table-column prop="idType" width="80" label="证件类型">
        <template slot-scope="scope">
          <span v-if="scope.row.idType == '1'">身份证</span>
          <span v-if="scope.row.idType == '2'">护 照</span>
          <span v-if="scope.row.idType == '3'">其 他</span>
        </template>
      </el-table-column>
      <el-table-column prop="idCard" width="150" label="证件号" />
      <el-table-column prop="address"  label="受检者地址" />
      <el-table-column prop="samplingTime" label="采样时间" />
      <el-table-column prop="testingResult" label="核酸检测结果">
        <template slot-scope="scope">
          <span v-if="scope.row.testingResult == '1'">阳性</span>
          <span v-if="scope.row.testingResult == '2'">阴性</span>
          <span v-else>暂无结果</span>
        </template>
      </el-table-column>
      <el-table-column prop="testingInstitutionName" label="检测机构名称" />
      <el-table-column label="操作" align="center">
        <template slot-scope="scope">

          <router-link :to="`/report/edit/${scope.row.id}`" style="margin-right: 5px">
            <el-button type="primary" size="mini">修改</el-button>
          </router-link>
          <el-button type="danger" @click="delReport(scope.row.id)"
                     size="mini">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="pageNum"
      :page-sizes="[10,50,100, 200, 300, 400]"
      :page-size="pageSize"
      layout="total, sizes, prev, pager, next, jumper"
      :total="total"
      size="">
    </el-pagination>


    <el-dialog title="核酸检测结果数据导入" :visible.sync="dialogVisible" width="30%" @close="fetchList">
      <el-form>
        <el-form-item label="请选择Excel文件">
          <el-upload
            class="upload-demo"
            drag
            ref="upload"
            action="http://localhost:8090/manager/report/import"
            :auto-upload="autoUpload"
            :on-remove="handleRemove"
            :before-remove="beforeRemove"
            :on-change="beforeUpload"
            :on-success="handleUploadSuccess"
            :on-error="handleUploadError"
            :data="extraParam"
            :limit="fileSizeLimit"
            :file-list="fileList"
            :on-exceed="handleExceed"
            :accept="fileType"
            multiple>
            <i class="el-icon-upload"></i>
            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
            <div class="el-upload__tip" slot="tip">
              <span style="color: #FE6D68;">*</span>
              请选择 .xls 或 .xlsx后缀的文件
            </div>
          </el-upload>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="submitUpload" type="primary">
          确认
        </el-button>
        <el-button @click="dialogVisible = false">
          取消
        </el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
  import {fetchReportList, getReportErrorPage, removeReportById,upload} from '@/api/report/report'
  export default {
    name: 'reportList',
    data(){
      return{
        reportList:[],
        searchForm:{
          name:'',
          phone: '',
          idCard:'',
          testingResult:'',
          queryStartTime:'',
          queryEndTime:''
        },
        dialogVisible: false, //文件上传对话框是否显示
        BASE_API: process.env.VUE_APP_BASE_API, //获取后端接口地址
        total:0,
        pageSize:10,
        pageNum:1,
        /* 上传时附带的额外参数,返回是一个对象 */
        extraParam: {},
        /* 已上传的文件列表 */
        fileList: [],
        /* 是否在选取文件后立即进行上传 */
        autoUpload: false,
        fileSizeLimit:100,
        fileType:'.xls,.xlsx',
        uploadAction:'http://localhost:8090/manager/report/import'
      }
    },
    created(){
      this.fetchList();
    },
    methods:{
      clearSearchForm(){
        this.searchForm = {}
        this.fetchList()
      },
      fetchList() {
        let data = {
          ...this.searchForm,
          pageNum:this.pageNum,
          pageSize:this.pageSize
        }
        console.log(data,'data')
        fetchReportList(data).then(resp => {
          this.reportList = resp.data.list
          this.total = resp.data.total
          this.pageSize = resp.data.pageSize
          this.pageNum = resp.data.pageNum
        })
      },
      delReport(id){
        this.$confirm('此操作将永久删除该记录,是否继续?', '提示', {
          confirmButtonText: '确定',
          cancelButtonText: '取消',
          type: 'warning'
        }).then(() => {
          removeReportById(id).then(resp => {
            this.$message.success(resp.message)
            this.fetchList()
          })
        }).catch(err => {
          this.$message({
            type: 'info',
            message: '已取消删除'
          })
        })
      },
      handleSizeChange(pageSize){
       this.pageSize =pageSize
       this.fetchList();
      },
      handleCurrentChange(pageNum){
        this.pageNum = pageNum
        this.fetchList();
      },
      export2Excel(){
        window.location.href =this.BASE_API
          + `/manager/report/export?name=${this.searchForm.name}&phone=${this.searchForm.phone}&idCard=${this.searchForm.idCard}&testingResult=${this.searchForm.testingResult}`
      },
      getErrorPage(){
        getReportErrorPage().then(resp => {
          this.reportList = resp.data.list
          this.total = resp.data.total
          this.pageSize = resp.data.pageSize
          this.pageNum = resp.data.pageNum
        })
      },
      /* 文件列表移除文件成功时的钩子 */
      handleRemove(file, fileList) {
        return this.$message.success(`已成功移除"${file.name}"文件`)
      },
      /* 处理上传失败时的勾子 */
      handleUploadError(err, file, fileList) {
        this.$message.error(`文件上传失败`)
      },
      /* 文件删除前的勾子 */
      beforeRemove(file, fileList) {
        return this.$confirm(`确定移除"${file.name}"文件吗?`)
      },
      /* 上传文件之前的钩子 因设置了auto-upload为false,如果使用before-upload,虽有提示,但是还是会请求服务器*/
      beforeUpload(file,fileList) {
        // 1、判断文件名是否重复,不允许上传相同文件
        let existFile = fileList.slice(0, fileList.length - 1).find(f => f.name === file.name)
        if(existFile){
          fileList.pop()
          this.$message.error(file.name+" 文件已存在!")
        }
        // 2、获取文件后缀
        fileList.forEach(everyFile => {
          const fileType = everyFile.name.substring(everyFile.name.lastIndexOf('.'))
          if(this.fileType.search(fileType) === -1){
            fileList.pop()
            this.$message.error("上传文件的类型不正确"+"文件类型必须为" + this.fileType + '')
          }
        })
        this.fileList = fileList;
      },
      /* 文件超出个数限制时的钩子 */
      handleExceed(files, fileList) {
        this.$message.error(`当前限制选择`+ this.fileSizeLimit +`个文件,本次选择了 ${files.length} 个文件,已超出了文件最大上传个数`)
      },
      /* 文件上传成功时的钩子 */
      handleUploadSuccess(res, file, fileList) {
        console.info(JSON.stringify(res))
      },
      /* 确定上传 */
      submitUpload() {
        upload(this.uploadAction,this.fileList).then(res => {
          this.$message.success(res.message)
          this.fileList = []
          this.dialogVisible = false
        });
      },
      downloadTemplate(){
        window.location.href = this.BASE_API + '/manager/report/downloadTemplate'
      }
    }
  }
</script>

6. 创建组件(使用dialog弹出新增编辑页面)

暂时没时间写

7. 后端创建

创建出来后端项目,配置好跨域,添加context-path

server:
  port: 8090
  servlet:
    context-path: /manager

8. 运行前配置

前后端分离的配置一般都是通过Nginx来管理静态资源,在通过Nginx将请求代理到后台的,所以需要对前端项目进行相关的配置

1. 本地运行

1. 修改 .env.development文件

VUE_APP_BASE_API修改为Nginx运行端口

VUE_APP_BASE_API = 'http://localhost'

2. 修改 request.js

src/utils/request.js中的axios相关哦诶之修改一下

  1. 修改baseURL
const service = axios.create({
    baseURL: process.env.VUE_APP_BASE_API,
    timeout:5000
})
  1. 修改响应拦截器
service.interceptors.response.use(response =>{
    const res = response.data
	// 这里添加上自己后端返回正确的编码
    if (res.code !== 20000 && res.code != 200) {}
})

3. 修改mock服务

本次未整合后端的登录注册服务,依旧使用原来的mock

  1. 修改mock/mock-server.js第39行
url: new RegExp(`/dev-api${url}`),
  1. 修改src/api/user.js为每一个接口添加baseURL
import request from '@/utils/request'

export function login(data) {
  return request({
    baseURL: '/dev-api',
    url: '/vue-admin-template/user/login',
    method: 'post',
    data
  })
}

export function getInfo(token) {
  return request({
    baseURL: '/dev-api',
    url: '/vue-admin-template/user/info',
    method: 'get',
    params: { token }
  })
}

export function logout() {
  return request({
    baseURL: '/dev-api',
    url: '/vue-admin-template/user/logout',
    method: 'post'
  })
}

4. Nginx修改

前端打包

npm run bubild:prod

将前端打包后生成的dist文件夹复制到Nginx解压目录下,修改conf/nginx.conf

server {
        listen       80;
        server_name  localhost;
        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   dist;
            index  index.html index.htm;
        }


        location /manager/ {
            proxy_pass http://localhost:8090;
        }
 }       

nginx解压目录的文件地址栏输入cmd,在cmd窗口运行Nginx

start nginx.exe

启动后端项目,并使用浏览器打开localhost进行测试

2. 部署线上环境

1. 后端修改

pom.xml添加打包的相关配置,在IDEA右侧maven -->LifeCycle --> package 或者直接执行mvn clean package

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <mainClass>net.lesscoding.MainApp</mainClass>
            </configuration>
            <version>1.4.2.RELEASE</version>
        </plugin>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <goals>
                        <goal>build-info</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.sonarsource.scanner.maven</groupId>
            <artifactId>sonar-maven-plugin</artifactId>
            <version>3.2</version>
        </plugin>
    </plugins>
</build>

2. 服务器相关配置

Logo

前往低代码交流专区

更多推荐