You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1020 lines
35 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. <template>
  2. <div>
  3. <!--主体-->
  4. <div>
  5. <div class="middlebox">
  6. <div class="contenttitle">
  7. 体检 /
  8. <span class="contenttitleBold">从文件导入检验结果</span>
  9. </div>
  10. </div>
  11. <div style="margin-bottom: 15px;display: flex;justify-content: space-between;
  12. background-color: #fff;border-radius: 8px;padding: 15px;margin-top: 7px;">
  13. <div></div>
  14. <div style="display: flex;">
  15. <div style="padding: 0 5px;">
  16. <a :underline="false" href="/files/检验结果导入模板_纵向.xls"><el-button class="commonbutton"
  17. size="small">下载纵向模版</el-button></a>
  18. </div>
  19. <div style="padding: 0 5px;">
  20. <a :underline="false" href="/files/检验结果导入模板_横向.xls"><el-button class="commonbutton"
  21. size="small">下载横向模版</el-button></a>
  22. </div>
  23. <div style="padding: 0 5px;">
  24. <el-button class="commonbutton" @click="seq = 10" size="small">导入</el-button>
  25. </div>
  26. <div style="padding: 0 5px;">
  27. <el-button class="commonbutton" @click="btnExport('tableData')" size="small"
  28. style="width:140px;">导入后结果状态导出</el-button>
  29. </div>
  30. </div>
  31. </div>
  32. <div id="tableData" style="padding: 15px;background-color: #fff;border-radius: 8px;">
  33. <el-table :data="tableData" :row-class-name="importRowClassName"
  34. :height="window.pageHeight < 600 ? 440 : (window.pageHeight - 210)" highlight-current-row size="small"
  35. :summary-method="getSummaries" show-summary>
  36. <el-table-column type="index" label="序号" width="40" align="center" />
  37. <el-table-column prop="importState" label="导入状态" min-width="80" sortable />
  38. <el-table-column prop="importDes" label="导入描述" min-width="180" sortable />
  39. <el-table-column v-for="(item, index) in importCols" :key="`col${index}`" :prop="item.dispLabel"
  40. align="center" :label="item.dataLabel || item.dispLabel" min-width="100" />
  41. </el-table>
  42. </div>
  43. </div>
  44. <!--弹窗-->
  45. <div>
  46. <el-dialog title="导入EXCEL注意事项" :visible.sync="dialogGroup.fileReadme" width="700px" :show-close="false"
  47. :append-to-body="true" :close-on-click-modal="false">
  48. <div style="height:400px;padding: 0 20px; ">
  49. <br />导入Excel时Excel的格式必须符合一定规范该格式的模板文件可通过下载模板获取
  50. <br />注意事项如下:
  51. <br />
  52. <div style="margin-left: 25px;">1条码号不能为空当条码号为检查项目条码时姓名可以为空否则不能为空
  53. <br />2从仪器导出的数据可能含有标本号与仪器通道但标本号与仪器通道不参与数据导入
  54. <br />3Excel结果纵向排列时须检查项目与结果的匹配横向排列时自动匹配
  55. <br />4Excel中标题列不能有单元格合并
  56. </div>
  57. </div>
  58. <span slot="footer" class="dialog-footer">
  59. <el-button class="commonbutton" type="primary" @click="seq++">下一步</el-button>
  60. <el-button class="commonbutton" @click="seq = -1">关闭</el-button>
  61. </span>
  62. </el-dialog>
  63. <el-dialog title="选择文件" :visible.sync="dialogGroup.fileChoose" width="700px" :show-close="false"
  64. :append-to-body="true" :close-on-click-modal="false">
  65. <div style="height:400px;padding: 0 50px;">
  66. <!-- webkitdirectory 选择文件属性 multiple 多选属性-->
  67. <div><input id="fileNames" type="file" accept=".xlsx,.xls" @change="changeFileChoose" @focus="fileGetFocus" />
  68. </div>
  69. <div style="margin: 5px 70px;width:240px;">
  70. <el-table :data="sheetNames" border ref="sheetNames" height="260" row-click="chooseSheetName"
  71. highlight-current-row size="small">
  72. <el-table-column prop="sheetName" label="Excel表单名" min-width="200" align="center" />
  73. </el-table>
  74. </div>
  75. <div style="display: flex;flex-wrap: wrap;">
  76. <span style="margin-top: 6px;">标题行 </span>
  77. <el-input type="number" v-model="readDataOpts.titleRow" size="small" style="width:60px;margin: 0 5px;" />
  78. <span style="margin-top: 6px;"></span>
  79. </div>
  80. <div style="margin-top: 7px;display: flex;flex-wrap: wrap;">
  81. <span style="margin-top: -1px;">Excel结果排列方式</span>
  82. <el-radio v-model="readDataOpts.resultMode" label="V">纵向排列</el-radio>
  83. <el-radio v-model="readDataOpts.resultMode" label="H">横向排列</el-radio>
  84. </div>
  85. <div style="margin-top: 7px;display: flex;flex-wrap: wrap;">
  86. <span style="margin-top: -1px;">条码方式</span>
  87. <el-radio v-model="dataImportOpts.barcodeMode" label="0">人员条码</el-radio>
  88. <el-radio v-model="dataImportOpts.barcodeMode" label="1">项目条码</el-radio>
  89. </div>
  90. <div style="display: flex;flex-wrap: wrap;">
  91. <span style="margin-top: 6px;">未填检查时间时起始检查时间</span>
  92. <el-date-picker v-model="dataImportOpts.startCheckDate" type="datetime" placeholder="选择起始检查时间" align="right"
  93. :picker-options="pickerOptions" value-format="yyyy-MM-dd HH:mm:ss" style="width:160px;" size="small">
  94. </el-date-picker>
  95. <span style="margin-top: 6px;">标本间隔时间</span>
  96. <el-input type="number" v-model="dataImportOpts.checkInterval" size="small"
  97. style="width:60px;margin: 0 5px;">
  98. <template slot="append"></template>
  99. </el-input>
  100. </div>
  101. </div>
  102. <span slot="footer" class="dialog-footer">
  103. <el-button class="commonbutton" type="primary" @click="seq--">上一步</el-button>
  104. <el-button class="commonbutton" type="primary" @click="seq++">下一步</el-button>
  105. <el-button class="commonbutton" @click="seq = -1">关闭</el-button>
  106. </span>
  107. </el-dialog>
  108. <el-dialog title="选择待导入的结果信息,并执行导入" :visible.sync="dialogGroup.fileDataOpr" width="700px" :show-close="false"
  109. :close-on-click-modal="false"> <!--:append-to-body="true" -->
  110. <div style="height:400px;">
  111. <div style="margin-top: -10px;">
  112. 1在标题列右击鼠标可设置实际导入数据的列名标题列有横向排列时项目列不显示<br>
  113. 2按住 Ctr1 Shift 键可进行多选<br>
  114. </div>
  115. <div style="margin: 5px 0px 0px;" @contextmenu.prevent=""> <!-- -->
  116. <el-table :data="excelData" border ref="excelData" @header-contextmenu="headerContextmenu" height="360"
  117. @row-click="chooseDataImport" row-key="id" highlight-current-row size="small"
  118. :row-class-name="handleRowClassName">
  119. <el-table-column type="index" align="center" label="序号" min-width="40" />
  120. <!--
  121. <el-table-column prop="choosed" align="center" label="选中" min-width="40"/>
  122. -->
  123. <el-table-column v-for="(item, index) in excelCols" :key="`col_${index}`" :prop="item.dispLabel"
  124. align="center" :label="(item.val ? '√ ' : '') + (item.dataLabel || item.dispLabel)"
  125. :min-width="100 + index" />
  126. </el-table>
  127. </div>
  128. </div>
  129. <span slot="footer" class="dialog-footer">
  130. <div style="display: flex;justify-content: space-between;">
  131. <div style="display: flex;">
  132. <el-button class="commonbutton" type="primary" @click="btnChoose('excelData', 'all')">全选</el-button>
  133. <el-button class="commonbutton" type="primary" @click="btnChoose('excelData')">取消全选</el-button>
  134. </div>
  135. <div style="display: flex;">
  136. <el-button class="commonbutton" type="primary" @click="seq--">上一步</el-button>
  137. <el-button class="commonbutton" type="primary" @click="btnImport">确定导入</el-button>
  138. <el-button class="commonbutton" @click="seq = -1">关闭</el-button>
  139. </div>
  140. </div>
  141. </span>
  142. </el-dialog>
  143. <!-- 通用进度条 -->
  144. <el-dialog title="数据处理中……" :visible.sync="elProgress.display" width="700px" height="400" :show-close="false"
  145. :close-on-click-modal="false" :append-to-body="true">
  146. <ElProgressOCX />
  147. </el-dialog>
  148. </div>
  149. </div>
  150. </template>
  151. <script>
  152. import moment from "moment"
  153. import { mapState, mapActions } from "vuex";
  154. import { read, readFile, utils } from "xlsx";
  155. import FileSaver from 'file-saver';
  156. import { getapi, postapi, putapi, deletapi } from "@/api/api";
  157. import { getPagePriv, checkPagePriv, deepCopy, arrayExistObj, arrayFilter, dddw, tcdate } from '../../utlis/proFunc';
  158. import ElProgressOCX from "../../components/report/ElProgressOCX.vue";
  159. export default {
  160. components: {
  161. ElProgressOCX,
  162. },
  163. data() {
  164. return {
  165. startPoint: -1, // 多选起点 -1 表示未选择
  166. endPoint: -1, // 多选终点 -1 表示未选择
  167. rClickRow: null, //右击的行
  168. rClickColumn: null, //右击的列(预留)
  169. dialogGroup: {
  170. fileReadme: true, //导入EXCEL注意事项
  171. fileChoose: false, //选择文件
  172. fileDataOpr: false, //文件数据分析操作 及导入
  173. },
  174. oldSeq: -2, //旧的步骤(辅助区分上一步,下一步)
  175. seq: 10, //当前显示窗口
  176. excelCols: [{ dispLabel: '', val: '', dataLabel: '' }], //excel数据列名 {dispLabel:'',val:'',dataLabel:''}
  177. importCols: [], // 实际导入的列(即有设置与 dataCols 匹配的列)
  178. excelData: [], // excel表格数据
  179. choosedData: [], // 选中的待导入的数据
  180. toApiBodys: [], // api需要的数据格式
  181. dataCols: [
  182. { dispLabel: '不设置', val: '' },
  183. { dispLabel: '条码号', val: 'barcode' },
  184. { dispLabel: '姓名', val: 'patientName' },
  185. { dispLabel: '标本号', val: 'sampleNo' },
  186. { dispLabel: '仪器通道', val: 'deviceChannel' },
  187. { dispLabel: '检查时间', val: 'checkDate' },
  188. { dispLabel: '项目', val: 'itemName' },
  189. { dispLabel: '结果', val: 'itemResult' }
  190. ],
  191. preBarcode: '', // 上一人条码号
  192. curCheckDate: '', // 当前检查时间
  193. tableData: [], //导入数据状态显示
  194. workBook: null, //EXCEL 工作薄
  195. sheetNames: [], //EXCEL 工作薄中的表单 {sheetName:}
  196. readDataOpts: {
  197. file: '', //选中的文件名
  198. sheetNameChoosed: '', //当前选中的表格
  199. titleRow: 1, //标题行
  200. resultMode: 'V', //Excel 结果排列方式 H:横向 V:纵向
  201. },
  202. readDataOptsInit: {},
  203. dataImportOpts: {
  204. barcodeMode: '1', // 条码方式: '0'/人员条码;'1'/项目条码
  205. startCheckDate: '', // 检查起始时间
  206. checkInterval: 30, // 标本间隔时间(秒)
  207. },
  208. dataImportOptsInit: {},
  209. pickerOptions: {
  210. shortcuts: [{
  211. text: '今天',
  212. onClick(picker) {
  213. picker.$emit('pick', new Date());
  214. }
  215. }, {
  216. text: '昨天',
  217. onClick(picker) {
  218. const date = new Date();
  219. date.setTime(date.getTime() - 3600 * 1000 * 24);
  220. picker.$emit('pick', date);
  221. }
  222. }, {
  223. text: '前天',
  224. onClick(picker) {
  225. const date = new Date();
  226. date.setTime(date.getTime() - 3600 * 1000 * 48);
  227. picker.$emit('pick', date);
  228. }
  229. }, {
  230. text: '一周前',
  231. onClick(picker) {
  232. const date = new Date();
  233. date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
  234. picker.$emit('pick', date);
  235. }
  236. }]
  237. },
  238. };
  239. },
  240. //组件创建完成,一般页面初始布局放在这里
  241. created() {
  242. this.seq = 10
  243. this.readDataOptsInit = Object.assign({}, this.readDataOpts)
  244. this.dataImportOptsInit = Object.assign({}, this.dataImportOpts)
  245. },
  246. //页面挂载完成,一般页面渲染数据放在这里
  247. mounted() {
  248. this.dictInit()
  249. },
  250. computed: {
  251. ...mapState(["window", "dict", "elProgress", "patientRegister", "customerOrg"]),
  252. },
  253. methods: {
  254. dddw, moment, checkPagePriv,
  255. dictInit() {
  256. },
  257. //清空进度数据数据
  258. clearProcess() {
  259. let elo = document.getElementById('fileNames')
  260. if (elo) elo.value = ''; // 清空选择的文件
  261. this.workBook = null //EXCEL 工作薄
  262. this.sheetNames = [] //EXCEL 工作薄中的表单 {sheetName:}
  263. this.readDataOpts = Object.assign({}, this.readDataOptsInit)
  264. this.dataImportOpts = Object.assign({}, this.dataImportOptsInit)
  265. this.startPoint = -1 // 多选起点 -1 表示未选择
  266. this.endPoint = -1 // 多选终点 -1 表示未选择
  267. },
  268. //第一次点下一步
  269. btnFirst() {
  270. if (!this.peisid || this.peisid == 'null') {
  271. this.$message.warning("该用户未选归属体检中心,不能执行此操作!");
  272. return
  273. }
  274. this.seq = 11
  275. },
  276. // 导入过程 上一步,下一步
  277. async btnProcess(seq) {
  278. let keys = Object.keys(this.dialogGroup)
  279. let count = 0 //选中待导入的人员数
  280. if (seq == 9 || seq == 29) {
  281. this.workBook = null
  282. this.sheetNameChoosed = ''
  283. seq = 0
  284. }
  285. if (seq == 19) seq = 0
  286. // console.log('keys',keys)
  287. switch (seq) {
  288. case -1:
  289. //关闭所有弹窗
  290. keys.forEach(e => {
  291. this.dialogGroup[e] = false
  292. })
  293. this.clearProcess()
  294. break;
  295. case 10:
  296. this.excelCols = []
  297. this.tableData = []
  298. this.startPoint = -1 // 多选起点 -1 表示未选择
  299. this.endPoint = -1 // 多选终点 -1 表示未选择
  300. //显示 EXCEL 导入
  301. keys.forEach(e => {
  302. if (e == 'fileReadme') {
  303. this.dialogGroup[e] = true
  304. } else {
  305. this.dialogGroup[e] = false
  306. }
  307. })
  308. break;
  309. case 11:
  310. //显示 文件选择 窗口
  311. keys.forEach(e => {
  312. if (e == 'fileChoose') {
  313. this.dialogGroup[e] = true
  314. } else {
  315. this.dialogGroup[e] = false
  316. }
  317. })
  318. break;
  319. case 12:
  320. // 从后面退回时,无需读数据
  321. if (this.oldSeq < seq) {
  322. if (!this.readData()) {
  323. this.seq--
  324. break;
  325. }
  326. }
  327. //显示 数据分析操作 窗口
  328. if (!this.dataImportOpts.startCheckDate) {
  329. this.$message.warning({ showClose: true, message: '请选择起始检查时间' })
  330. break;
  331. }
  332. if (!this.dataImportOpts.checkInterval) {
  333. this.$message.warning({ showClose: true, message: '请填写标本间隔时间' })
  334. break;
  335. } else {
  336. try {
  337. Number(this.dataImportOpts.checkInterval)
  338. } catch (error) {
  339. this.$message.warning({ showClose: true, message: '标本间隔时间只能填写数字' })
  340. break;
  341. }
  342. }
  343. keys.forEach(e => {
  344. if (e == 'fileDataOpr') {
  345. this.dialogGroup[e] = true
  346. } else {
  347. this.dialogGroup[e] = false
  348. }
  349. })
  350. break;
  351. case 13:
  352. count = 0
  353. this.excelData.forEach(e => {
  354. if (e.choosed) count++
  355. })
  356. if (count == 0) {
  357. this.$message.warning("未选中要导入的记录")
  358. this.seq--
  359. break;
  360. }
  361. //显示 文件数据导入前参数设定 窗口
  362. keys.forEach(e => {
  363. if (e == 'fileDataOpts') {
  364. this.dialogGroup[e] = true
  365. } else {
  366. this.dialogGroup[e] = false
  367. }
  368. })
  369. break;
  370. default:
  371. break;
  372. }
  373. },
  374. //导入完后,导入状态显示
  375. importRowClassName({ row, rowIndex }) {
  376. if (row.importState == '导入失败') {
  377. return "danger";
  378. } else {
  379. return "";
  380. }
  381. },
  382. //多选 颜色标记
  383. handleRowClassName({ row, rowIndex }) {
  384. if (row.choosed) {
  385. return "current-row";
  386. } else {
  387. return "";
  388. }
  389. },
  390. // 导入完后,状态统计
  391. getSummaries(param) {
  392. const { columns, data } = param;
  393. const sumCol = [2]; //需合计的列
  394. const sums = [];
  395. let success = 0, fail = 0;
  396. columns.forEach((column, index) => {
  397. //显示合计列
  398. if (index === 1) {
  399. sums[index] = "导入合计";
  400. return;
  401. }
  402. //不合计的列
  403. if (sumCol.indexOf(index) == -1) {
  404. sums[index] = "";
  405. return;
  406. }
  407. data.forEach((item) => {
  408. console.log('item,column.property', item, column.property)
  409. if (item[column.property]) {
  410. fail++
  411. } else {
  412. success++
  413. }
  414. });
  415. });
  416. sums[2] = `成功:${success} 条,失败:${fail} 条。`
  417. return sums;
  418. },
  419. //清除所选文件
  420. fileGetFocus(e) {
  421. console.log('fileGetFocus(e)', e)
  422. e.value = '' //未起作用的
  423. // e.target.files = []
  424. },
  425. // 获取选择的文件
  426. changeFileChoose(e) {
  427. if (e.target.files.length <= 0) return;
  428. // console.log('file',e.target.files[0])
  429. this.readDataOpts.file = e.target.files[0];
  430. // 调用导入Excel文件的方法
  431. // File {name: 'vulkan-1.dll',
  432. // console.log('file',file,file.type)
  433. let fileName = this.readDataOpts.file.name.split('.')
  434. let fileNameExt = fileName[fileName.length - 1].toLowerCase()
  435. if (fileNameExt != 'xls' && fileNameExt != 'xlsx') {
  436. this.$message.warning("你选的文件可能不是标准的Excel文件!")
  437. }
  438. this.readFile(this.readDataOpts.file);
  439. },
  440. //sheetjs 读取文件
  441. readFile(file) {
  442. const reader = new FileReader();
  443. // reader.readAsArrayBuffer(file);
  444. // 定义读取文件
  445. reader.onload = (e) => {
  446. let data = e.target.result;
  447. // let typedArray = new Uint8Array(data);
  448. // var workBook = read(typedArray,{type:'array'})
  449. // this.workBook = read(data,{type:'binary',cellDates:true}) //日期将转成 标准 日期格式,显示会 undefined
  450. this.workBook = read(data, { type: 'binary' })
  451. // var workBook = readFile(file); //后端读法
  452. this.sheetNames = []
  453. this.workBook.SheetNames.forEach(e => {
  454. this.sheetNames.push({ sheetName: e })
  455. })
  456. this.$nextTick(() => {
  457. this.$refs['sheetNames'].setCurrentRow(this.sheetNames[0]);
  458. this.chooseSheetName(this.sheetNames[0])
  459. })
  460. }
  461. // 错误处理
  462. reader.onerror = function (event) {
  463. // 读取失败时执行的代码
  464. console.error("File could not be read!" + event.target.error);
  465. };
  466. reader.readAsBinaryString(file);
  467. },
  468. //选择要导入的Excel表单名
  469. chooseSheetName(row) {
  470. this.readDataOpts.sheetNameChoosed = row.sheetName
  471. },
  472. //读取数据
  473. readData() {
  474. let ret = 1
  475. if (!this.readDataOpts.file || !this.workBook || !this.readDataOpts.sheetNameChoosed) {
  476. this.$message.warning("请选择文件及要导入的表格")
  477. return 0
  478. }
  479. if (this.readDataOpts.titleRow <= 0) {
  480. this.$message.warning("标题行不能小于1")
  481. return 0
  482. }
  483. let worksheet = this.workBook.Sheets[this.readDataOpts.sheetNameChoosed];
  484. //分析标题信息
  485. let head = utils.sheet_to_json(worksheet, { header: 1 });
  486. this.excelCols = []
  487. head[this.readDataOpts.titleRow - 1].forEach(e => {
  488. let lfind = arrayExistObj(this.dataCols, 'dispTitle', e), val = '', dataLabel = '';
  489. if (lfind > -1) {
  490. val = this.dataCols[lfind].val
  491. dataLabel = this.dataCols[lfind].dataLabel
  492. }
  493. this.excelCols.push({
  494. dispLabel: e,
  495. val,
  496. dataLabel
  497. })
  498. })
  499. //console.log('head,',head,this.excelCols)
  500. this.excelData = utils.sheet_to_json(worksheet, { raw: false, range: this.readDataOpts.titleRow - 1 }); //raw:false,range:1 从第1行开始读取
  501. this.excelData.forEach((e, index) => {
  502. e.choosed = false;
  503. e.index = index;
  504. e.id = 'excelData' + index;
  505. });
  506. //console.log('this.excelCols',this.excelCols)
  507. // console.log('sheet_to_json excelData',this.excelData)
  508. // 导入后,批量分析Excel列 与 数据字段 的匹配关系
  509. this.parseExcelData()
  510. return ret
  511. },
  512. //选择要导入的数据
  513. chooseDataImport(row) {
  514. // console.log("this.excelData",this.excelData);
  515. // 按住了shift键
  516. if (this.window.shift) {
  517. //清除所有选择
  518. this.excelData.forEach((e, index) => {
  519. e.choosed = false;
  520. e.index = index;
  521. });
  522. if (this.startPoint == -1) {
  523. this.excelData[row.index].choosed = true;
  524. this.startPoint = row.index;
  525. } else {
  526. if (this.startPoint > row.index) {
  527. for (let i = row.index; i <= this.startPoint; i++) {
  528. this.excelData[i].choosed = true;
  529. }
  530. } else if (this.startPoint <= row.index) {
  531. for (let i = this.startPoint; i <= row.index; i++) {
  532. this.excelData[i].choosed = true;
  533. }
  534. }
  535. }
  536. } else if (this.window.ctrl) { // 按住了ctrl 键
  537. this.excelData[row.index].choosed = true;
  538. if (this.startPoint == -1) {
  539. this.startPoint = row.index;
  540. }
  541. } else {
  542. // 未按住了ctrl 、shift 键
  543. //清除所有选择
  544. console.log("清除所有选择");
  545. this.excelData.forEach((e, index) => {
  546. e.choosed = false;
  547. e.index = index;
  548. });
  549. // console.log(this.excelData,row.index);
  550. // console.log(this.excelData[row.index].choosed);
  551. this.excelData[row.index].choosed = true;
  552. this.startPoint = row.index;
  553. }
  554. },
  555. // 全选(取消全选)
  556. btnChoose(refName, type) {
  557. let choosed = false
  558. if (type && type == 'all') {
  559. choosed = true
  560. }
  561. this[refName].forEach(e => {
  562. e.choosed = choosed
  563. if (choosed) {
  564. this.$refs[refName].setCurrentRow(e)
  565. }
  566. })
  567. if (!choosed) {
  568. this.$refs[refName].setCurrentRow()
  569. }
  570. },
  571. //右击标题
  572. headerContextmenu(column, event) {
  573. let chooseCol = column.minWidth - 100
  574. let items = [] //菜单
  575. this.dataCols.forEach(e => {
  576. if (!(this.readDataOpts.resultMode == 'H' && (e.val == 'itemResult' || e.val == 'itemName'))) {
  577. items.push({
  578. label: e.dispLabel,
  579. onClick: () => {
  580. this.setColumn(chooseCol, e)
  581. },
  582. })
  583. }
  584. })
  585. //右击显示的菜单
  586. this.$contextmenu({
  587. items,
  588. event,
  589. //x: event.clientX,
  590. //y: event.clientY,
  591. customClass: "custom-class",
  592. zIndex: 30000, //够大再能在最上面显示
  593. minWidth: 80,
  594. height: 20
  595. });
  596. return false;
  597. },
  598. //设置真实数据字段列
  599. setColumn(oldColNum, newColObj) {
  600. // console.log('oldColNum,newColObj',oldColNum,newColObj)
  601. //如果数据字段已设置过,则将之前设置的先清空
  602. if (newColObj.dispLabel != "不设置") {
  603. let lfind = arrayExistObj(this.excelCols, 'dataLabel', newColObj.dispLabel)
  604. if (lfind > -1) {
  605. this.excelCols[lfind].dataLabel = ''
  606. this.excelCols[lfind].val = ''
  607. }
  608. }
  609. this.excelCols[oldColNum].dataLabel = newColObj.dispLabel
  610. this.excelCols[oldColNum].val = newColObj.val
  611. // console.log('newColObj.val',newColObj.val)
  612. let tempDate = ''
  613. if (newColObj.val == 'birthDate') {
  614. this.excelData.forEach(e => {
  615. tempDate = e[this.excelCols[oldColNum].dispLabel]
  616. // console.log('birthDate',tempDate)
  617. e[this.excelCols[oldColNum].dispLabel] = tempDate ? moment(new Date(tempDate)).format('yyyy-MM-DD') : ''
  618. })
  619. }
  620. },
  621. // 选择EXCEL文件后,批量分析Excel列 与 数据字段 的匹配关系
  622. parseExcelData() {
  623. let lfind = -1
  624. this.excelCols.forEach((e, i) => {
  625. lfind = arrayExistObj(this.dataCols, 'dispLabel', e.dispLabel)
  626. if (lfind > -1) this.setColumn(i, this.dataCols[lfind])
  627. });
  628. },
  629. // 确定导入数据库
  630. btnImport() {
  631. this.elProgress.display = true;
  632. this.elProgress.percentage = 0;
  633. this.importCols = []
  634. this.excelCols.forEach(e => {
  635. if (e.val) {
  636. this.importCols.push(e)
  637. }
  638. })
  639. this.tableData = [] // 记录导入后结果状态
  640. this.choosedData = [] //选中待导入的数据
  641. this.excelData.forEach(e => {
  642. if (e.choosed) this.choosedData.push(e)
  643. });
  644. //导入进行中
  645. this.importing()
  646. // 开始导入时,清除选择的 文件
  647. document.getElementById('fileNames').value = '';
  648. },
  649. //导入进行时
  650. async importing() {
  651. // 将Excel的数据转换成接口的数据
  652. this.excelDataToApiBodys(this.choosedData)
  653. console.log('this.toApiBodys', this.toApiBodys)
  654. // 旧接口:/api/app/patientregister/createpatientregisterexcel
  655. for (let i = 0; i < this.toApiBodys.length; i++) {
  656. this.elProgress.percentage = Math.floor(
  657. ((i + 1) * 100) / this.choosedData.length
  658. );
  659. let res = await postapi('/api/app/patientregister/CreatePatientRegisterFromExcel', this.toApiBodys[i])
  660. if (res.code >= 0) {
  661. this.tableData.push(Object.assign({ importState: '导入成功' }, this.toApiBodys[i]))
  662. } else {
  663. this.tableData.push(Object.assign({ importState: '导入失败', importDes: res.message }, this.toApiBodys[i]))
  664. }
  665. }
  666. // 结束导入
  667. this.elProgress.display = false;
  668. this.seq = -1
  669. },
  670. //将Excel的数据转换成接口的数据
  671. excelDataToApiBodys(ExcelAllChoosedData) {
  672. this.toApiBodys = []
  673. ExcelAllChoosedData.forEach(e => {
  674. this.excelDataToApiBody(e)
  675. });
  676. },
  677. //将Excel的数据转换成接口的数据
  678. excelDataToApiBody(ExcelData) {
  679. console.log('ExcelData,this.excelCols', ExcelData, this.excelCols)
  680. let body = {
  681. barcodeMode: this.dataImportOpts.barcodeMode
  682. }
  683. // 纵向数据
  684. if (this.readDataOpts.resultMode == 'V') {
  685. this.excelCols.forEach(e => {
  686. if (e.val) {
  687. switch (e.val) {
  688. // case 'age':
  689. // if (ExcelData[e.dispLabel]) body[e.val] = parseInt(ExcelData[e.dispLabel])
  690. // break;
  691. // case 'birthDate':
  692. // if(ExcelData[e.dispLabel]) body[e.val] = moment(new Date(ExcelData[e.dispLabel])).format('yyyy-MM-DD')
  693. // break;
  694. // case 'poisons':
  695. // if (ExcelData[e.dispLabel]) body[e.val] = ExcelData[e.dispLabel].replaceAll(",", ",").split(",")
  696. // break;
  697. default:
  698. body[e.val] = ExcelData[e.dispLabel]
  699. break;
  700. }
  701. }
  702. })
  703. if (!body.checkDate) {
  704. // console.log('body.checkDate,this.preBarcode,this.curCheckDate',body.checkDate,this.preBarcode,this.curCheckDate)
  705. if (!this.preBarcode) {
  706. this.preBarcode = body.barcode
  707. this.curCheckDate = this.dataImportOpts.startCheckDate
  708. }
  709. if (this.preBarcode != body.barcode) {
  710. this.preBarcode = body.barcode
  711. this.curCheckDate = moment(new Date(new Date(this.curCheckDate).getTime() + Number(this.dataImportOpts.checkInterval) * 1000)).format('yyyy-MM-DD HH:mm:ss')
  712. }
  713. body.checkDate = this.curCheckDate
  714. }
  715. if (body.deviceChannel == undefined) delete body.deviceChannel
  716. if (body.sampleNo == undefined) delete body.sampleNo
  717. let lfind = arrayExistObj(this.toApiBodys,'barcode',body.barcode)
  718. if(lfind == -1){
  719. this.toApiBodys.push(Object.assign({},body,{details:[{ itemName: body.itemName, itemResult: body.itemResult, checkDate: body.checkDate }]}))
  720. }else{
  721. this.toApiBodys[this.toApiBodys.length - 1].details.push({ itemName: body.itemName, itemResult: body.itemResult, checkDate: body.checkDate })
  722. }
  723. } else {
  724. // 横向数据
  725. // { dataLabel: "", dispLabel: "P LCR", val: "" }
  726. body = {
  727. barcode: ExcelData['条码号'],
  728. barcodeMode: this.dataImportOpts.barcodeMode,
  729. patientName: ExcelData['姓名'],
  730. sampleNo: ExcelData['标本号'],
  731. deviceChannel: ExcelData['仪器通道'],
  732. checkDate: ExcelData['检查时间'],
  733. details: []
  734. }
  735. if (!body.checkDate) {
  736. // console.log('body.checkDate,this.preBarcode,this.curCheckDate',body.checkDate,this.preBarcode,this.curCheckDate)
  737. if (!this.preBarcode) {
  738. this.preBarcode = body.barcode
  739. this.curCheckDate = this.dataImportOpts.startCheckDate
  740. }
  741. if (this.preBarcode != body.barcode) {
  742. this.preBarcode = body.barcode
  743. this.curCheckDate = moment(new Date(new Date(this.curCheckDate).getTime() + Number(this.dataImportOpts.checkInterval) * 1000)).format('yyyy-MM-DD HH:mm:ss')
  744. }
  745. body.checkDate = this.curCheckDate
  746. }
  747. if (body.deviceChannel == undefined) delete body.deviceChannel
  748. if (body.sampleNo == undefined) delete body.sampleNo
  749. // [
  750. // { dispLabel: '条码号', val: 'barcode' },
  751. // { dispLabel: '姓名', val: 'patientName' },
  752. // { dispLabel: '标本号', val: 'sampleNo' },
  753. // { dispLabel: '仪器通道', val: 'deviceChannel' },
  754. // { dispLabel: '检查时间', val: 'checkDate' },
  755. // { dispLabel: '项目', val: 'itemName' },
  756. // { dispLabel: '结果', val: 'itemResult' }
  757. // ]
  758. this.excelCols.forEach(e => {
  759. if (!e.dataLabel && !e.val) {
  760. body.details.push({ itemName: e.dispLabel, itemResult: ExcelData[e.dispLabel], checkDate: body.checkDate })
  761. }
  762. })
  763. this.toApiBodys.push(body)
  764. }
  765. },
  766. //选择要导入的数据
  767. rowClickPrList(row) {
  768. // console.log("this.excelData",this.excelData);
  769. // 按住了shift键
  770. if (this.window.shift) {
  771. //清除所有选择
  772. this.prList.forEach((e, index) => {
  773. e.choosed = false;
  774. e.index = index;
  775. });
  776. if (this.startPoint == -1) {
  777. this.prList[row.index].choosed = true;
  778. this.startPoint = row.index;
  779. } else {
  780. if (this.startPoint > row.index) {
  781. for (let i = row.index; i <= this.startPoint; i++) {
  782. this.prList[i].choosed = true;
  783. }
  784. } else if (this.startPoint <= row.index) {
  785. for (let i = this.startPoint; i <= row.index; i++) {
  786. this.prList[i].choosed = true;
  787. }
  788. }
  789. }
  790. } else if (this.window.ctrl) { // 按住了ctrl 键
  791. this.prList[row.index].choosed = true;
  792. if (this.startPoint == -1) {
  793. this.startPoint = row.index;
  794. }
  795. } else {
  796. // 未按住了ctrl 、shift 键
  797. //清除所有选择
  798. console.log("清除所有选择");
  799. this.prList.forEach((e, index) => {
  800. e.choosed = false;
  801. e.index = index;
  802. });
  803. // console.log(this.excelData,row.index);
  804. // console.log(this.excelData[row.index].choosed);
  805. this.prList[row.index].choosed = true;
  806. this.startPoint = row.index;
  807. }
  808. },
  809. //通用导出
  810. btnExport(elId) {
  811. let table = document.getElementById(elId);
  812. let tableData = table.innerHTML
  813. let fileName = moment(new Date()).format('yyyyMMDDHHmmss') + '.xls'
  814. let blob = new Blob([tableData], { type: "text/plain;charset=utf-8" });
  815. FileSaver.saveAs(blob, fileName);
  816. },
  817. },
  818. watch: {
  819. "seq": {
  820. immediate: true, // 立即执行
  821. // // deep: true, // 深度监听复杂类型内变化
  822. handler(newVal, oldVal) {
  823. console.log('watch:seq:', newVal, oldVal)
  824. if (!oldVal && oldVal != 0) {
  825. this.oldSeq = -2
  826. } else {
  827. this.oldSeq = oldVal
  828. }
  829. if (newVal != oldVal) {
  830. this.btnProcess(newVal);
  831. }
  832. }
  833. },
  834. "readDataOpts.titleRow": {
  835. // immediate: true, // 立即执行
  836. // deep: true, // 深度监听复杂类型内变化
  837. handler(newVal, oldVal) {
  838. console.log('watch:readDataOpts.titleRow:', newVal, oldVal)
  839. if (newVal && newVal != oldVal) {
  840. if (this.seq == 11) this.readData()
  841. }
  842. }
  843. },
  844. //所选体检次数改变时,自动获取登记日期
  845. "query.customerOrgRegister.id": {
  846. // immediate: true, // 立即执行
  847. // deep: true, // 深度监听复杂类型内变化
  848. handler(newVal, oldVal) {
  849. if (newVal && newVal != oldVal) {
  850. this.query.startDate = new Date(this.query.customerOrgRegister.beginTime)
  851. this.query.endDate = this.query.customerOrgRegister.isComplete == 'N' ? new Date() : new Date(this.query.customerOrgRegister.endTime)
  852. }
  853. }
  854. },
  855. //选体检新体检次数改变时,获取分组数据
  856. "customerOrgRegister.id": {
  857. // immediate: true, // 立即执行
  858. // deep: true, // 深度监听复杂类型内变化
  859. handler(newVal, oldVal) {
  860. if (newVal && newVal != oldVal) {
  861. this.getGroups(newVal)
  862. }
  863. }
  864. },
  865. },
  866. };
  867. </script>
  868. <style scoped>
  869. @import '../../assets/css/global_button.css';
  870. @import '../../assets/css/global_card.css';
  871. @import '../../assets/css/global_dialog.css';
  872. @import '../../assets/css/global_form.css';
  873. @import '../../assets/css/global_input.css';
  874. @import '../../assets/css/global_table.css';
  875. @import '../../assets/css/global_menu.css';
  876. @import '../../assets/css/global.css';
  877. .spanLeftClass {
  878. margin-top: 6px;
  879. width: 70px;
  880. }
  881. .spanMidClass {
  882. text-align: center;
  883. margin-top: 6px;
  884. width: 50px;
  885. }
  886. /* type=number 显示微调按钮 */
  887. ::v-deep input[type="number"]::-webkit-inner-spin-button,
  888. input[type="number"]::-webkit-outer-spin-button {
  889. -webkit-appearance: button !important;
  890. margin: 0 -12px 0 0 !important;
  891. }
  892. ::v-deep .el-table__header th {
  893. font-family: "Microsoft YaHei";
  894. }
  895. </style>