From a5cba9ab040f6f97558046885dce3017f9bab7e9 Mon Sep 17 00:00:00 2001
From: WXL (wul) <wl_5969728@163.com>
Date: 星期二, 11 八月 2026 14:12:29 +0800
Subject: [PATCH] 提交

---
 src/views/sfstatistics/propaganda/index.vue |  770 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 765 insertions(+), 5 deletions(-)

diff --git a/src/views/sfstatistics/propaganda/index.vue b/src/views/sfstatistics/propaganda/index.vue
index c957b81..5f258a4 100644
--- a/src/views/sfstatistics/propaganda/index.vue
+++ b/src/views/sfstatistics/propaganda/index.vue
@@ -1,17 +1,777 @@
 <template>
-  <div>瀹f暀缁熻</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="瀹f暀鍙戦�佹椂闂�" 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">宸叉垚鍔熷彂閫佺殑瀹f暀</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">鎮h�呭凡闃呰鐨勫鏁�</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="瀹f暀鍙戦�佹�婚噺"
+          align="center"
+          sortable="custom"
+        >
+          <template slot-scope="scope">
+            <el-button
+              size="medium"
+              type="text"
+              @click="
+                handleViewDetails(
+                  scope.row,
+                  'totalCountInfo',
+                  '瀹f暀鍙戦�佹�婚噺鏄庣粏'
+                )
+              "
+            >
+              <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, // 瀹f暀鍙戦�佹椂闂�
+        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>

--
Gitblit v1.9.3