Prechádzať zdrojové kódy

添加 医生工作量图表 (doctorWorkChart)

cc12458 1 rok pred
rodič
commit
09e875858f

+ 8 - 0
src/api/dataAnalysis.js

@@ -8,6 +8,14 @@ export function getDoctorWork({page, limit, ...data}) {
         params: {page, limit}
     })
 }
+// 获取医生工作量信息
+export function getDoctorWorkChart(data) {
+    return request({
+        url: '/data/analysis/docDemReport',
+        method: 'post',
+        data,
+    })
+}
 // 医院使用分析
 export function getHospitalWork(data) {
     return request({

+ 8 - 0
src/router/dataAnalysis.js

@@ -6,6 +6,14 @@ export default [{
         title: '医生工作量数据',
         pftitle: '数据分析'
     }
+}, {
+    path: 'doctorWorkChart',
+    name: 'doctorWorkChart',
+    component: () => import('@/views/dataAnalysis/DoctorWorkChart.vue'),
+    meta: {
+        title: '医生工作量图表',
+        pftitle: '数据分析'
+    }
 }, {
     path: 'hospitalWork',
     name: 'hospitalWork',

+ 21 - 0
src/utils/format.js

@@ -107,4 +107,25 @@ export function formatMonth(date) {
     const formattedMonth = month < 10 ? '0' + month : month; // 如果月份小于10,前面补0
 
     return `${year}-${formattedMonth}`; // 返回格式化后的字符串
+}
+
+export function subtractMonths(value, date = new Date()) {
+    // 获取原始日期的年、月、日
+    const year = date.getFullYear();
+    const month = date.getMonth();
+    const day = date.getDate();
+
+    let _month = month - Math.abs(value);
+    const _year =  year + Math.floor((_month - (month + 1)) / 12);
+
+    _month = _month - Math.floor((_month - (month + 1)) / 12) * 12;
+
+    // 处理月份天数不足的情况,比如1月31日加1个月不能是2月31日
+    let _date = new Date(_year, _month, day);
+    if (_date.getMonth() !== _month) {
+        const _day = Math.min(day, new Date(_year, _month + 1, 0).getDate());
+        _date = new Date(_year, _month, _day);
+    }
+
+    return _date;
 }

+ 2 - 2
src/views/dataAnalysis/DoctorWork.vue

@@ -127,7 +127,7 @@ export default {
       cascader: [],
       orderType: void 0,
       start: void 0,
-      end: Date.now(),
+      end: new Date(),
 
       categoryOptions: [
         {label: '进入系统量', value: '1'},
@@ -177,7 +177,7 @@ export default {
       this.start = void 0;
       this.end = new Date();
 
-      this.load();
+      this.search();
     },
     exportDow() {},
 

+ 371 - 0
src/views/dataAnalysis/DoctorWorkChart.vue

@@ -0,0 +1,371 @@
+<template>
+  <div class="druglist">
+    <!-- 顶部筛选 -->
+    <div class="screening">
+      <div class="screening-title flex-vertical-center-l">
+        <img src="~@/assets/filters.png" alt/>
+      </div>
+      <div class="screening-form flex-vertical-center-l flex-wrap">
+        <div class="screening-item flex-vertical-center-l">
+          <div class="input" style="display: flex;">
+            <el-date-picker size="mini" v-model="start" type="month" placeholder="起始年月"
+                            :picker-options="startPickerOptions" format="yyyy-MM" :clearable="false"></el-date-picker>
+            <el-date-picker size="mini" v-model="end" type="month" placeholder="结束年月"
+                            :picker-options="endPickerOptions" format="yyyy-MM"></el-date-picker>
+          </div>
+        </div>
+        <div class="screening-item flex-vertical-center-l">
+          <span>医疗机构名称:</span>
+          <div class="input">
+            <el-select
+                size="mini"
+                v-model="cascader[1]"
+                placeholder="请选择"
+                @change="getCascaderC($event)"
+            >
+              <el-option
+                  :label="item.name"
+                  :value="item.pid"
+                  v-for="(item) in cascaderBOptions"
+                  :key="item.pid"
+              ></el-option>
+            </el-select>
+          </div>
+        </div>
+        <div class="screening-item flex-vertical-center-l">
+          <span>科室名称:</span>
+          <div class="input">
+            <el-select size="mini" v-model="cascader[2]" placeholder="请选择" multiple collapse-tags clearable
+                       @change="getCascaderD('')">
+              <el-option
+                  :label="item.name"
+                  :value="item.pid"
+                  v-for="(item) in cascaderCOptions"
+                  :key="item.pid"
+              ></el-option>
+            </el-select>
+          </div>
+        </div>
+        <div class="screening-item flex-vertical-center-l">
+          <span>医生名称:</span>
+          <div class="input">
+            <el-select size="mini" v-model="cascader[3]" placeholder="请输入关键词" multiple collapse-tags clearable
+                       reserve-keyword filterable remote :remote-method="getCascaderD" :loading="loadDoctorSelect"
+                       :disabled="!cascader[1]">
+              <el-option
+                  :label="item.username"
+                  :value="item.pid"
+                  v-for="(item) in cascaderDOptions"
+                  :key="item.pid"
+              ></el-option>
+            </el-select>
+          </div>
+        </div>
+
+        <el-button type="primary" size="mini" @click="search()">搜索</el-button>
+        <el-button type="warning" size="mini" @click="clearFilter()">清空</el-button>
+        <!--<el-button type="primary" size="mini" @click="exportDow()">导出</el-button>-->
+      </div>
+    </div>
+
+    <!-- 展示数据层 -->
+    <div class="showData">
+      <!-- 展示两种数据 -->
+      <div class="showEcharts">
+        <div id="myChart" :style="{width: '100%'}" class="myCharts"></div>
+        <div class="e-format" style="margin-bottom:20px;">
+          格式:量级维度-时间:数量
+          <span style="margin-left:30px;"></span> X轴:时间 Y轴:单位(次)
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import {getDepartSelect, getDoctorSelect} from "@/api/system.js";
+import {medicalInstitution} from "@/api/city";
+import {getDoctorWorkChart} from "@/api/dataAnalysis";
+import {formatMonth, subtractMonths} from "@/utils/format";
+import {mapGetters} from "vuex";
+
+export default {
+  data() {
+    return {
+      loadDoctorSelect: false,
+      cascaderAOptions: [],
+      cascaderBOptions: [],
+      cascaderCOptions: [],
+      cascaderDOptions: [],
+      cascader: [],
+      start: subtractMonths(-12),
+      end: new Date(),
+
+      categoryOptions: [
+        {label: '进入系统量', value: 'docInCount'},
+        {label: '辅诊系统推导给HIS量', value: 'docOutCount'},
+        {label: '已收费处方量', value: 'docPayCount'},
+        {label: '协定方使用量', value: 'associationCount'},
+        {label: '智能推导量', value: 'pushCount'},
+        {label: '智能辩证推导量', value: 'dialecticalCount'},
+        {label: '方剂检索量', value: 'prescriptionsCount'},
+        {label: '名家医案检索量', value: 'famousCount'},
+        {label: '病历转方量', value: 'medicalCount'},
+      ],
+
+      tableData: [],
+
+      page: 1,
+      limit: 10,
+      total: 0,
+
+
+      startPickerOptions: {
+        disabledDate: (date) => {
+          const value = date.getTime();
+          return this.end && value > this.end.getTime() || value > Date.now()
+        }
+      },
+      endPickerOptions: {
+        disabledDate: (date) => {
+          const value = date.getTime();
+          return this.start && value < this.start.getTime() || value > Date.now()
+        }
+      }
+    };
+  },
+  created() {
+    this.getCascaderB(this.getuserinfo.organizationid);
+  },
+  mounted() {
+    this.load();
+  },
+  methods: {
+    search() {
+      this.load();
+    },
+    clearFilter() {
+      this.getCascaderB(this.getuserinfo.organizationid)
+      this.orderType = void 0;
+      this.start = subtractMonths(-12);
+      this.end = new Date();
+
+      this.search();
+    },
+    exportDow() {},
+    // 获取医疗机构选择器
+    async getCascaderB(id) {
+      this.cascader = [id, '', [], []];
+      this.cascaderBOptions = []
+      this.cascaderCOptions = []
+      this.cascaderDOptions = []
+      let res = await medicalInstitution({organizationId: id});
+      if (res.ResultCode == 0) { this.cascaderBOptions = res.Data; }
+    },
+    // 获取医疗机构下的科室
+    async getCascaderC(id) {
+      this.cascader = [this.cascader[0], id, [], []];
+      let res = await getDepartSelect({institutionId: id});
+      if (res.ResultCode == 0) { this.cascaderCOptions = res.Data; }
+
+      await this.getCascaderD().catch();
+    },
+    async getCascaderD(keyword) {
+      if (!this.cascader[1]) return;
+      this.loadDoctorSelect = true;
+      const res = await getDoctorSelect({
+        keyword,
+        organizationid: this.cascader[0] || '',
+        sititutionid: this.cascader[1] || '',
+        departmentsIds: this.cascader[2] || [],
+      })
+      if (res.ResultCode == 0) {
+        this.cascaderDOptions = res.Data.Items;
+      }
+      this.loadDoctorSelect = false;
+    },
+    // 获取列表
+    async load() {
+      let params = {
+        startMon: formatMonth(this.start),
+        endMon: formatMonth(this.end),
+        orgId: this.cascader[0] || '',
+        stiId: this.cascader[1] || '',
+        deptId: '',
+        deptIds: this.cascader[2] || [],
+        doctorIds: this.cascader[3] || [],
+      };
+      let res = await getDoctorWorkChart(params);
+      if (res.ResultCode == 0) {
+        this.tableData = res.Data;
+        this.drawLine();
+      }
+    },
+    drawLine() {
+      // 基于准备好的dom,初始化echarts实例
+      let myChart = this.$echarts.init(document.getElementById("myChart"));
+      // 绘制图表
+      myChart.setOption({
+        grid: {
+          left: "1%",
+          right: "1%",
+          bottom: "3%",
+          containLabel: true
+        },
+        legend: {},
+        tooltip: {trigger: 'axis'},
+        xAxis: {type: "category",},
+        yAxis: {type: "value"},
+        dataset: {source: this.tableData},
+        series: this.categoryOptions.map((item) => {
+          return {type: 'bar', seriesLayoutBy: 'row', name: item.label, encode: {x: 'mon', y: item.value}}
+        })
+      });
+    }
+  },
+  computed: {
+    ...mapGetters(["getuserinfo"])
+  }
+};
+</script>
+<style lang="scss" scoped>
+@import "../../style/common.scss";
+@import "../../style/base.scss";
+
+.showData {
+  background: #fff;
+  border-radius: 10px;
+  margin-top: 5px;
+  padding: 10px;
+  height: 71vh;
+  overflow: auto;
+
+  .top-change {
+    margin-bottom: 20px;
+    width: 100px;
+    height: 34px;
+    border-radius: 8px;
+    cursor: pointer;
+    border: 1px solid #dcdfe6;
+    font-size: 14px;
+
+    div:first-child {
+      border-radius: 8px 0 0 8px;
+    }
+
+    div:last-child {
+      border-radius: 0 8px 8px 0;
+    }
+
+    div {
+      height: 34px;
+      width: 50px;
+      box-sizing: border-box;
+      line-height: 34px;
+      text-align: center;
+    }
+
+    .choosed {
+      background: #5386f6;
+      color: #fff;
+    }
+  }
+
+  .bg-yellow {
+    border-radius: 8px;
+    background: #ffae45;
+    color: #fff;
+    width: 74px;
+    height: 34px;
+    margin-bottom: 20px;
+    cursor: pointer;
+    margin-left: 20px;
+  }
+
+  .showEcharts {
+    width: 100%;
+
+    .myCharts {
+      height: 360px;
+    }
+  }
+
+  .showList {
+    width: 100%;
+
+    ul {
+      li {
+        padding: 15px;
+        border-bottom: 1px solid #dcdfe6;
+      }
+
+      li:hover {
+        background: #dcdfe6;
+        cursor: default;
+      }
+
+      .title {
+        font-weight: bold;
+        color: #5386f6;
+        width: 90px;
+      }
+    }
+  }
+}
+</style>
+<style lang="scss" scoped>
+@media screen and (min-width: 1681px) and (max-width: 1920px) {
+  .showData {
+    height: 81vh;
+
+    .showEcharts {
+      width: 100%;
+
+      .myCharts {
+        height: 500px;
+      }
+    }
+  }
+}
+
+@media screen and (min-width: 1601px) and (max-width: 1680px) {
+  .showData {
+    height: 80vh;
+
+    .showEcharts {
+      width: 100%;
+
+      .myCharts {
+        height: 500px;
+      }
+    }
+  }
+}
+
+@media screen and (min-width: 1361px) and (max-width: 1600px) {
+  .showData {
+    height: 73vh;
+
+    .showEcharts {
+      width: 100%;
+
+      .myCharts {
+        height: 410px;
+      }
+    }
+  }
+}
+
+@media screen and(min-width: 1281px) and (max-width: 1360px) {
+  .showData {
+    height: 73vh;
+
+    .showEcharts {
+      width: 100%;
+
+      .myCharts {
+        height: 410px;
+      }
+    }
+  }
+}
+</style>