3 changed files with 486 additions and 3 deletions
			
			
		- 
					9src/components/report/TurnoverReport.vue
 - 
					7src/router/index.js
 - 
					473src/views/doctorCheck/checkPicImport.vue
 
@ -0,0 +1,473 @@ | 
				
			|||
<template> | 
				
			|||
  <div> | 
				
			|||
    <!--主体--> | 
				
			|||
    <div> | 
				
			|||
      <div class="middlebox"> | 
				
			|||
        <div class="contenttitle"> | 
				
			|||
          体检 / | 
				
			|||
          <span class="contenttitleBold">批量导入检查图片</span> | 
				
			|||
        </div> | 
				
			|||
      </div> | 
				
			|||
      <div style="margin-bottom: 15px;display: flex;justify-content: space-between; | 
				
			|||
        background-color: #fff;border-radius: 8px;padding: 15px;margin-top: 7px;"> | 
				
			|||
        <div></div> | 
				
			|||
        <div style="display: flex;"> | 
				
			|||
          <div style="padding: 0 5px;"> | 
				
			|||
            <el-button class="commonbutton" @click="btnImport" size="small">导入</el-button> | 
				
			|||
          </div> | 
				
			|||
          <div style="padding: 0 5px;"> | 
				
			|||
            <el-button class="commonbutton" @click="btnExport('tableData')" size="small" | 
				
			|||
              style="width:140px;">导入后结果状态导出</el-button> | 
				
			|||
          </div> | 
				
			|||
        </div> | 
				
			|||
      </div> | 
				
			|||
      <div id="tableData" style="padding: 15px;background-color: #fff;border-radius: 8px;"> | 
				
			|||
        <el-table :data="tableData" :row-class-name="importRowClassName" | 
				
			|||
          :height="window.pageHeight < 600 ? 440 : (window.pageHeight - 210)" highlight-current-row size="small" | 
				
			|||
          :summary-method="getSummaries" show-summary> | 
				
			|||
          <el-table-column type="index" label="序号" width="40" align="center" /> | 
				
			|||
          <el-table-column prop="importState" label="导入状态" min-width="80" sortable /> | 
				
			|||
          <el-table-column prop="importDes" label="导入描述" min-width="280" sortable /> | 
				
			|||
          <el-table-column prop="fileName" label="导入文件" min-width="180" sortable /> | 
				
			|||
        </el-table> | 
				
			|||
      </div> | 
				
			|||
    </div> | 
				
			|||
 | 
				
			|||
    <!--弹窗--> | 
				
			|||
    <div> | 
				
			|||
      <!-- 选照片上传 --> | 
				
			|||
      <el-dialog title="选择照片上传" :visible.sync="upPic.visible" width="800px" height="400" :close-on-click-modal="false" | 
				
			|||
        :append-to-body="true"> | 
				
			|||
        <div> | 
				
			|||
          <div> | 
				
			|||
            <el-upload action="#" multiple :file-list="fileList" accept=".jpg,.png,.bmp,.gif" list-type="picture-card" | 
				
			|||
              :on-change="onChange" :auto-upload="false"> | 
				
			|||
              <i slot="default" class="el-icon-plus"></i> | 
				
			|||
              <div slot="file" slot-scope="{file}"> | 
				
			|||
                <img class="el-upload-list__item-thumbnail" :src="file.url" :alt="file.name"> | 
				
			|||
                <span class="el-upload-list__item-actions"> | 
				
			|||
                  <span class="el-upload-list__item-preview" @click="handlePictureCardPreview(file)"> | 
				
			|||
                    <i class="el-icon-zoom-in"></i> | 
				
			|||
                  </span> | 
				
			|||
                  <!-- 不需再下载 | 
				
			|||
                  <span | 
				
			|||
                    v-if="!disabled" | 
				
			|||
                    class="el-upload-list__item-delete" | 
				
			|||
                    @click="handleDownload(file)" | 
				
			|||
                  > | 
				
			|||
                    <i class="el-icon-download"></i> | 
				
			|||
                  </span> | 
				
			|||
                  --> | 
				
			|||
                  <span class="el-upload-list__item-delete" @click="handleRemove(file)"> | 
				
			|||
                    <i class="el-icon-delete"></i> | 
				
			|||
                  </span> | 
				
			|||
                </span> | 
				
			|||
              </div> | 
				
			|||
            </el-upload> | 
				
			|||
          </div> | 
				
			|||
          <div style="display: flex;margin-top: 10px;justify-content:space-between;"> | 
				
			|||
            <div> | 
				
			|||
              <div style="display: flex;"> | 
				
			|||
                <el-select v-model="upPic.getCheckNoMode" placeholder="截取检查号" filterable size="small" | 
				
			|||
                  style="width:180px;margin: 0 5px 0 0;"> | 
				
			|||
                  <el-option label="文件名即检查号" value="0" /> | 
				
			|||
                  <el-option label="文件名左侧 ? 个字符" value="1" /> | 
				
			|||
                  <el-option label="文件名右侧 ? 个字符" value="2" /> | 
				
			|||
                  <el-option label="文件名第 ? 至 ? 个字符" value="3" /> | 
				
			|||
                </el-select> | 
				
			|||
                <el-input v-model="upPic.startLen" style="width:150px;" size="small" | 
				
			|||
                  v-if="upPic.getCheckNoMode == '1' || upPic.getCheckNoMode == '2' || upPic.getCheckNoMode == '3'" /> | 
				
			|||
                <span v-if="upPic.getCheckNoMode == '3'" style="padding: 5px 5px;">至</span> | 
				
			|||
                <el-input v-model="upPic.endLen" style="width:150px;" size="small" v-if="upPic.getCheckNoMode == '3'" /> | 
				
			|||
              </div> | 
				
			|||
            </div> | 
				
			|||
            <div> | 
				
			|||
              <el-button type="primary" class="btnClass" @click="btnUpload">上传</el-button> | 
				
			|||
            </div> | 
				
			|||
          </div> | 
				
			|||
        </div> | 
				
			|||
      </el-dialog> | 
				
			|||
 | 
				
			|||
      <el-dialog :visible.sync="dialogVisible" :append-to-body="true"> | 
				
			|||
        <img width="100%" :src="dialogImageUrl" alt=""> | 
				
			|||
      </el-dialog> | 
				
			|||
 | 
				
			|||
      <!-- 通用进度条 --> | 
				
			|||
      <el-dialog title="数据处理中……" :visible.sync="elProgress.display" width="700px" height="400" :show-close="false" | 
				
			|||
        :close-on-click-modal="false" :append-to-body="true"> | 
				
			|||
        <ElProgressOCX /> | 
				
			|||
      </el-dialog> | 
				
			|||
    </div> | 
				
			|||
  </div> | 
				
			|||
