WXL (wul)
2026-05-07 ecbcc059d43f64877551756de129c653d31d0032
src/views/sfstatistics/propaganda/index.vue
@@ -1,17 +1,912 @@
<template>
  <div>宣教统计</div>
  <div class="education-statistics">
    <!-- 搜索区域 -->
    <div class="search-container">
      <el-form
        :model="queryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        label-width="100px"
      >
        <el-form-item label="统计维度" prop="groupType">
          <el-select
            v-model="queryParams.groupType"
            placeholder="请选择统计维度"
            @change="handleGroupTypeChange"
            style="width: 180px"
          >
            <el-option label="按科室统计" value="1"></el-option>
            <el-option label="按病区统计" value="2"></el-option>
          </el-select>
          <el-select
            style="margin-left: 10px"
            v-if="queryParams.groupType == '2'"
            v-model="queryParams.hospitaldistrictcodes"
            size="medium"
            multiple
            filterable
            placeholder="请选择病区"
          >
            <el-option
              v-for="item in flatArrayhospit"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
          <el-select
            v-else-if="queryParams.groupType == '1'"
            v-model="queryParams.deptcodes"
            size="medium"
            multiple
            filterable
            placeholder="请选择科室"
          >
            <el-option
              v-for="item in flatArraydept"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="就诊类型" prop="hospType">
          <el-select
            v-model="queryParams.hospType"
            placeholder="请选择就诊类型"
            clearable
            style="width: 150px"
          >
            <el-option label="门诊" value="1"></el-option>
            <el-option label="出院" value="2"></el-option>
            <el-option label="专病" value="3"></el-option>
            <el-option label="入院/外部导入" value="4"></el-option>
            <el-option label="体检" value="5"></el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="入院时间" prop="dateRange">
          <el-date-picker
            v-model="queryParams.dateRange"
            type="daterange"
            range-separator="至"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            value-format="yyyy-MM-dd"
            style="width: 280px"
            :picker-options="datePickerOptions"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="宣教发送时间" prop="visittime">
          <el-date-picker
            v-model="queryParams.visittime"
            type="date"
            placeholder="选择日期"
            value-format="yyyy-MM-dd"
            style="width: 180px"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="medium"
            @click="handleQuery"
            :loading="loading"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
            >重置</el-button
          >
          <el-button
            type="warning"
            plain
            icon="el-icon-download"
            size="medium"
            @click="handleExport"
            v-hasPermi="['system:statistics:export']"
            >导出</el-button
          >
        </el-form-item>
      </el-form>
    </div>
    <!-- 统计数据概览 -->
    <div class="summary-cards" v-if="statisticsData.total > 0">
      <el-row :gutter="20">
        <el-col :span="6">
          <div class="summary-card">
            <div class="card-title">发送总量</div>
            <div class="card-value">{{ totalCount }}</div>
            <div class="card-desc">总宣教发送次数</div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="summary-card">
            <div class="card-title">发送成功量</div>
            <div class="card-value">{{ sendSuccessCount }}</div>
            <div class="card-desc">已成功发送的宣教</div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="summary-card">
            <div class="card-title">已读量</div>
            <div class="card-value">{{ readCount }}</div>
            <div class="card-desc">患者已阅读的宣教</div>
          </div>
        </el-col>
        <el-col :span="6">
          <div class="summary-card">
            <div class="card-title">平均发送成功率</div>
            <div class="card-value">{{ avgSendSuccessRate }}%</div>
            <div class="card-desc">整体发送成功比例</div>
          </div>
        </el-col>
      </el-row>
    </div>
    <!-- 数据表格 -->
    <div class="table-container">
      <el-table
        v-loading="loading"
        :data="statisticsData.list"
        :border="true"
        style="width: 100%"
        :default-sort="{ prop: 'totalCount', order: 'descending' }"
        @sort-change="handleSortChange"
      >
        <el-table-column prop="groupName" label="分组名称" align="center" fixed>
          <!-- <template slot-scope="scope">
            <span class="group-name" @click="handleGroupDetail(scope.row)">
              {{ scope.row.groupName }}
            </span>
          </template> -->
        </el-table-column>
        <el-table-column
          prop="groupCode"
          label="分组编码"
          align="center"
        ></el-table-column>
        <el-table-column
          prop="totalCount"
          label="宣教发送总量"
          align="center"
          sortable="custom"
        >
          <template slot-scope="scope">
            <span class="count-highlight">{{ scope.row.totalCount }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="sendSuccessCount"
          label="发送成功量"
          align="center"
          sortable="custom"
        >
          <template slot-scope="scope">
            <span class="success-count">{{ scope.row.sendSuccessCount }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="readCount"
          label="已读量"
          align="center"
          sortable="custom"
        >
          <template slot-scope="scope">
            <span class="read-count">{{ scope.row.readCount }}</span>
          </template>
        </el-table-column>
        <el-table-column
          prop="sendSuccessRate"
          label="发送成功率"
          align="center"
          width="200"
          sortable="custom"
        >
          <template slot-scope="scope">
            <el-progress
              :percentage="scope.row.sendSuccessRate * 100"
              :stroke-width="8"
              :show-text="false"
              style="width: 80px; margin: 0 auto"
              :color="getRateColor(scope.row.sendSuccessRate)"
            />
            <span class="rate-text"
              >{{ (scope.row.sendSuccessRate * 100).toFixed(1) }}%</span
            >
          </template>
        </el-table-column>
        <el-table-column
          prop="readRate"
          label="已读率"
          align="center"
          width="200"
          sortable="custom"
        >
          <template slot-scope="scope">
            <el-progress
              :percentage="scope.row.readRate * 100"
              :stroke-width="8"
              :show-text="false"
              style="width: 80px; margin: 0 auto"
              :color="getRateColor(scope.row.readRate)"
            />
            <span class="rate-text"
              >{{ (scope.row.readRate * 100).toFixed(1) }}%</span
            >
          </template>
        </el-table-column>
        <!-- <el-table-column label="操作" align="center" width="200" fixed="right">
          <template slot-scope="scope">
            <el-button
              type="text"
              size="small"
              @click="handleDetail(scope.row)"
              icon="el-icon-document"
              v-hasPermi="['system:statistics:detail']"
              >详细数据</el-button
            >
            <el-button
              type="text"
              size="small"
              @click="handleExportGroup(scope.row)"
              icon="el-icon-download"
              v-hasPermi="['system:statistics:export']"
              >导出</el-button
            >
          </template>
        </el-table-column> -->
      </el-table>
      <!-- 分页 -->
      <pagination
        v-show="statisticsData.total > 0"
        :total="statisticsData.total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </div>
    <!-- 分组详情弹窗 -->
    <el-dialog
      :title="detailTitle"
      :visible.sync="detailVisible"
      width="80%"
      :before-close="handleCloseDetail"
    >
      <div v-loading="detailLoading">
        <el-row :gutter="20" class="detail-header">
          <el-col :span="6">
            <div class="detail-item">
              <label>分组名称:</label>
              <span>{{ currentGroup.groupName }}</span>
            </div>
          </el-col>
          <el-col :span="6">
            <div class="detail-item">
              <label>分组编码:</label>
              <span>{{ currentGroup.groupCode }}</span>
            </div>
          </el-col>
          <el-col :span="6">
            <div class="detail-item">
              <label>发送成功率:</label>
              <span class="rate-highlight"
                >{{ (currentGroup.sendSuccessRate * 100).toFixed(1) }}%</span
              >
            </div>
          </el-col>
          <el-col :span="6">
            <div class="detail-item">
              <label>已读率:</label>
              <span class="rate-highlight"
                >{{ (currentGroup.readRate * 100).toFixed(1) }}%</span
              >
            </div>
          </el-col>
        </el-row>
        <el-tabs v-model="detailActiveTab" class="detail-tabs">
          <el-tab-pane label="趋势分析" name="trend">
            <!-- 这里可以放置图表组件 -->
            <div class="chart-placeholder">
              趋势图表(可根据需求接入ECharts)
            </div>
          </el-tab-pane>
          <el-tab-pane label="明细数据" name="detail">
            <el-table
              :data="detailList"
              border
              style="width: 100%; margin-top: 20px"
            >
              <el-table-column
                prop="patientName"
                label="患者姓名"
                align="center"
                width="120"
              ></el-table-column>
              <el-table-column
                prop="patientNo"
                label="患者编号"
                align="center"
                width="120"
              ></el-table-column>
              <el-table-column
                prop="sendTime"
                label="发送时间"
                align="center"
                width="180"
              ></el-table-column>
              <el-table-column
                prop="readTime"
                label="阅读时间"
                align="center"
                width="180"
              ></el-table-column>
              <el-table-column
                prop="educationTitle"
                label="宣教标题"
                align="center"
              ></el-table-column>
              <el-table-column
                prop="status"
                label="状态"
                align="center"
                width="100"
              >
                <template slot-scope="scope">
                  <el-tag
                    :type="
                      scope.row.status === '已读'
                        ? 'success'
                        : scope.row.status === '发送成功'
                        ? 'info'
                        : 'danger'
                    "
                    size="small"
                  >
                    {{ scope.row.status }}
                  </el-tag>
                </template>
              </el-table-column>
            </el-table>
          </el-tab-pane>
        </el-tabs>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="detailVisible = false">关 闭</el-button>
      </span>
    </el-dialog>
  </div>
</template>
<script>
import { gethelibraryCount } from "@/api/AiCentre/index";
export default {
  name: "EducationStatistics",
  data() {
    return {};
    return {
      // 查询参数
      queryParams: {
        pageNum: 1,
        pageSize: 1000,
        groupType: "1", // 1-科室,2-病区
        deptcodes: ['all'], // 科室code数组
        hospitaldistrictcodes: [], // 病区code数组
        hospType: undefined, // 就诊类型
        starttime: undefined, // 入院开始时间
        endtime: undefined, // 入院结束时间
        visittime: undefined, // 宣教发送时间
        dateRange: [], // 入院时间范围
        orderBy: "totalCount", // 排序字段
        order: "descending", // 排序方式
      },
      // 统计数据
      statisticsData: {
        total: 0,
        list: [],
      },
      // 下拉选项
      flatArraydept: [],
      flatArrayhospit: [],
      // 加载状态
      loading: false,
      detailLoading: false,
      // 详情弹窗
      detailVisible: false,
      detailTitle: "",
      detailActiveTab: "trend",
      currentGroup: {},
      detailList: [],
      // 日期选择器配置
      datePickerOptions: {
        disabledDate(time) {
          return time.getTime() > Date.now();
        },
        shortcuts: [
          {
            text: "最近一周",
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
              picker.$emit("pick", [start, end]);
            },
          },
          {
            text: "最近一个月",
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
              picker.$emit("pick", [start, end]);
            },
          },
          {
            text: "最近三个月",
            onClick(picker) {
              const end = new Date();
              const start = new Date();
              start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
              picker.$emit("pick", [start, end]);
            },
          },
        ],
      },
    };
  },
  created() {},
  computed: {
    // 计算总发送量
    totalCount() {
      return this.statisticsData.list.reduce(
        (sum, item) => sum + item.totalCount,
        0
      );
    },
  methods: {},
    // 计算总发送成功量
    sendSuccessCount() {
      return this.statisticsData.list.reduce(
        (sum, item) => sum + item.sendSuccessCount,
        0
      );
    },
    // 计算总已读量
    readCount() {
      return this.statisticsData.list.reduce(
        (sum, item) => sum + item.readCount,
        0
      );
    },
    // 计算平均发送成功率
    avgSendSuccessRate() {
      if (this.statisticsData.list.length === 0) return 0;
      const totalRate = this.statisticsData.list.reduce(
        (sum, item) => sum + item.sendSuccessRate,
        0
      );
      return ((totalRate / this.statisticsData.list.length) * 100).toFixed(1);
    },
  },
  created() {
    this.flatArrayhospit = this.$store.getters.belongWards.map((ward) => {
      return {
        label: ward.districtName,
        value: ward.districtCode,
      };
    });
    this.flatArraydept = this.$store.getters.belongDepts.map((dept) => {
      return {
        label: dept.deptName,
        value: dept.deptCode,
      };
    });
    console.log(this.flatArrayhospit,'this.flatArrayhospit');
    this.flatArraydept.push({ label: "全部", value: "all" });
    this.flatArrayhospit.push({ label: "全部", value: "all" });
    this.getList();
  },
  methods: {
    // 获取统计数据列表
    async getList() {
      this.loading = true;
      try {
        console.log(this.queryParams.hospitaldistrictcodes);
        // 构建请求参数
        const params = {
          pageNum: this.queryParams.pageNum,
          pageSize: this.queryParams.pageSize,
          hospitaldistrictcodes:
            this.queryParams.hospitaldistrictcodes.includes("all")
              ? this.getAllWardCodes()
              : this.queryParams.hospitaldistrictcodes,
          deptcodes: this.queryParams.deptcodes.includes("all")
            ? this.getAllDeptCodes()
            : this.queryParams.deptcodes,
        };
        // 根据统计维度设置参数
        if (this.queryParams.groupType == "1") {
          params.hospitaldistrictcodes = [];
        } else if (this.queryParams.groupType == "2") {
          params.deptcodes = [];
        }
        // 设置时间参数
        if (
          this.queryParams.dateRange &&
          this.queryParams.dateRange.length === 2
        ) {
          params.starttime = this.queryParams.dateRange[0];
          params.endtime = this.queryParams.dateRange[1];
        }
        // 设置其他参数
        if (this.queryParams.hospType) {
          params.hospType = this.queryParams.hospType;
        }
        if (this.queryParams.visittime) {
          params.visittime = this.queryParams.visittime;
        }
        // 调用接口
        const response = await gethelibraryCount(params);
        this.statisticsData = {
          total: response.total || 0,
          list: response.list || [],
        };
      } catch (error) {
        console.error("获取统计数据失败:", error);
        this.$message.error("获取统计数据失败");
        this.statisticsData = { total: 0, list: [] };
      } finally {
        this.loading = false;
      }
    },
    getAllWardCodes() {
      return this.flatArrayhospit
        .filter((item) => item.value !== "all")
        .map((item) => item.value);
    },
    getAllDeptCodes() {
      return this.flatArraydept
        .filter((item) => item.value !== "all")
        .map((item) => item.value);
    },
    // 统计维度变更
    handleGroupTypeChange(value) {
      // 切换维度时清空对应的选择
      if (value === "dept") {
        this.queryParams.hospitaldistrictcodes = [];
      } else if (value === "ward") {
        this.queryParams.deptcodes = [];
      }
    },
    // 处理搜索
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    // 重置搜索条件
    resetQuery() {
      this.queryParams = {
        pageNum: 1,
        pageSize: 10,
        groupType: "dept",
        deptcodes: [],
        hospitaldistrictcodes: [],
        hospType: undefined,
        starttime: undefined,
        endtime: undefined,
        visittime: undefined,
        dateRange: [],
        orderBy: "totalCount",
        order: "descending",
      };
      this.getList();
    },
    // 表格排序
    handleSortChange({ column, prop, order }) {
      if (prop) {
        this.queryParams.orderBy = prop;
        this.queryParams.order = order;
        this.getList();
      }
    },
    // 根据成功率获取进度条颜色
    getRateColor(rate) {
      const percentage = rate * 100;
      if (percentage >= 90) return "#67C23A";
      if (percentage >= 80) return "#E6A23C";
      if (percentage >= 60) return "#409EFF";
      return "#F56C6C";
    },
    // 查看分组详情
    handleGroupDetail(row) {
      this.currentGroup = row;
      this.detailTitle = `${row.groupName} - 宣教统计详情`;
      this.detailActiveTab = "trend";
      this.detailVisible = true;
      this.loadDetailData();
    },
    // 加载详情数据
    async loadDetailData() {
      this.detailLoading = true;
      try {
        // 这里应该调用获取详情的接口
        // 模拟数据
        setTimeout(() => {
          this.detailList = [
            {
              patientName: "张三",
              patientNo: "P202312001",
              sendTime: "2023-12-01 10:30:00",
              readTime: "2023-12-01 14:20:00",
              educationTitle: "骨科术后康复指导",
              status: "已读",
            },
            {
              patientName: "李四",
              patientNo: "P202312002",
              sendTime: "2023-12-01 11:15:00",
              readTime: "",
              educationTitle: "心血管疾病预防",
              status: "发送成功",
            },
            {
              patientName: "王五",
              patientNo: "P202312003",
              sendTime: "2023-12-02 09:45:00",
              readTime: "2023-12-02 16:10:00",
              educationTitle: "糖尿病饮食指导",
              status: "已读",
            },
          ];
          this.detailLoading = false;
        }, 500);
      } catch (error) {
        console.error("加载详情数据失败:", error);
        this.$message.error("加载详情数据失败");
        this.detailLoading = false;
      }
    },
    // 查看详情
    handleDetail(row) {
      this.currentGroup = row;
      this.detailTitle = `${row.groupName} - 明细数据`;
      this.detailActiveTab = "detail";
      this.detailVisible = true;
      this.loadDetailData();
    },
    // 导出当前分组数据
    handleExportGroup(row) {
      this.$confirm(`确定要导出 "${row.groupName}" 的统计数据吗?`, "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          // 这里调用导出接口
          this.$message.success("导出任务已开始,请稍后在下载中心查看");
        })
        .catch(() => {});
    },
    // 导出全部数据
    handleExport() {
      this.$confirm("确定要导出全部统计数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          // 构建导出参数
          const exportParams = { ...this.queryParams };
          delete exportParams.pageNum;
          delete exportParams.pageSize;
          // 这里调用导出接口
          this.$message.success("导出任务已开始,请稍后在下载中心查看");
        })
        .catch(() => {});
    },
    // 关闭详情弹窗
    handleCloseDetail(done) {
      this.$confirm("确认关闭?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          done();
        })
        .catch(() => {});
    },
  },
};
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.education-statistics {
  padding: 20px;
  background: #fff;
  min-height: calc(100vh - 84px);
  .search-container {
    background: #f8f9fa;
    padding: 20px;
    border-radius: 8px;
    margin-bottom: 20px;
    border: 1px solid #ebeef5;
  }
  .summary-cards {
    margin-bottom: 20px;
    .summary-card {
      background: #fff;
      border-radius: 8px;
      padding: 20px;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
      text-align: center;
      border: 1px solid #ebeef5;
      transition: all 0.3s ease;
      &:hover {
        transform: translateY(-5px);
        box-shadow: 0 4px 20px 0 rgba(0, 0, 0, 0.15);
      }
      .card-title {
        font-size: 14px;
        color: #909399;
        margin-bottom: 10px;
      }
      .card-value {
        font-size: 28px;
        font-weight: 600;
        color: #409eff;
        margin-bottom: 5px;
      }
      .card-desc {
        font-size: 12px;
        color: #c0c4cc;
      }
    }
  }
  .table-container {
    background: #fff;
    border-radius: 8px;
    padding: 20px;
    border: 1px solid #ebeef5;
    .group-name {
      color: #409eff;
      cursor: pointer;
      text-decoration: underline;
      transition: color 0.3s;
      &:hover {
        color: #66b1ff;
      }
    }
    .count-highlight {
      font-weight: 600;
      color: #606266;
    }
    .success-count {
      color: #67c23a;
      font-weight: 600;
    }
    .read-count {
      color: #e6a23c;
      font-weight: 600;
    }
    .rate-text {
      display: block;
      margin-top: 5px;
      font-size: 12px;
      color: #606266;
    }
  }
  .detail-header {
    margin-bottom: 20px;
    padding: 20px;
    background: #f8f9fa;
    border-radius: 8px;
    .detail-item {
      label {
        color: #909399;
        font-size: 14px;
      }
      span {
        font-size: 16px;
        font-weight: 500;
        color: #303133;
      }
      .rate-highlight {
        color: #409eff;
        font-weight: 600;
      }
    }
  }
  .detail-tabs {
    margin-top: 20px;
  }
  .chart-placeholder {
    height: 300px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f8f9fa;
    border-radius: 8px;
    color: #909399;
    border: 1px dashed #dcdfe6;
  }
}
// 响应式调整
@media (max-width: 1200px) {
  .education-statistics {
    padding: 10px;
  }
  .summary-cards {
    .el-col {
      margin-bottom: 10px;
    }
  }
}
</style>