WXL
2026-09-17 3df460667d74868d0a54fce12d5e881bbd6c0c1a
src/views/statistics/utilization.vue
@@ -29,124 +29,56 @@
    <el-card style="margin-bottom: 20px;">
      <div slot="header" class="clearfix">
        <h3>2. 器官弃用原因分析</h3>
        <el-form :inline="true" style="float: right;">
          <el-form-item label="器官类型" style="margin-bottom: 0;">
            <el-select
              v-model="abandonQuery.organType"
              placeholder="请选择器官类型"
              clearable
              size="mini"
              style="width: 150px"
              @change="handleAbandonQuery"
            >
              <el-option
                v-for="item in organOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </el-form>
      </div>
      <EChartsWrapper
        v-if="useMock"
        :id="'abandon-analysis-chart'"
        :options="abandonChartOptions"
        height="400px"
      />
      <el-empty v-else description="后端暂未提供器官弃用原因统计接口"></el-empty>
    </el-card>
    <!-- 第三部分:每供体器官获取数量 -->
    <el-card>
      <div slot="header" class="clearfix">
        <h3>3. 每供体器官获取数量</h3>
        <el-form :inline="true" style="float: right;">
          <el-form-item label="年份" style="margin-bottom: 0;">
            <el-date-picker
              v-model="perDonorQuery.year"
              type="year"
              placeholder="选择年份"
              value-format="yyyy"
              size="mini"
              style="width: 120px"
              @change="handlePerDonorQuery"
            />
          </el-form-item>
          <el-form-item label="器官类型" style="margin-bottom: 0;">
            <el-select
              v-model="perDonorQuery.organType"
              placeholder="请选择器官类型"
              clearable
              size="mini"
              style="width: 150px"
              @change="handlePerDonorQuery"
            >
              <el-option
                v-for="item in organOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
          </el-form-item>
        </el-form>
      </div>
      <el-table
        v-loading="loading"
        :data="tableData"
        style="width: 100%"
        border
        stripe
      >
        <el-table-column label="年份" prop="year" align="center"  />
        <el-table-column label="供体姓名" prop="donorName" align="center"  />
        <el-table-column label="获取数量" prop="acquisitionCount" align="center" >
          <template slot-scope="{ row }">
            <el-tag :type="getCountTag(row.acquisitionCount)" size="small">
              {{ row.acquisitionCount }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column label="全肝" prop="liver" align="center"  />
        <el-table-column label="左半肝" prop="leftLiver" align="center"  />
        <el-table-column label="右半肝" prop="rightLiver" align="center"  />
        <el-table-column label="心脏" prop="heart" align="center"  />
        <el-table-column label="小肠" prop="intestine" align="center"  />
        <el-table-column label="胰腺" prop="pancreas" align="center"  />
        <el-table-column label="左肾" prop="leftKidney" align="center"  />
        <el-table-column label="右肾" prop="rightKidney" align="center"  />
        <el-table-column label="全肺" prop="fullLung" align="center"  />
        <el-table-column label="左肺" prop="leftLung" align="center"  />
        <el-table-column label="右肺" prop="rightLung" align="center"  />
        <el-table-column label="左眼角膜" prop="leftCornea" align="center"  />
        <el-table-column label="右眼角膜" prop="rightCornea" align="center"  />
        <el-table-column label="地区" prop="region" align="center"  />
        <el-table-column label="操作" align="center"  fixed="right">
          <template slot-scope="{ row }">
            <el-button
              type="text"
              size="mini"
              @click="viewDonorDetail(row)"
            >
              详情
            </el-button>
          </template>
        </el-table-column>
      </el-table>
      <el-pagination
        v-show="total > 0"
        :total="total"
        :page.sync="query.page"
        :limit.sync="query.limit"
        @current-change="handlePagination"
        @size-change="handlePageSizeChange"
        style="margin-top: 20px;"
        layout="total, sizes, prev, pager, next, jumper"
        :page-sizes="[10, 20, 50, 100]"
      />
      <template v-if="useMock">
        <el-table
          v-loading="loading"
          :data="perDonorTable"
          style="width: 100%"
          border
          stripe
        >
          <el-table-column label="年份" prop="year" align="center" />
          <el-table-column label="供体姓名" prop="donorName" align="center" />
          <el-table-column label="获取数量" prop="acquisitionCount" align="center">
            <template slot-scope="{ row }">
              <el-tag :type="getCountTag(row.acquisitionCount)" size="small">
                {{ row.acquisitionCount }}
              </el-tag>
            </template>
          </el-table-column>
          <el-table-column label="全肝" prop="liver" align="center" />
          <el-table-column label="左半肝" prop="leftLiver" align="center" />
          <el-table-column label="右半肝" prop="rightLiver" align="center" />
          <el-table-column label="心脏" prop="heart" align="center" />
          <el-table-column label="小肠" prop="intestine" align="center" />
          <el-table-column label="胰腺" prop="pancreas" align="center" />
          <el-table-column label="左肾" prop="leftKidney" align="center" />
          <el-table-column label="右肾" prop="rightKidney" align="center" />
          <el-table-column label="全肺" prop="fullLung" align="center" />
          <el-table-column label="左肺" prop="leftLung" align="center" />
          <el-table-column label="右肺" prop="rightLung" align="center" />
          <el-table-column label="左眼角膜" prop="leftCornea" align="center" />
          <el-table-column label="右眼角膜" prop="rightCornea" align="center" />
          <el-table-column label="地区" prop="region" align="center" />
        </el-table>
      </template>
      <el-empty v-else description="后端暂未提供每供体器官获取明细接口"></el-empty>
    </el-card>
  </div>
</template>
@@ -154,6 +86,10 @@
<script>
import FilterPanel from '@/components/charts/FilterPanel.vue';
import EChartsWrapper from '@/components/charts/EChartsWrapper.vue';
import { organType } from '@/api/businessApi';
import { toTotalsList, toNum } from '@/utils/totals';
import statisticsMode, { isStatisticsMock } from '@/utils/statisticsMode';
import { COLORS, axisTooltip, grid, xAxis, yAxis, legend, barSeries } from '@/utils/chartStyle';
export default {
  name: 'OrganUtilization',
@@ -167,8 +103,6 @@
      query: {
        region: [],
        monthRange: [],
        ageRange: [],
        chinaCategory: [],
        organType: null,
        page: 1,
        limit: 10
@@ -182,13 +116,13 @@
          type: 'select',
          multiple: true,
          options: [
            { label: '青岛地区', value: 'qingdao' },
            { label: '日照地区', value: 'rizhao' },
            { label: '济南地区', value: 'jinan' },
            { label: '烟台地区', value: 'yantai' },
            { label: '威海地区', value: 'weihai' },
            { label: '潍坊地区', value: 'weifang' },
            { label: '临沂地区', value: 'linyi' }
            { label: '青岛地区', value: '青岛地区' },
            { label: '日照地区', value: '日照地区' },
            { label: '济南地区', value: '济南地区' },
            { label: '烟台地区', value: '烟台地区' },
            { label: '威海地区', value: '威海地区' },
            { label: '潍坊地区', value: '潍坊地区' },
            { label: '临沂地区', value: '临沂地区' }
          ],
          width: '300px'
        },
@@ -203,60 +137,19 @@
          width: '300px'
        },
        {
          label: '年龄段',
          prop: 'ageRange',
          type: 'select',
          multiple: true,
          options: [
            { label: '<17岁', value: '0-17' },
            { label: '18-49岁', value: '18-49' },
            { label: '50-69岁', value: '50-69' },
            { label: '>69岁', value: '70-100' }
          ],
          width: '200px'
        },
        {
          label: '中国分类',
          prop: 'chinaCategory',
          type: 'select',
          multiple: true,
          options: [
            { label: '中国一类(DBD)', value: '1' },
            { label: '中国二类(DCD)', value: '2' },
            { label: '中国三类(DBCD)', value: '3' }
          ],
          width: '200px'
        },
        {
          label: '器官类型',
          prop: 'organType',
          type: 'select',
          options: [
            { label: '全肝', value: 'liver' },
            { label: '肾脏', value: 'kidney' },
            { label: '心脏', value: 'heart' },
            { label: '肺', value: 'lung' },
            { label: '眼角膜', value: 'cornea' }
            { label: '全肝', value: '全肝' },
            { label: '肾脏', value: '肾脏' },
            { label: '心脏', value: '心脏' },
            { label: '肺', value: '肺' },
            { label: '胰腺', value: '胰腺' },
            { label: '眼角膜', value: '眼角膜' }
          ],
          width: '200px'
        }
      ],
      // 器官选项
      organOptions: [
        { label: '全肝', value: 'liver' },
        { label: '左半肝', value: 'leftLiver' },
        { label: '右半肝', value: 'rightLiver' },
        { label: '心脏', value: 'heart' },
        { label: '小肠', value: 'intestine' },
        { label: '胰腺', value: 'pancreas' },
        { label: '左肾', value: 'leftKidney' },
        { label: '右肾', value: 'rightKidney' },
        { label: '全肺', value: 'fullLung' },
        { label: '左肺', value: 'leftLung' },
        { label: '右肺', value: 'rightLung' },
        { label: '左眼角膜', value: 'leftCornea' },
        { label: '右眼角膜', value: 'rightCornea' }
      ],
      // 加载状态
