index.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722
  1. <template>
  2. <div class="pages">
  3. <div class="block">
  4. <div class="blockCon">
  5. <div class="lefts">
  6. <el-dropdown>
  7. <el-button :class="formData.year.name ? 'color-btn' : ''">
  8. {{ formData.year.name || '按年' }}
  9. <i class="el-icon-arrow-down el-icon--right"></i>
  10. </el-button>
  11. <el-dropdown-menu slot="dropdown">
  12. <el-dropdown-item v-for="(item, index) in yearList" :key="index" @click.native="funSeleterYear(item)">{{ item.name }}</el-dropdown-item>
  13. </el-dropdown-menu>
  14. </el-dropdown>
  15. <el-dropdown>
  16. <el-button :disabled="formData.year.name == ''" :class="formData.quarter.name ? 'color-btn' : ''">
  17. {{ formData.quarter.name || '按季' }}
  18. <i class="el-icon-arrow-down el-icon--right"></i>
  19. </el-button>
  20. <el-dropdown-menu slot="dropdown">
  21. <el-dropdown-item v-for="(item, index) in quarterList" :key="index" @click.native="funSeleterQuarter(item)">{{ item.name }}</el-dropdown-item>
  22. </el-dropdown-menu>
  23. </el-dropdown>
  24. <div class="selects">
  25. <el-date-picker v-model="formData.startTime" type="date" format="yyyy 年 MM 月 dd 日" value-format="yyyyMMdd" placeholder="开始日期"></el-date-picker>
  26. <el-date-picker
  27. v-model="formData.endTime"
  28. type="date"
  29. style="margin-left: 10px"
  30. format="yyyy 年 MM 月 dd 日"
  31. value-format="yyyyMMdd"
  32. placeholder="结束日期"
  33. ></el-date-picker>
  34. </div>
  35. <el-button class="btn1" type="primary" @click="funQuery">查询</el-button>
  36. </div>
  37. </div>
  38. </div>
  39. <div class="cardBox">
  40. <Title :title="'运行病历数量'" />
  41. <div class="contentBox">
  42. <div class="left">
  43. <div class="l">
  44. <div class="i" @click="goto('/caseNumber')">
  45. <div class="ba">病案数量</div>
  46. <div class="num cpoin">{{ countsData.case_total }}</div>
  47. </div>
  48. <div class="i" @click="goto('/defectNumber')" style="background: #38a1f1">
  49. <div class="ba">缺陷病案</div>
  50. <div class="num">{{ countsData.defect_case_total }}</div>
  51. </div>
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. <!-- 科室排名 -->
  57. <div class="chart">
  58. <Title :title="'科室排名'" />
  59. <div id="myChart1" style="width: 100%; height: 600px"></div>
  60. </div>
  61. <!-- 缺陷问题 -->
  62. <div class="chart">
  63. <Title :title="'缺陷问题'" />
  64. <el-form :inline="true" :model="caseSearchData" class="demo-form-inline">
  65. <el-form-item label="">
  66. <el-select v-model="caseSearchData.department" filterable clearable placeholder="科室">
  67. <el-option v-for="(item, index) in departmentList" :label="item.name" :value="item.name" :key="index"></el-option>
  68. </el-select>
  69. </el-form-item>
  70. <el-form-item>
  71. <el-button type="primary" @click="getCaseList">查询</el-button>
  72. </el-form-item>
  73. </el-form>
  74. <ProblemTableBoxVue :data="caseList" />
  75. </div>
  76. <!-- 医师排名 -->
  77. <div class="chart">
  78. <div class="title-box">
  79. <Title :title="'医师排名'" />
  80. <div class="xz-btn" @click="handleExport">下载</div>
  81. </div>
  82. <div class="medicalRecord-box">
  83. <div class="medicalRecord-list-box">
  84. <MedicalRecordTableBoxVue :data="medicalRecordList" />
  85. <!-- 分页控制 -->
  86. <mPagination v-if="medicalRecordList && medicalRecordList.length !== 0" :data="paginationData" @pageChangeEvent="pageHasChanged"></mPagination>
  87. </div>
  88. <div id="myChart2" style="width: 100%; height: 400px"></div>
  89. </div>
  90. </div>
  91. </div>
  92. </template>
  93. <script>
  94. import * as echarts from 'echarts';
  95. import { mapGetters } from 'vuex';
  96. import Title from '@/components/Title';
  97. import ProblemTableBoxVue from './components/ProblemTableBox.vue';
  98. import MedicalRecordTableBoxVue from './components/MedicalRecordTableBox.vue';
  99. import mPagination from '@/components/m-pagination';
  100. import { medicalRecordDoctorExport } from '@/api/excel'
  101. export default {
  102. components: {
  103. Title,
  104. ProblemTableBoxVue,
  105. MedicalRecordTableBoxVue,
  106. mPagination
  107. },
  108. name: 'Dashboard',
  109. computed: {
  110. ...mapGetters(['name']),
  111. },
  112. data() {
  113. return {
  114. formData: {
  115. rangeDate: [],
  116. chooseDate: '',
  117. startTime: '',
  118. endTime: '',
  119. year: {
  120. name: '',
  121. },
  122. month: {
  123. name: '',
  124. },
  125. quarter: {
  126. name: '',
  127. },
  128. problem: 'all',
  129. defectFelg: 'all',
  130. type: '1',
  131. },
  132. homeData: {},
  133. quarterList: [],
  134. monthList: [],
  135. yearList: [],
  136. countsData: {
  137. case_total: 0,
  138. defect_case_total: 0,
  139. },
  140. caseSearchData: {
  141. department: ''
  142. },
  143. caseList: [],
  144. departmentList: [],
  145. doctorList:[], // 医生列表
  146. medicalRecordList: [], // 医师排名
  147. // 分页数据
  148. paginationData: {
  149. total: 10,
  150. currentPage: 1,
  151. pageSize: 10,
  152. },
  153. };
  154. },
  155. mounted() {
  156. this.storageSet('start_time', '');
  157. this.storageSet('end_time', '');
  158. this.formData.chooseDate = '30';
  159. this.chooseTime(this.formData.chooseDate);
  160. if (this.storageGet('homeFrom')) {
  161. this.formData = this.storageGet('homeFrom');
  162. this.storageRemove('homeFrom');
  163. }
  164. this.getDepartmentList()
  165. this.funQuery();
  166. this.selectInfo();
  167. },
  168. beforeRouteEnter(to, from, next) {
  169. next(vm => {
  170. // 回到原来的位置
  171. const position = JSON.parse(window.sessionStorage.getItem('position'))
  172. document.querySelector('.app-wrapper').scrollTop = position
  173. })
  174. },
  175. beforeRouteLeave(to, from, next) {
  176. // 保存离开页面时的位置
  177. const position = document.querySelector('.app-wrapper').scrollTop
  178. window.sessionStorage.setItem('position', JSON.stringify(position))
  179. next()
  180. },
  181. methods: {
  182. // 获取医生列表
  183. getDoctorList(){
  184. this.$axios2.post('/case-quality/doctor_list').then(res => {
  185. this.doctorList = res.data;
  186. });
  187. },
  188. // 缺陷病例统计
  189. getAnalysis(){
  190. this.$axios2.post('/case-quality/analysis').then(res => {
  191. });
  192. },
  193. // 甲乙病级病例
  194. getMedicalRecordLevel(){
  195. this.$axios2.post('/case-quality/medical_record_level').then(res => {
  196. });
  197. },
  198. // 获取部门集合
  199. getDepartmentList() {
  200. this.$axios.post('/get_omr_department_list').then(res => {
  201. this.departmentList = res.data;
  202. });
  203. },
  204. // 获取缺陷问题
  205. getCaseList() {
  206. let pramse = {
  207. start_time: this.formData.startTime,
  208. end_time: this.formData.endTime,
  209. department: this.caseSearchData.department
  210. };
  211. this.$axios.post('/case-quality/defect_issues', pramse).then(res => {
  212. this.caseList = res.data.list
  213. });
  214. },
  215. // 获取医师排名
  216. getMedicalRecordList() {
  217. let pramse = {
  218. start_time: this.formData.startTime,
  219. end_time: this.formData.endTime,
  220. page_size: this.paginationData.pageSize,
  221. page: this.paginationData.currentPage,
  222. };
  223. this.$axios2.post('/case-quality/medical_record_doctor', pramse).then(res => {
  224. let medicalRecordList = res.data.list;
  225. this.medicalRecordList = medicalRecordList;
  226. // 销毁上一次实例
  227. echarts.init(document.getElementById('myChart2')).dispose();
  228. // 构建新实例
  229. let myChart = echarts.init(document.getElementById('myChart2'));
  230. window.addEventListener('resize', function () {
  231. myChart.resize();
  232. });
  233. let data1 = [];
  234. let data2 = [];
  235. let data3 = [];
  236. medicalRecordList.forEach(ele => {
  237. data1.push(ele.key);
  238. data2.push(ele.defect_doc_count);
  239. data3.push(ele.doc_count);
  240. });
  241. myChart.setOption({
  242. // toolbox: {
  243. // feature: {
  244. // saveAsImage: {
  245. // name: '医师排名',
  246. // },
  247. // },
  248. // },
  249. tooltip: {
  250. trigger: 'axis',
  251. axisPointer: {
  252. // Use axis to trigger tooltip
  253. type: 'shadow' // 'shadow' as default; can also be 'line' or 'shadow'
  254. }
  255. },
  256. legend: {},
  257. grid: {
  258. left: '3%',
  259. right: '4%',
  260. bottom: '3%',
  261. containLabel: true
  262. },
  263. color:['#54C5A0FF','#5B8EC3FF'],
  264. xAxis: {
  265. type: 'value'
  266. },
  267. yAxis: {
  268. type: 'category',
  269. data: data1
  270. },
  271. series: [
  272. {
  273. name: '缺陷病例数',
  274. type: 'bar',
  275. stack: 'total',
  276. label: {
  277. show: true,
  278. },
  279. emphasis: {
  280. focus: 'series'
  281. },
  282. data: data2
  283. },
  284. {
  285. name: '总病例数',
  286. type: 'bar',
  287. stack: 'total',
  288. label: {
  289. show: true
  290. },
  291. emphasis: {
  292. focus: 'series'
  293. },
  294. data: data3
  295. }
  296. ]
  297. });
  298. });
  299. },
  300. pageHasChanged() {
  301. this.getMedicalRecordList();
  302. },
  303. getCounts() {
  304. let pramse = {
  305. start_time: this.formData.startTime,
  306. end_time: this.formData.endTime,
  307. };
  308. this.$axios.post('/case-quality/analysis', pramse).then(res => {
  309. this.countsData = res.data;
  310. });
  311. },
  312. funSeleterYear(val) {
  313. this.formData.year = val;
  314. this.formData.type = '1';
  315. this.formData.endTime = this.goTimeTwe(val.end);
  316. this.formData.startTime = this.goTimeTwe(val.start);
  317. },
  318. funSeleterMonth(val) {
  319. this.formData.month = val;
  320. this.formData.type = '3';
  321. this.formData.year = {
  322. name: '',
  323. };
  324. this.formData.quarter = {
  325. name: '',
  326. };
  327. this.formData.endTime = this.goTimeTwe(val.end);
  328. this.formData.startTime = this.goTimeTwe(val.start);
  329. },
  330. funSeleterQuarter(val) {
  331. this.formData.type = '2';
  332. this.formData.quarter = val;
  333. this.formData.endTime = this.formData.year.name + this.zh(val.end);
  334. this.formData.startTime = this.formData.year.name + this.zh(val.start);
  335. },
  336. zh(str) {
  337. let arr = str.split('-');
  338. return arr.join('');
  339. },
  340. selectInfo() {
  341. // let pramse = {};
  342. this.$axios.post('/selectInfo').then(res => {
  343. //问题属性 level
  344. this.quarterList = res.data.quarter;
  345. // 季度
  346. this.monthList = res.data.month;
  347. //月
  348. this.yearList = res.data.year;
  349. });
  350. },
  351. // 选择时间段
  352. chooseTime(time) {
  353. this.formData.rangeDate = this.timesCalculation(time).slice(0, 2);
  354. },
  355. // 医师排名导出
  356. handleExport() {
  357. const params = {
  358. start_time: this.formData.startTime,
  359. end_time: this.formData.endTime,
  360. page_size: this.paginationData.pageSize,
  361. page: this.paginationData.currentPage,
  362. is_export: 1
  363. }
  364. medicalRecordDoctorExport(params).then(res => {
  365. const content = res.data // 后台返回二进制数据
  366. const blob = new Blob([content])
  367. const fileName = `医师排名.csv`
  368. if ('download' in document.createElement('a')) { // 非IE下载
  369. const elink = document.createElement('a')
  370. elink.download = fileName
  371. elink.style.display = 'none'
  372. elink.href = URL.createObjectURL(blob)
  373. document.body.appendChild(elink)
  374. elink.click()
  375. URL.revokeObjectURL(elink.href) // 释放URL 对象
  376. document.body.removeChild(elink)
  377. } else { // IE10+下载
  378. navigator.msSaveBlob(blob, fileName)
  379. }
  380. })
  381. },
  382. funQuery() {
  383. //查询
  384. let type_id = '';
  385. if (this.formData.type == '1') {
  386. type_id = this.formData.year.id || '';
  387. } else if (this.formData.type == '2') {
  388. type_id = this.formData.quarter.id;
  389. } else {
  390. type_id = this.formData.month.id;
  391. }
  392. let pramse = {
  393. start_time: this.formData.startTime,
  394. end_time: this.formData.endTime,
  395. };
  396. this.storageSet('start_time', this.formData.startTime);
  397. this.storageSet('end_time', this.formData.endTime);
  398. this.storageSet('homeFrom', this.formData);
  399. this.initCharts1(pramse);
  400. this.getCounts();
  401. this.getCaseList();
  402. // 获取医师排名
  403. this.getMedicalRecordList();
  404. // 缺陷病例统计
  405. this.getAnalysis();
  406. // 甲乙病级病例
  407. this.getMedicalRecordLevel();
  408. },
  409. initCharts1(pramse) {
  410. this.$axios.post('/case-quality/ranking_department', pramse).then(res => {
  411. let dataName = [];
  412. let dataFleg = [];
  413. const total_error_medical = [];
  414. let dataNum = [];
  415. for (let item in res.data.list.slice(0, 10)) {
  416. dataName.push(res.data.list[item].name);
  417. dataFleg.push(res.data.list[item].total_medical);
  418. dataNum.push(res.data.list[item].item);
  419. total_error_medical.push(res.data.list[item].total_error_medical);
  420. }
  421. // 销毁上一次实例
  422. echarts.init(document.getElementById('myChart1')).dispose();
  423. // 构建新实例
  424. let myChart = echarts.init(document.getElementById('myChart1'));
  425. window.addEventListener('resize', function () {
  426. myChart.resize();
  427. });
  428. if (res.data.list.length) {
  429. myChart.setOption({
  430. toolbox: {
  431. feature: {
  432. saveAsImage: {
  433. name: '科室排名',
  434. },
  435. },
  436. },
  437. tooltip: {
  438. trigger: 'axis',
  439. axisPointer: {
  440. type: 'cross',
  441. crossStyle: {
  442. color: '#999',
  443. },
  444. },
  445. },
  446. legend: {
  447. show: true,
  448. },
  449. xAxis: {
  450. type: 'category',
  451. data: dataName,
  452. axisLabel: {
  453. rotate: 15,
  454. },
  455. },
  456. grid: {
  457. left: '3%',
  458. right: '3%',
  459. bottom: '3%',
  460. containLabel: true,
  461. },
  462. yAxis: {
  463. type: 'value',
  464. },
  465. series: [
  466. {
  467. data: dataFleg,
  468. type: 'bar',
  469. showBackground: true,
  470. barMaxWidth: 30,
  471. label: {
  472. show: true,
  473. position: 'top',
  474. },
  475. name: '病案数',
  476. backgroundStyle: {
  477. color: 'rgba(180, 180, 180, 0.2)',
  478. },
  479. },
  480. {
  481. data: total_error_medical,
  482. type: 'bar',
  483. showBackground: true,
  484. barMaxWidth: 30,
  485. label: {
  486. show: true,
  487. position: 'top',
  488. },
  489. name: '缺陷病案数',
  490. backgroundStyle: {
  491. color: 'rgba(180, 180, 180, 0.2)',
  492. },
  493. },
  494. ],
  495. });
  496. } else {
  497. myChart.setOption({
  498. title: {
  499. text: '暂无数据',
  500. x: 'center',
  501. y: 'center',
  502. textStyle: {
  503. fontSize: 14,
  504. fontWeight: 'normal',
  505. },
  506. },
  507. });
  508. }
  509. });
  510. },
  511. },
  512. };
  513. </script>
  514. <style lang="scss" scoped>
  515. .pages {
  516. padding: 0 18px;
  517. background: #f4f4f4;
  518. .btnNAv {
  519. display: flex;
  520. justify-content: flex-end;
  521. padding-top: 30px;
  522. padding-bottom: 10px;
  523. a {
  524. padding: 15px 30px;
  525. color: #fff;
  526. border-radius: 10px;
  527. margin-left: 15px;
  528. }
  529. .bj {
  530. background: #35ae4a;
  531. }
  532. .bc {
  533. background: #dd7500;
  534. }
  535. .dc {
  536. background: #439ab6;
  537. }
  538. .fh {
  539. background: #185da6;
  540. }
  541. }
  542. .block {
  543. margin-bottom: 20px;
  544. background: #fff;
  545. padding: 25px 15px;
  546. border-radius: 5px;
  547. .blockCon {
  548. display: flex;
  549. justify-content: space-between;
  550. .lefts {
  551. display: flex;
  552. }
  553. .selects {
  554. margin: 0 20px;
  555. span {
  556. margin-right: 10px;
  557. }
  558. }
  559. }
  560. .ytext {
  561. font-size: 16px;
  562. color: #e48d53;
  563. font-weight: 400;
  564. line-height: 40px;
  565. }
  566. }
  567. .cardBox {
  568. margin: 0 0 16px 0;
  569. background: #fff;
  570. padding: 25px 15px;
  571. border-radius: 5px;
  572. .contentBox {
  573. display: flex;
  574. .left {
  575. display: flex;
  576. flex: 1;
  577. .l {
  578. display: flex;
  579. flex: 1;
  580. flex-wrap: wrap;
  581. .i {
  582. width: calc(50% - 20px);
  583. margin-right: 20px;
  584. height: 86px;
  585. background: #30b48e;
  586. border-radius: 5px;
  587. position: relative;
  588. overflow: hidden;
  589. display: flex;
  590. align-items: center;
  591. justify-content: center;
  592. text-align: center;
  593. .ba {
  594. flex: 1;
  595. font-size: 16px;
  596. font-weight: 400;
  597. color: #fff;
  598. }
  599. .num {
  600. font-size: 24px;
  601. font-weight: bold;
  602. color: #fff;
  603. flex: 1;
  604. margin-left: 20px;
  605. }
  606. .icon {
  607. width: 50px;
  608. position: absolute;
  609. top: 18px;
  610. right: 25px;
  611. }
  612. }
  613. }
  614. .r {
  615. margin: 0 9% 0 0;
  616. .i {
  617. width: 195px;
  618. height: 56px;
  619. background: #eaf4ff;
  620. border-radius: 4px;
  621. margin: 0 0 9px 0;
  622. display: flex;
  623. align-items: center;
  624. justify-content: center;
  625. .icon {
  626. width: 22px;
  627. margin: 6px 0 0 27px;
  628. }
  629. .t {
  630. font-size: 18px;
  631. font-weight: 400;
  632. color: #333333;
  633. text-align: left;
  634. margin-left: 10px;
  635. }
  636. .rt {
  637. margin-left: 10px;
  638. font-weight: bold;
  639. color: #38a1f2;
  640. font-size: 18px;
  641. text-align: left;
  642. }
  643. }
  644. .i:nth-child(1) {
  645. background: #93d2f3;
  646. }
  647. .i:nth-child(2) {
  648. background: #f4ce98;
  649. }
  650. .i:nth-child(3) {
  651. background: #de868f;
  652. }
  653. }
  654. }
  655. .right {
  656. width: 521px;
  657. }
  658. }
  659. }
  660. .cpoin {
  661. cursor: pointer;
  662. }
  663. .chart {
  664. margin-bottom: 16px;
  665. background-color: #fff;
  666. padding: 25px 15px;
  667. }
  668. }
  669. .color-btn {
  670. color: #409eff;
  671. border-color: #c6e2ff;
  672. background-color: #ecf5ff;
  673. }
  674. .title-box{
  675. width: 100%;
  676. display: flex;
  677. align-items: center;
  678. justify-content: space-between;
  679. .xz-btn{
  680. width: 84px;
  681. height: 32px;
  682. text-align: center;
  683. line-height: 32px;
  684. background: #185DA6;
  685. border-radius: 6px;
  686. color: #fff;
  687. font-size: 14px;
  688. cursor: pointer;
  689. }
  690. }
  691. .medicalRecord-box{
  692. width: 100%;
  693. display: flex;
  694. margin-top: 16px;
  695. &>div{
  696. flex: 1;
  697. }
  698. .medicalRecord-list-box{
  699. flex: 1;
  700. }
  701. }
  702. </style>