</template> | 
				
			|||
<script> | 
				
			|||
import moment from "moment" | 
				
			|||
import { mapState, mapActions } from "vuex"; | 
				
			|||
import { read, readFile, utils } from "xlsx"; | 
				
			|||
import FileSaver from 'file-saver'; | 
				
			|||
 | 
				
			|||
import { getapi, postapi, putapi, deletapi } from "@/api/api"; | 
				
			|||
import { getPagePriv, checkPagePriv, deepCopy, arrayExistObj, arrayFilter, dddw, tcdate } from '../../utlis/proFunc'; | 
				
			|||
import ElProgressOCX from "../../components/report/ElProgressOCX.vue"; | 
				
			|||
 | 
				
			|||
 | 
				
			|||
export default { | 
				
			|||
  components: { | 
				
			|||
    ElProgressOCX, | 
				
			|||
  }, | 
				
			|||
  data() { | 
				
			|||
    return { | 
				
			|||
      pagePriv: { | 
				
			|||
        routeUrlorPageName: 'patientRegisterBatch', //当前页面归属路由或归属页面权限名称 | 
				
			|||
        privs: [] // 页面权限 | 
				
			|||
      }, | 
				
			|||
      upPic: { | 
				
			|||
        visible: false, | 
				
			|||
        getCheckNoMode: '3', | 
				
			|||
        startLen: null, | 
				
			|||
        endLen: null | 
				
			|||
      }, | 
				
			|||
      tableData: [], | 
				
			|||
      dialogImageUrl: '', | 
				
			|||
      dialogVisible: false, | 
				
			|||
      fileList: [], | 
				
			|||
    }; | 
				
			|||
  }, | 
				
			|||
 | 
				
			|||
 | 
				
			|||
  //组件创建完成,一般页面初始布局放在这里 | 
				
			|||
  created() { | 
				
			|||
    //获取用户当前页面的权限 | 
				
			|||
    let userPriv = window.sessionStorage.getItem('userPriv') | 
				
			|||
    if (userPriv) this.pagePriv.privs = deepCopy(getPagePriv(this.pagePriv.routeUrlorPageName)) | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
  }, | 
				
			|||
 | 
				
			|||
  //页面挂载完成,一般页面渲染数据放在这里 | 
				
			|||
  mounted() { | 
				
			|||
    this.dictInit() | 
				
			|||
 | 
				
			|||
  }, | 
				
			|||
 | 
				
			|||
  computed: { | 
				
			|||
    ...mapState(["window", "dict", "elProgress", "patientRegister", "customerOrg"]), | 
				
			|||
  }, | 
				
			|||
 | 
				
			|||
  methods: { | 
				
			|||
    dddw, moment, checkPagePriv, | 
				
			|||
    dictInit() { | 
				
			|||
 | 
				
			|||
 | 
				
			|||
      //性别(查询 仅档案用) | 
				
			|||
      getapi("/api/app/sex").then((res) => { | 
				
			|||
        if (res.code == 1) { | 
				
			|||
          this.dict.sex = res.data; | 
				
			|||
        } | 
				
			|||
      }); | 
				
			|||
 | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    // 导入完后,状态统计 | 
				
			|||
    getSummaries(param) { | 
				
			|||
      const { columns, data } = param; | 
				
			|||
      const sumCol = [2]; //需合计的列 | 
				
			|||
      const sums = []; | 
				
			|||
      let success = 0, fail = 0; | 
				
			|||
 | 
				
			|||
      columns.forEach((column, index) => { | 
				
			|||
        //显示合计列 | 
				
			|||
        if (index === 1) { | 
				
			|||
          sums[index] = "导入合计"; | 
				
			|||
          return; | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        //不合计的列 | 
				
			|||
        if (sumCol.indexOf(index) == -1) { | 
				
			|||
          sums[index] = ""; | 
				
			|||
          return; | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        data.forEach((item) => { | 
				
			|||
          console.log('item,column.property', item, column.property) | 
				
			|||
          if (item[column.property]) { | 
				
			|||
            fail++ | 
				
			|||
          } else { | 
				
			|||
            success++ | 
				
			|||
          } | 
				
			|||
        }); | 
				
			|||
 | 
				
			|||
      }); | 
				
			|||
      sums[2] = `成功:${success} 条,失败:${fail} 条。` | 
				
			|||
 | 
				
			|||
      return sums; | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    onChange(file, fileList) { | 
				
			|||
      this.fileList = fileList; | 
				
			|||
      console.log('file, fileList', file, fileList); | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    // 显示选文件与上传 | 
				
			|||
    btnImport() { | 
				
			|||
      this.upPic.visible = true | 
				
			|||
 | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    // 上传 | 
				
			|||
    btnUpload() { | 
				
			|||
      console.log('that.registerCheckId', this.registerCheckId) | 
				
			|||
      if (!(this.fileList && typeof this.fileList == 'object' && this.fileList.length > 0)) { | 
				
			|||
        this.$message.warning({ showClose: true, message: "请选择要上传的文件!" }) | 
				
			|||
        return | 
				
			|||
      } | 
				
			|||
      let checkSize = [] | 
				
			|||
      this.fileList.forEach(e => { | 
				
			|||
        if (e.size > 1024 * 1024 * 20) checkSize.push(e.name) | 
				
			|||
      }); | 
				
			|||
 | 
				
			|||
      if (checkSize.length > 0) { | 
				
			|||
        this.$message.error({ showClose: true, message: `所选文件大于 20MB ,文件过大无法上传` }) | 
				
			|||
        return; | 
				
			|||
      } | 
				
			|||
 | 
				
			|||
      let that = this; | 
				
			|||
      let count = 0, err = ''; | 
				
			|||
      console.log('this.fileList', this.fileList) | 
				
			|||
      for (let i = 0; i < this.fileList.length; i++) { | 
				
			|||
        let file = this.fileList[i] | 
				
			|||
        console.log('file', file) | 
				
			|||
        let reader = new FileReader(); | 
				
			|||
         | 
				
			|||
        // 定义读取文件 | 
				
			|||
        reader.onload = (event) => { | 
				
			|||
          let fileName = "" | 
				
			|||
          let dotIndex = file.name.lastIndexOf('.'); | 
				
			|||
          if (dotIndex > -1) fileName = file.name.substring(0, dotIndex); | 
				
			|||
          let data = event.target.result; | 
				
			|||
          // console.log('base64', data.length, data) | 
				
			|||
          let checkRequestNo = fileName | 
				
			|||
          switch (this.upPic.getCheckNoMode) { | 
				
			|||
            case '1': | 
				
			|||
              checkRequestNo = fileName.substring(0, this.upPic.startLen) | 
				
			|||
              if (checkRequestNo.length != this.upPic.startLen) err = `从 ${fileName} 截取的 检查申请号长度不够` | 
				
			|||
              break; | 
				
			|||
            case '2': | 
				
			|||
              checkRequestNo = fileName.substring(fileName.length - this.upPic.startLen) | 
				
			|||
              if (checkRequestNo.length != this.upPic.startLen) err = `从 ${fileName} 截取的 检查申请号长度不够` | 
				
			|||
              break; | 
				
			|||
            case '3': | 
				
			|||
              checkRequestNo = fileName.substring(this.upPic.startLen - 1, this.upPic.endLen) | 
				
			|||
              if (checkRequestNo.length != Number(this.upPic.endLen - this.upPic.startLen) + Number(1)) err = `从 ${fileName} 截取的 检查申请号长度不够` | 
				
			|||
              break; | 
				
			|||
            default: | 
				
			|||
              break; | 
				
			|||
          } | 
				
			|||
          count++; | 
				
			|||
          if (err) {             | 
				
			|||
            that.tableData.push({ | 
				
			|||
              importState:'导入失败', | 
				
			|||
              importDes:err, | 
				
			|||
              fileName, | 
				
			|||
            })             | 
				
			|||
          } else {    | 
				
			|||
            let rd = { | 
				
			|||
              importState:'导入失败', | 
				
			|||
              importDes:err, | 
				
			|||
              fileName, | 
				
			|||
            }          | 
				
			|||
            let body = { | 
				
			|||
              checkRequestNo, | 
				
			|||
              pictureName: fileName, | 
				
			|||
              pictureBase64: data | 
				
			|||
            } | 
				
			|||
            postapi('/api/app/RegisterCheckPicture/ImportRegisterCheckPicture', body) | 
				
			|||
              .then(res => { | 
				
			|||
 | 
				
			|||
                if (res.code > -1) {                   | 
				
			|||
                  rd.importState = "导入成功" | 
				
			|||
                  rd.importDes = ""                   | 
				
			|||
                } else { | 
				
			|||
                  rd.importDes = res.message                   | 
				
			|||
                } | 
				
			|||
              }) | 
				
			|||
              .catch(error => { | 
				
			|||
                rd.importDes = `${error}`                 | 
				
			|||
              }) | 
				
			|||
              .finally(() => { | 
				
			|||
                that.tableData.push(rd) | 
				
			|||
                if (count == that.fileList.length) { | 
				
			|||
                  that.closePicUpload() | 
				
			|||
                } | 
				
			|||
              }); | 
				
			|||
          } | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        // 错误处理 | 
				
			|||
        reader.onerror = function (event) { | 
				
			|||
          // 读取失败时执行的代码 | 
				
			|||
          this.$message.error({ showClose: true, message: `文件转 base64 失败:${event.target.error}` }) | 
				
			|||
        }; | 
				
			|||
 | 
				
			|||
        reader.readAsDataURL(file.raw); | 
				
			|||
        // reader.readAsText(this.fileList[0].raw); | 
				
			|||
        // reader.readAsBinaryString(this.fileList[0].raw); | 
				
			|||
      }; | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    closePicUpload(){ | 
				
			|||
      this.upPic.visible = false | 
				
			|||
 | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    //导入完后,导入状态显示 | 
				
			|||
    importRowClassName({ row, rowIndex }) { | 
				
			|||
      if (row.importState == '导入失败') { | 
				
			|||
        return "danger"; | 
				
			|||
      } else { | 
				
			|||
        return ""; | 
				
			|||
      } | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    //导入进行时 | 
				
			|||
    // startRow : 从第几行开始导入,同名判断交互时,续接导入 | 
				
			|||
    // startRowNameType : 续接导入的第1行,执行 同名判断模式, 3.不提示,按新人导入(前端不用管) | 
				
			|||
    async importing(startRow, startRowNameType) { | 
				
			|||
      let body = {} | 
				
			|||
      for (let i = startRow; i < this.choosedData.length; i++) { | 
				
			|||
        this.elProgress.percentage = Math.floor( | 
				
			|||
          ((i + 1) * 100) / this.choosedData.length | 
				
			|||
        ); | 
				
			|||
 | 
				
			|||
        // 结束导入 | 
				
			|||
        if (i == this.choosedData.length - 1) { | 
				
			|||
          this.elProgress.display = false; | 
				
			|||
          this.seq = -1 | 
				
			|||
        } | 
				
			|||
      } | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    //通用导出 | 
				
			|||
    btnExport(elId) { | 
				
			|||
      let table = document.getElementById(elId); | 
				
			|||
      let tableData = table.innerHTML | 
				
			|||
      let fileName = moment(new Date()).format('yyyyMMDDHHmmss') + '.xls' | 
				
			|||
      let blob = new Blob([tableData], { type: "text/plain;charset=utf-8" }); | 
				
			|||
      FileSaver.saveAs(blob, fileName); | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
  }, | 
				
			|||
 | 
				
			|||
  watch: { | 
				
			|||
    "seq": { | 
				
			|||
      immediate: true, // 立即执行 | 
				
			|||
      // // deep: true, // 深度监听复杂类型内变化 | 
				
			|||
      handler(newVal, oldVal) { | 
				
			|||
        console.log('watch:seq:', newVal, oldVal) | 
				
			|||
        if (!oldVal && oldVal != 0) { | 
				
			|||
          this.oldSeq = -2 | 
				
			|||
        } else { | 
				
			|||
          this.oldSeq = oldVal | 
				
			|||
        } | 
				
			|||
        if (newVal != oldVal) { | 
				
			|||
          this.btnProcess(newVal); | 
				
			|||
        } | 
				
			|||
      } | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    "readDataOpts.titleRow": { | 
				
			|||
      // immediate: true, // 立即执行 | 
				
			|||
      // deep: true, // 深度监听复杂类型内变化 | 
				
			|||
      handler(newVal, oldVal) { | 
				
			|||
        console.log('watch:readDataOpts.titleRow:', newVal, oldVal) | 
				
			|||
        if (newVal && newVal != oldVal) { | 
				
			|||
          if (this.seq == 11) this.readData() | 
				
			|||
        } | 
				
			|||
      } | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    //所选体检次数改变时,自动获取登记日期 | 
				
			|||
    "query.customerOrgRegister.id": { | 
				
			|||
      // immediate: true, // 立即执行 | 
				
			|||
      // deep: true, // 深度监听复杂类型内变化 | 
				
			|||
      handler(newVal, oldVal) { | 
				
			|||
        if (newVal && newVal != oldVal) { | 
				
			|||
          this.query.startDate = new Date(this.query.customerOrgRegister.beginTime) | 
				
			|||
          this.query.endDate = this.query.customerOrgRegister.isComplete == 'N' ? new Date() : new Date(this.query.customerOrgRegister.endTime) | 
				
			|||
        } | 
				
			|||
      } | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
    //选体检新体检次数改变时,获取分组数据 | 
				
			|||
    "customerOrgRegister.id": { | 
				
			|||
      // immediate: true, // 立即执行 | 
				
			|||
      // deep: true, // 深度监听复杂类型内变化 | 
				
			|||
      handler(newVal, oldVal) { | 
				
			|||
        if (newVal && newVal != oldVal) { | 
				
			|||
          this.getGroups(newVal) | 
				
			|||
        } | 
				
			|||
      } | 
				
			|||
    }, | 
				
			|||
 | 
				
			|||
 | 
				
			|||
  }, | 
				
			|||
 | 
				
			|||
 | 
				
			|||
}; | 
				
			|||
</script> | 
				
			|||
<style scoped> | 
				
			|||
@import '../../assets/css/global_button.css'; | 
				
			|||
@import '../../assets/css/global_card.css'; | 
				
			|||
@import '../../assets/css/global_dialog.css'; | 
				
			|||
@import '../../assets/css/global_form.css'; | 
				
			|||
@import '../../assets/css/global_input.css'; | 
				
			|||
@import '../../assets/css/global_table.css'; | 
				
			|||
@import '../../assets/css/global.css'; | 
				
			|||
 | 
				
			|||
 | 
				
			|||
.spanLeftClass { | 
				
			|||
  margin-top: 6px; | 
				
			|||
  width: 70px; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
.spanMidClass { | 
				
			|||
  text-align: center; | 
				
			|||
  margin-top: 6px; | 
				
			|||
  width: 50px; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/* type=number 显示微调按钮 */ | 
				
			|||
::v-deep input[type="number"]::-webkit-inner-spin-button, | 
				
			|||
input[type="number"]::-webkit-outer-spin-button { | 
				
			|||
  -webkit-appearance: button !important; | 
				
			|||
  margin: 0 -12px 0 0 !important; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
::v-deep .menu_item { | 
				
			|||
  line-height: 24px; | 
				
			|||
} | 
				
			|||
</style> | 
				
			|||
						Write
						Preview
					
					
					Loading…
					
					Cancel
						Save
					
		Reference in new issue