@@ -264,322 +157,33 @@
      // 图表配置
      acquisitionChartOptions: {},
      abandonChartOptions: {},
      // 表格数据
      tableData: [],
      total: 0,
      // 器官类型分布数据
      organData: [],
      // 弃用分析查询参数
      abandonQuery: {
        organType: null
      },
      // 每供体查询参数
      perDonorQuery: {
        year: '2024',
        organType: null
      },
      // 模拟数据
      mockData: {
        acquisition: [
          {
            month: '2024-01',
            region: '青岛地区',
            liver: { acquisition: 8, transplant: 7 },
            kidney: { acquisition: 12, transplant: 10 },
            heart: { acquisition: 3, transplant: 3 },
            lung: { acquisition: 5, transplant: 4 },
            cornea: { acquisition: 20, transplant: 18 }
          },
          {
            month: '2024-02',
            region: '青岛地区',
            liver: { acquisition: 10, transplant: 9 },
            kidney: { acquisition: 15, transplant: 13 },
            heart: { acquisition: 4, transplant: 3 },
            lung: { acquisition: 6, transplant: 5 },
            cornea: { acquisition: 25, transplant: 22 }
          },
          {
            month: '2024-01',
            region: '日照地区',
            liver: { acquisition: 5, transplant: 4 },
            kidney: { acquisition: 8, transplant: 7 },
            heart: { acquisition: 2, transplant: 2 },
            lung: { acquisition: 3, transplant: 2 },
            cornea: { acquisition: 15, transplant: 13 }
          }
        ],
        abandon: [
          { name: '器官质量不合格', value: 45 },
          { name: '供体疾病', value: 25 },
          { name: '运输问题', value: 15 },
          { name: '受体匹配问题', value: 10 },
          { name: '其他原因', value: 5 }
        ],
        table: [
          {
            id: 1,
            year: '2024',
            donorName: '张三',
            acquisitionCount: 5,
            liver: 1,
            leftLiver: 0,
            rightLiver: 0,
            heart: 0,
            intestine: 0,
            pancreas: 0,
            leftKidney: 1,
            rightKidney: 1,
            fullLung: 0,
            leftLung: 0,
            rightLung: 0,
            leftCornea: 1,
            rightCornea: 1,
            region: '青岛地区'
          },
          {
            id: 2,
            year: '2024',
            donorName: '李四',
            acquisitionCount: 3,
            liver: 0,
            leftLiver: 0,
            rightLiver: 0,
            heart: 1,
            intestine: 0,
            pancreas: 0,
            leftKidney: 0,
            rightKidney: 0,
            fullLung: 0,
            leftLung: 1,
            rightLung: 1,
            leftCornea: 0,
            rightCornea: 0,
            region: '青岛地区'
          },
          {
            id: 3,
            year: '2024',
            donorName: '王五',
            acquisitionCount: 6,
            liver: 1,
            leftLiver: 0,
            rightLiver: 0,
            heart: 0,
            intestine: 0,
            pancreas: 1,
            leftKidney: 1,
            rightKidney: 1,
            fullLung: 0,
            leftLung: 0,
            rightLung: 0,
            leftCornea: 1,
            rightCornea: 1,
            region: '日照地区'
          },
          {
            id: 4,
            year: '2024',
            donorName: '赵六',
            acquisitionCount: 4,
            liver: 0,
            leftLiver: 0,
            rightLiver: 0,
            heart: 0,
            intestine: 0,
            pancreas: 0,
            leftKidney: 1,
            rightKidney: 1,
            fullLung: 0,
            leftLung: 0,
            rightLung: 0,
            leftCornea: 1,
            rightCornea: 1,
            region: '济南地区'
          },
          {
            id: 5,
            year: '2023',
            donorName: '钱七',
            acquisitionCount: 3,
            liver: 0,
            leftLiver: 0,
            rightLiver: 0,
            heart: 1,
            intestine: 0,
            pancreas: 0,
            leftKidney: 0,
            rightKidney: 0,
            fullLung: 1,
            leftLung: 0,
            rightLung: 1,
            leftCornea: 0,
            rightCornea: 0,
            region: '烟台地区'
          }
        ]
      }
      // 无接口部分的模拟数据(仅 mock 模式展示)
      abandonReasons: [
        { name: '器官质量不合格', value: 45 },
        { name: '供体疾病', value: 25 },
        { name: '运输问题', value: 15 },
        { name: '受体匹配问题', value: 10 },
        { name: '其他原因', value: 5 }
      ],
      perDonorTable: [
        { id: 1, year: '2024', donorName: '张三', acquisitionCount: 5, liver: 1, leftLiver: 0, rightLiver: 0, heart: 0, intestine: 0, pancreas: 0, leftKidney: 1, rightKidney: 1, fullLung: 0, leftLung: 0, rightLung: 0, leftCornea: 1, rightCornea: 1, region: '青岛地区' },
        { id: 2, year: '2024', donorName: '李四', acquisitionCount: 3, liver: 0, leftLiver: 0, rightLiver: 0, heart: 1, intestine: 0, pancreas: 0, leftKidney: 0, rightKidney: 0, fullLung: 0, leftLung: 1, rightLung: 1, leftCornea: 0, rightCornea: 0, region: '青岛地区' },
        { id: 3, year: '2024', donorName: '王五', acquisitionCount: 6, liver: 1, leftLiver: 0, rightLiver: 0, heart: 0, intestine: 0, pancreas: 1, leftKidney: 1, rightKidney: 1, fullLung: 0, leftLung: 0, rightLung: 0, leftCornea: 1, rightCornea: 1, region: '日照地区' },
        { id: 4, year: '2024', donorName: '赵六', acquisitionCount: 4, liver: 0, leftLiver: 0, rightLiver: 0, heart: 0, intestine: 0, pancreas: 0, leftKidney: 1, rightKidney: 1, fullLung: 0, leftLung: 0, rightLung: 0, leftCornea: 1, rightCornea: 1, region: '济南地区' },
        { id: 5, year: '2023', donorName: '钱七', acquisitionCount: 3, liver: 0, leftLiver: 0, rightLiver: 0, heart: 1, intestine: 0, pancreas: 0, leftKidney: 0, rightKidney: 0, fullLung: 1, leftLung: 0, rightLung: 1, leftCornea: 0, rightCornea: 0, region: '烟台地区' }
      ]
    };
  },
  created() {
    this.loadData();
  },
  methods: {
    // 加载数据
    async loadData() {
      this.loading = true;
      try {
        await new Promise(resolve => setTimeout(resolve, 500));
        // 处理表格数据
        let filteredData = [...this.mockData.table];
        if (this.query.region.length > 0) {
          filteredData = filteredData.filter(item =>
            this.query.region.includes(item.region.replace('地区', '').toLowerCase())
          );
        }
        if (this.query.monthRange && this.query.monthRange.length === 2) {
          // 这里可以添加月份筛选逻辑
        }
        if (this.query.ageRange.length > 0) {
          // 这里可以添加年龄筛选逻辑
        }
        if (this.query.chinaCategory.length > 0) {
          // 这里可以添加中国分类筛选逻辑
        }
        if (this.query.organType) {
          const organMap = { liver: 'liver', kidney: ['leftKidney', 'rightKidney'], heart: 'heart', lung: ['fullLung', 'leftLung', 'rightLung'], cornea: ['leftCornea', 'rightCornea'] };
          const organProp = organMap[this.query.organType];
          if (organProp) {
            if (Array.isArray(organProp)) {
              filteredData = filteredData.filter(item =>
                organProp.some(prop => item[prop] > 0)
              );
            } else {
              filteredData = filteredData.filter(item => item[organProp] > 0);
            }
          }
        }
        // 分页
        const start = (this.query.page - 1) * this.query.limit;
        this.tableData = filteredData.slice(start, start + this.query.limit);
        this.total = filteredData.length;
        // 更新图表
        this.updateCharts();
      } catch (error) {
        console.error('加载数据失败:', error);
        this.$message.error('数据加载失败');
      } finally {
        this.loading = false;
      }
  computed: {
    useMock() {
      return isStatisticsMock();
    },
    // 更新图表
    updateCharts() {
      // 获取移植统计图
      const regions = [...new Set(this.mockData.acquisition.map(item => item.region))];
      const months = [...new Set(this.mockData.acquisition.map(item => item.month))];
      const organTypes = ['liver', 'kidney', 'heart', 'lung', 'cornea'];
      const organNames = ['肝脏', '肾脏', '心脏', '肺', '眼角膜'];
      const seriesData = [];
      const xAxisData = [];
      // 生成x轴数据
      months.forEach(month => {
        regions.forEach(region => {
          xAxisData.push(`${month}\n${region}`);
        });
      });
      // 生成系列数据
      organTypes.forEach((organType, index) => {
        const data = [];
        months.forEach(month => {
          regions.forEach(region => {
            const item = this.mockData.acquisition.find(d => d.month === month && d.region === region);
            if (item) {
              data.push(item[organType]?.acquisition || 0);
            } else {
              data.push(0);
            }
          });
        });
        seriesData.push({
          name: `${organNames[index]}-获取`,
          type: 'bar',
          stack: '总量',
          data: data,
          itemStyle: this.getSeriesColor(index * 2)
        });
        const transplantData = [];
        months.forEach(month => {
          regions.forEach(region => {
            const item = this.mockData.acquisition.find(d => d.month === month && d.region === region);
            if (item) {
              transplantData.push(item[organType]?.transplant || 0);
            } else {
              transplantData.push(0);
            }
          });
        });
        seriesData.push({
          name: `${organNames[index]}-移植`,
          type: 'bar',
          stack: '总量',
          data: transplantData,
          itemStyle: this.getSeriesColor(index * 2 + 1)
        });
      });
      this.acquisitionChartOptions = {
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'shadow'
          }
        },
        legend: {
          data: seriesData.map(item => item.name),
          top: 20
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        xAxis: {
          type: 'category',
          data: xAxisData,
          axisLabel: {
            interval: 0,
            rotate: 45
          }
        },
        yAxis: {
          type: 'value',
          name: '数量(个)'
        },
        series: seriesData
      };
      // 弃用分析图
      this.abandonChartOptions = {
    abandonChartOptions() {
      return {
        tooltip: {
          trigger: 'item',
          formatter: '{a} <br/>{b} : {c} ({d}%)'
@@ -587,7 +191,7 @@
        legend: {
          orient: 'vertical',
          left: 'left',
          data: this.mockData.abandon.map(item => item.name)
          data: this.abandonReasons.map(item => item.name)
        },
        series: [
          {
@@ -595,7 +199,7 @@
            type: 'pie',
            radius: '55%',
            center: ['50%', '60%'],
            data: this.mockData.abandon,
            data: this.abandonReasons,
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
@@ -606,18 +210,65 @@
          }
        ]
      };
    }
  },
  created() {
    this.loadData();
    // 数据源(mock/真实接口)切换时重新加载
    this.$watch(() => statisticsMode.useMock, () => this.loadData());
  },
  methods: {
    // 组装查询条件
    buildQuery() {
      const q = {};
      if (this.query.region && this.query.region.length) {
        q.area = this.query.region.join(',');
      }
      if (this.query.monthRange && this.query.monthRange.length === 2) {
        q.startTime = this.query.monthRange[0];
        q.endTime = this.query.monthRange[1];
      }
      if (this.query.organType) {
        q.organName = this.query.organType;
      }
      return q;
    },
    // 获取系列颜色
    getSeriesColor(index) {
      const colors = [
        '#409EFF', '#8cc5ff',  // 肝脏
        '#67C23A', '#95d475',  // 肾脏
        '#E6A23C', '#f3c96b',  // 心脏
        '#F56C6C', '#f89898',  // 肺
        '#909399', '#b1b3b8'   // 眼角膜
      ];
      return { color: colors[index % colors.length] };
    // 加载数据
    async loadData() {
      this.loading = true;
      try {
        const q = this.buildQuery();
        const typeRes = await organType(q);
        this.organData = toTotalsList(typeRes).map(item => ({
          name: item.organName || '未知',
          acquisition: toNum(item.getCount),
          transplant: toNum(item.useCount)
        }));
        this.updateCharts();
      } catch (error) {
        console.error('加载数据失败:', error);
        this.$message.error('数据加载失败');
      } finally {
        this.loading = false;
      }
    },
    // 更新图表
    updateCharts() {
      this.acquisitionChartOptions = {
        tooltip: axisTooltip(),
        legend: legend(['获取数量', '移植数量']),
        grid: grid(),
        xAxis: xAxis(this.organData.map(item => item.name)),
        yAxis: yAxis('数量(个)'),
        series: [
          barSeries('获取数量', this.organData.map(item => item.acquisition), COLORS.blue),
          barSeries('移植数量', this.organData.map(item => item.transplant), COLORS.green)
        ]
      };
    },
    // 获取数量标签
@@ -638,8 +289,6 @@
      this.query = {
        region: [],
        monthRange: [],
        ageRange: [],
        chinaCategory: [],
        organType: null,
        page: 1,
        limit: 10
@@ -647,42 +296,16 @@
      this.loadData();
    },
    // 处理分页
    handlePagination(page) {
      this.query.page = page;
      this.loadData();
    },
    // 处理分页大小变化
    handlePageSizeChange(size) {
      this.query.limit = size;
      this.query.page = 1;
      this.loadData();
    },
    // 处理弃用查询
    handleAbandonQuery() {
      // 这里可以根据abandonQuery筛选数据
      console.log('弃用查询:', this.abandonQuery);
    },
    // 处理每供体查询
    handlePerDonorQuery() {
      this.loadData();
    },
    // 处理导出
    handleExport() {
      this.$message.info('导出功能开发中...');
    },
    // 查看捐献者详情
    viewDonorDetail(row) {
      this.$router.push({
        path: '/donor/detail',
        query: { id: row.id }
      });
    }
  }
};
</script>
<style scoped>
.statistics-page {
  padding: 20px;
}
</style>