WXL (wul)
6 天以前 a5cba9ab040f6f97558046885dce3017f9bab7e9
src/views/sfstatistics/propaganda/index.vue
@@ -1,17 +1,777 @@
<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">
            <el-button
              size="medium"
              type="text"
              @click="
                handleViewDetails(
                  scope.row,
                  'totalCountInfo',
                  '宣教发送总量明细'
                )
              "
            >
              <span class="count-highlight">{{ scope.row.totalCount }}</span>
            </el-button>
          </template>
        </el-table-column>
        <el-table-column
          prop="sendSuccessCount"
          label="发送成功量"
          align="center"
          sortable="custom"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                handleViewDetails(
                  scope.row,
                  'sendSuccessCountInfo',
                  '发送成功量明细'
                )
              "
            >
              <span class="success-count">{{ scope.row.sendSuccessCount }}</span>
            </el-button>
          </template>
        </el-table-column>
        <el-table-column
          prop="readCount"
          label="已读量"
          align="center"
          sortable="custom"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                handleViewDetails(scope.row, 'readCountInfo', '已读量明细')
              "
            >
              <span class="read-count">{{ scope.row.readCount }}</span>
            </el-button>
          </template>
        </el-table-column>
        <el-table-column
          prop="sendSuccessRate"
          label="发送成功率"
          align="center"
          width="200"
          sortable="custom"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                handleViewDetails(
                  scope.row,
                  'sendSuccessRate',
                  '发送成功率明细'
                )
              "
            >
              <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
              >
            </el-button>
          </template>
        </el-table-column>
        <el-table-column
          prop="readRate"
          label="已读率"
          align="center"
          width="200"
          sortable="custom"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                handleViewDetails(scope.row, 'readRate', '已读率明细')
              "
            >
              <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
              >
            </el-button>
          </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>
    <!-- 明细数据弹窗 -->
    <PropagandaDetailDialog
      :visible="detailVisible"
      :title="detailTitle"
      :data="detailList"
      :loading="detailLoading"
      @close="detailVisible = false"
    />
  </div>
</template>
<script>
import { gethelibraryCount, gethelibraryCountHyperlink } from "@/api/AiCentre/index";
import PropagandaDetailDialog from "./components/PropagandaDetailDialog.vue";
export default {
  name: "EducationStatistics",
  components: { PropagandaDetailDialog },
  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: "",
      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";
    },
    // 点击统计数字查看明细(仿随访统计模式)
    handleViewDetails(row, infoKey, titleSuffix) {
      const groupName = row.groupName || "";
      this.detailTitle = `${groupName} - ${titleSuffix}`;
      this.detailVisible = true;
      this.detailLoading = true;
      this.detailList = [];
      const params = {
        hyperLinkInfoType: infoKey,
        hospType: this.queryParams.hospType,
      };
      // 根据统计维度传入对应的过滤参数
      if (this.queryParams.groupType == "1") {
        // 按科室统计
        params.deptcodes = [row.groupCode];
      } else if (this.queryParams.groupType == "2") {
        // 按病区统计
        params.hospitaldistrictcodes = [row.groupCode];
      }
      // 传入时间范围
      if (
        this.queryParams.dateRange &&
        this.queryParams.dateRange.length === 2
      ) {
        params.starttime = this.queryParams.dateRange[0];
        params.endtime = this.queryParams.dateRange[1];
      }
      if (this.queryParams.visittime) {
        params.visittime = this.queryParams.visittime;
      }
      gethelibraryCountHyperlink(params)
        .then((response) => {
          this.detailList = response.data || [];
          this.detailLoading = false;
        })
        .catch(() => {
          this.detailLoading = false;
        });
    },
    // 导出全部数据
    handleExport() {
      this.$confirm("确定要导出全部统计数据吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          const exportParams = { ...this.queryParams };
          delete exportParams.pageNum;
          delete exportParams.pageSize;
          this.$message.success("导出任务已开始,请稍后在下载中心查看");
        })
        .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;
    }
  }
}
// 响应式调整
@media (max-width: 1200px) {
  .education-statistics {
    padding: 10px;
  }
  .summary-cards {
    .el-col {
      margin-bottom: 10px;
    }
  }
}
</style>