WXL (wul)
3 天以前 17efc8b0fb7d3fa66eb8e22f32b81e3e14bcd7f6
测试完成
已修改13个文件
已添加4个文件
711 ■■■■ 文件已修改
SY-wl.zip 补丁 | 查看 | 原始文档 | blame | 历史
dist (2).zip 补丁 | 查看 | 原始文档 | blame | 历史
dist.zip 补丁 | 查看 | 原始文档 | blame | 历史
lishui.zip 补丁 | 查看 | 原始文档 | blame | 历史
src/api/AiCentre/Followup.js 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/AiCentre/SingleTask.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/user.js 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CallButton/index.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService.js 66 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/index.vue 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/index.vue 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/propaganda/particty.vue 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sfstatistics/percentage/index.vue 427 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sfstatistics/percentage/satisfaction.vue 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
wailian.zip 补丁 | 查看 | 原始文档 | blame | 历史
SY-wl.zip
Binary files differ
dist (2).zip
Binary files differ
dist.zip
Binary files differ
lishui.zip
Binary files differ
src/api/AiCentre/Followup.js
@@ -45,7 +45,13 @@
    // data: data,
  });
}
export function getTaskFollowupList(data) {
  return request({
    url: "/smartor/ivrTaskTemplate/list",
    method: "post",
    data: data,
  });
}
// 新增随访模板分类树
export function addFollowupclassify(data) {
  return request({
src/api/AiCentre/SingleTask.js
@@ -136,6 +136,14 @@
    method: "get",
  });
}
// 获取语音任务模板详情
export function selectInfoByCondition(data) {
  return request({
     url: "/smartor/ivrTaskTemplate/selectInfoByCondition",
    method: "post",
    data: data,
  });
}
// 任务模板新增修改
export function TaskTemplatecomit(data) {
  return request({
src/api/system/user.js
@@ -94,17 +94,18 @@
    data: data,
  });
}
// 满意度明细查询
export function getSfStatisticsJoydetails(data) {
// 满意度统计
export function getSfStatisticsJoy(data) {
  return request({
    url: "/smartor/serviceSubtask/getSfStatisticsJoydetails",
    url: "/smartor/serviceSubtask/getSfStatisticsCount",
    method: "post",
    data: data,
  });
}// 满意度统计
export function getSfStatisticsJoy(data) {
}
// 满意度统计详情
export function getSfStatisticsJoyInfo(data) {
  return request({
    url: "/smartor/serviceSubtask/getSfStatisticsJoy",
    url: "/smartor/serviceSubtask/getSfStatisticsCountDetails",
    method: "post",
    data: data,
  });
@@ -119,7 +120,7 @@
}
// 任务随访率统计表的下钻明细
// sendstate = 2 待随访 5 待随访失败
// sendstate = 2 待随访 5 待随访失败
// preachform = 任务形式(1,人工 2,纸质  3,电话  4,短信  5.微信公众号 6.微信小程序 7.支付宝小程序  8.智能机器人  9.钉钉)
export function querySubtaskList(data) {
  return request({
src/components/CallButton/index.vue
@@ -55,8 +55,9 @@
      sipStatus: "未连接",
      sipStatusClass: "status-disconnected",
      sipConfig: {
        wsUrl: "wss://192.168.10.124:7443",
        sipUri: "",
        // 移除硬编码的wsUrl和domain
        wsUrl:'',
        sipUri:'',
        password: "Smartor@2023",
        displayName: "Web 小龙",
        // realm: "9.208.5.18:8090",
@@ -100,8 +101,9 @@
  },
  async mounted() {
    if (localStorage.getItem("orgname")=='景宁畲族自治县人民医院') {
      return
    const orgName = localStorage.getItem("orgname");
    if (orgName == "景宁畲族自治县人民医院") {
      return;
    }
    await this.CallgetList();
    this.isRegistering = true; // 开始注册
@@ -191,8 +193,13 @@
        const res = await CallgetList();
        this.randomNum = res.data[0].tel;
        this.randomID = res.data[0].id;
        // 正确设置 sipUri
        this.sipConfig.sipUri = `${this.randomNum}@192.168.10.124`;
        // 动态设置sipUri,域名部分会在sipService中动态处理
        const orgName = localStorage.getItem("orgname");
        if (orgName == "丽水市中医院") {
          this.sipConfig.sipUri = `${this.randomNum}@192.168.10.124`;
        } else if (orgName == "龙泉市人民医院") {
          this.sipConfig.sipUri = `${this.randomNum}@10.10.0.220`;
        }
      } catch (error) {
        console.error("获取分机号失败:", error);
        // this.updateStatus("failed", "获取分机号失败");
src/utils/sipService.js
@@ -1,6 +1,21 @@
import JsSIP from "jssip";
import { Notification, MessageBox, Message, Loading } from "element-ui";
// 医院机构与SIP服务器映射配置
const HOSPITAL_CONFIG = {
  丽水市中医院: {
    wsUrl: "wss://192.168.10.124:7443",
    domain: "192.168.10.124",
  },
  龙泉市人民医院: {
    wsUrl: "wss://10.10.0.220:7443",
    domain: "10.10.0.220",
  },
  // 可以继续添加其他医院配置
  default: {
    wsUrl: "wss://192.168.10.124:7443",
    domain: "192.168.10.124",
  },
};
class SipService {
  constructor() {
    this.ua = null;
@@ -10,17 +25,40 @@
    this.onIncomingCall = null;
    this.isRegistered = false; // 新增注册状态标志
    this.registrationTime = null; // 新增注册成功时间戳
    this.currentConfig = null; // 存储当前配置
  }
  init(config) {
  // 获取医院配置方法
  getHospitalConfig() {
    const orgName=localStorage.getItem("orgname");
    return HOSPITAL_CONFIG[orgName] || HOSPITAL_CONFIG.default;
  }
  init(baseConfig) {
    try {
      this.updateStatus("connecting", "连接中;...");
      // 获取机构名称,如果没有传入则从localStorage读取
      const orgName = baseConfig.orgName || localStorage.getItem("orgname");
      // 根据机构名称获取对应的服务器配置
      const hospitalConfig = this.getHospitalConfig(orgName);
console.log(hospitalConfig,'88');
      // 合并配置
      this.currentConfig = {
        ...baseConfig,
        ...hospitalConfig,
      };
      console.log(
        `当前机构: ${orgName}, 使用服务器: ${this.currentConfig.domain}`
      );
      this.updateStatus("connecting", "连接中...");
console.log(baseConfig.sipUri,'baseConfig.sipUri');
      this.ua = new JsSIP.UA({
        sockets: [new JsSIP.WebSocketInterface(config.wsUrl)],
        uri: config.sipUri,
        password: config.password,
        display_name: config.displayName,
        sockets: [new JsSIP.WebSocketInterface(this.currentConfig.wsUrl)],
        uri: baseConfig.sipUri, // 这里使用基础的sipUri,domain部分会被动态替换
        password: baseConfig.password,
        display_name: baseConfig.displayName,
        iceServers: [],
        register: true,
        sessionExpires: 1800,
@@ -78,7 +116,9 @@
      const remaining = minDelay - timeSinceRegistration;
      return {
        canCall: false,
        reason: `注册成功,资源加载中请等待 ${Math.ceil(remaining / 1000)} 秒后再呼叫`,
        reason: `注册成功,资源加载中请等待 ${Math.ceil(
          remaining / 1000
        )} 秒后再呼叫`,
      };
    }
@@ -99,7 +139,8 @@
        if (!this.ua.isRegistered()) {
          throw new Error("SIP未注册,无法呼叫");
        }
        const targetUri = `sip:${targetNumber}@${this.currentConfig.domain}`;
        console.log(`呼叫目标: ${targetUri}`);
        const options = {
          sessionTimers: true, // 启用会话计时器
          sessionTimersExpires: 150,
@@ -122,10 +163,7 @@
          },
        };
        this.currentSession = this.ua.call(
          `sip:${targetNumber}@192.168.10.124`,
          options
        );
        this.currentSession = this.ua.call(targetUri, options);
        this.setupPeerConnection(this.currentSession);
        this.setupAudio(this.currentSession);
src/views/followvisit/discharge/index.vue
@@ -1382,13 +1382,13 @@
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // if (this.endOut == 0) {
        // this.topqueryParams.endSendDateTime = this.formatDateToYYYYMMDDHHMMSS(
        //   this.getEndOfDay()
        // );
      // } else {
      //   this.topqueryParams.endSendDateTime = null;
      // }
      if (this.endOut == 0) {
        this.topqueryParams.endSendDateTime = this.formatDateToYYYYMMDDHHMMSS(
          this.getEndOfDay()
        );
      } else {
        this.topqueryParams.endSendDateTime = null;
      }
      // 接受异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
src/views/followvisit/record/index.vue
@@ -107,6 +107,17 @@
            @change="handleChange"
          ></el-cascader>
        </el-form-item>
         <el-form-item label="日期限制" prop="status">
          <el-select v-model="endOut" placeholder="请选择">
            <el-option
              v-for="item in endOuts"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="诊断名称" prop="leavediagname">
          <el-input
            v-model="topqueryParams.leavediagname"
@@ -847,7 +858,17 @@
      },
      value: [],
      list: [],
  endOut: localStorage.getItem("orgname") == "丽水市中医院" ? 0 : 1, //0 出院时间(正序)    1 出院时间(倒序)   2 发送时间(正序)    3 发送时间(倒序)  7应随访日期(倒序) 应随访日期(正序)
      endOuts: [
        {
          value: 0,
          label: "截止至当日服务",
        },
        {
          value: 1,
          label: "全部服务",
        },
      ],
      sourcetype: [
        {
          value: 1,
@@ -1042,6 +1063,13 @@
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
        if (this.endOut == 0) {
        this.topqueryParams.endSendDateTime = this.formatDateToYYYYMMDDHHMMSS(
          this.getEndOfDay()
        );
      } else {
        this.topqueryParams.endSendDateTime = null;
      }
      // 接受异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
@@ -1098,6 +1126,7 @@
        this.total = response.total;
      });
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
@@ -1113,6 +1142,21 @@
        }
      });
    },
    getEndOfDay() {
      const date = new Date(); // 创建一个表示当前时间的Date对象
      date.setHours(23, 59, 59, 0); // 将时间设置为23:59:59.000
      return date;
    },
     formatDateToYYYYMMDDHHMMSS(date) {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, "0"); // 月份补零
      const day = String(date.getDate()).padStart(2, "0"); // 日期补零
      const hours = String(date.getHours()).padStart(2, "0");
      const minutes = String(date.getMinutes()).padStart(2, "0");
      const seconds = String(date.getSeconds()).padStart(2, "0");
      return `${year}-${month}-${day}`;
    },
    buidegetTasklist(type) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
src/views/index.vue
@@ -3,7 +3,7 @@
    <div class="home" style="margin-top: 40px; margin-left: 20px">
      <!-- 头部两个 -->
      <el-row :gutter="20">
        <el-col :span="11">
        <el-col :span="8">
          <!-- 就诊统计盒子 -->
          <div class="grid-content bg-purple headerBox bgc1">
            <div class="title">
@@ -45,7 +45,7 @@
            </el-row>
          </div>
        </el-col>
        <el-col :span="9">
        <el-col :span="12">
          <div class="grid-content bg-purple headerBox bgc2">
            <div class="title">
              出院服务
@@ -60,7 +60,8 @@
              <span>{{ endatd }} ~ {{ statd }}</span>
            </div>
            <el-row :gutter="20">
              <el-col :span="10">
              <!-- 出院就诊量 -->
              <el-col :span="6">
                <div class="home-user-task-stats">
                  <p style="color: red">{{ DischargeData.rc }}</p>
                </div>
@@ -68,12 +69,31 @@
                  <p>出院就诊量(人次)</p>
                </div>
              </el-col>
              <el-col :span="10">
              <!-- 首次服务 -->
              <el-col :span="6">
                <div class="home-user-task-stats">
                  <p style="color: red">{{ DischargeData.rs }}</p>
                  <div class="text-color2">
                    <p>出院服务量(人次)</p>
                  </div>
                  <p style="color: #67c23a">{{ DischargeData.scsf }}</p>
                </div>
                <div class="text-color2" style="color: #67c23a">
                  <p>首次服务(人次)</p>
                </div>
              </el-col>
              <!-- 再次服务 -->
              <el-col :span="6">
                <div class="home-user-task-stats">
                  <p style="color: #e6a23c">{{ DischargeData.zcsf }}</p>
                </div>
                <div class="text-color2" style="color: #e6a23c">
                  <p>再次服务(人次)</p>
                </div>
              </el-col>
              <!-- 专病服务 -->
              <el-col :span="6">
                <div class="home-user-task-stats">
                  <p style="color: #409eff">{{ DischargeData.zbsf }}</p>
                </div>
                <div class="text-color2" style="color: #409eff">
                  <p>专病服务(人次)</p>
                </div>
              </el-col>
            </el-row>
@@ -336,8 +356,11 @@
        年: "year",
      },
      DischargeData: {
        rs: "",
        rc: "",
        rc: "", // 出院就诊量(总人次)
        rs: "", // 出院服务量(总人次)
        scsf: 0, // 新增:首次服务人次
        zcsf: 0, // 新增:再次服务人次
        zbsf: 0, // 新增:专病服务人次
      },
      OutpatientData: {
        rs: "",
src/views/patient/propaganda/particty.vue
@@ -743,7 +743,7 @@
                    <el-radio
                      v-for="(
                        items, index
                      ) in item.ivrTaskScriptTargetoptionList"
                      ) in item.ivrLibaScriptTargetoptionList"
                      :key="index"
                      :label="index"
                      >{{ items.targetvalue }}</el-radio
@@ -765,7 +765,7 @@
                    <el-checkbox
                      v-for="(
                        items, index
                      ) in item.ivrTaskScriptTargetoptionList"
                      ) in item.ivrLibaScriptTargetoptionList"
                      :key="index"
                      :label="index"
                    >
@@ -904,6 +904,7 @@
  getbaseopera,
  getFollowuplist,
  getvFollowup,
  selectInfoByCondition,
  Taskparticty,
  deleteTaskparticty,
  getTaskInfo,
@@ -1610,13 +1611,18 @@
          // 疾病
          this.getillness(this.form.libtemplateid);
          getvFollowup({ id: this.form.libtemplateid }).then((res) => {
          selectInfoByCondition({id:this.form.templateid} ).then((res) => {
            if (res.code == 200) {
              this.previewtf = true;
              this.previewtftype = 1;
              this.questionList = res.data.ivrLibaTemplateScriptVOList;
              this.objyl.ivrLibaTemplateScriptVOList =
                res.data.ivrLibaTemplateScriptVOList;
              this.questionList = res.data.ivrTaskTemplateScriptVOList;
              this.questionList.forEach((item) => {
                item.qremark = [];
                item.ivrLibaScriptTargetoptionList =
                  item.ivrTaskScriptTargetoptionList;
              });
              this.objyl.ivrLibaScriptTargetoptionList =
                res.data.ivrTaskScriptTargetoptionList;
            } else {
            }
          });
@@ -2201,7 +2207,7 @@
        this.objyl.suitway = this.objyl.suitway.join(",");
      }
      this.objyl.ivrLibaTemplateScriptVOList.forEach((item) => {
        item.ivrTaskScriptTargetoptionList = item.ivrTaskScriptTargetoptionList;
        item.ivrTaskScriptTargetoptionList = item.ivrLibaScriptTargetoptionList;
      });
      this.objyl.ivrTaskTemplateScriptVOList =
        this.objyl.ivrLibaTemplateScriptVOList;
@@ -2319,7 +2325,7 @@
            this.objyl.isoperation = 1;
            this.objyl.ivrLibaTemplateScriptVOList.forEach((item) => {
              item.ivrTaskScriptTargetoptionList =
                item.ivrTaskScriptTargetoptionList;
                item.ivrLibaScriptTargetoptionList;
            });
            this.objyl.ivrTaskTemplateScriptVOList =
              this.objyl.ivrLibaTemplateScriptVOList;
src/views/sfstatistics/percentage/index.vue
@@ -88,8 +88,9 @@
                  range-separator="至"
                  start-placeholder="开始日期"
                  end-placeholder="结束日期"
                  :default-time="['00:00:00', '23:59:59']">
                  :default-time="['00:00:00', '23:59:59']"
                >
                  >
                </el-date-picker>
              </el-form-item>
@@ -401,6 +402,21 @@
                    key="pendingFollowUp"
                    prop="pendingFollowUp"
                  >
                    <template slot-scope="scope">
                      <el-button
                        size="medium"
                        type="text"
                        @click="
                          viewDetails(
                            scope.row.pendingFollowUpInfo,
                            scope.row.leavehospitaldistrictname + '待随访列表'
                          )
                        "
                        ><span class="button-zx">{{
                          scope.row.pendingFollowUp
                        }}</span></el-button
                      >
                    </template>
                  </el-table-column>
                  <el-table-column
                    label="随访成功"
@@ -415,6 +431,21 @@
                    key="followUpFail"
                    prop="followUpFail"
                  >
                    <template slot-scope="scope">
                      <el-button
                        size="medium"
                        type="text"
                        @click="
                          viewDetails(
                            scope.row.followUpFailInfo,
                            scope.row.leavehospitaldistrictname + '随访失败列表'
                          )
                        "
                        ><span class="button-zx">{{
                          scope.row.followUpFail
                        }}</span></el-button
                      >
                    </template>
                  </el-table-column>
                  <el-table-column
                    label="随访率"
@@ -855,6 +886,198 @@
        </div>
      </div>
    </el-dialog>
    <!-- 各类详情 -->
    <el-dialog
      :title="infotitle"
      :visible.sync="infotitleVisible"
      v-loading="infotitloading"
      width="70%"
      :close-on-click-modal="false"
    >
      <div class="examine-jic">
        <div class="jic-value">
          <el-row :gutter="20">
            <!-- 选择患者列表 -->
            <el-table :data="infotitlelist" height="660" style="width: 100%">
              <el-table-column
                prop="sendname"
                align="center"
                label="姓名"
                width="100"
              >
              </el-table-column>
              <el-table-column
                prop="taskName"
                align="center"
                width="200"
                show-overflow-tooltip
                label="任务名称"
              >
              </el-table-column>
              <el-table-column
                prop="sendstate"
                align="center"
                width="200"
                label="任务状态"
              >
                <template slot-scope="scope">
                  <div v-if="scope.row.sendstate == 1">
                    <el-tag type="primary" :disable-transitions="false"
                      >表单已领取</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 2">
                    <el-tag type="primary" :disable-transitions="false"
                      >待随访</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 3">
                    <el-tag type="success" :disable-transitions="false"
                      >表单已发送</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 4">
                    <el-tag type="info" :disable-transitions="false"
                      >不执行</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 5">
                    <el-tag type="danger" :disable-transitions="false"
                      >发送失败</el-tag
                    >
                  </div>
                  <div v-if="scope.row.sendstate == 6">
                    <el-tag type="success" :disable-transitions="false"
                      >已完成</el-tag
                    >
                  </div>
                </template>
              </el-table-column>
              <el-table-column
                prop="visitTime"
                align="center"
                label="应随访时间"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                prop="finishtime"
                align="center"
                label="随访完成时间"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                label="出院日期"
                width="200"
                align="center"
                key="endtime"
                prop="endtime"
              >
                <template slot-scope="scope">
                  <span>{{ formatTime(scope.row.endtime) }}</span>
                </template></el-table-column
              >
              <el-table-column
                label="责任护士"
                width="120"
                align="center"
                key="nurseName"
                prop="nurseName"
              />
              <el-table-column
                label="主治医生"
                width="120"
                align="center"
                key="drname"
                prop="drname"
              />
              <el-table-column
                label="结果状态"
                align="center"
                key="excep"
                prop="excep"
                width="120"
              >
                <template slot-scope="scope">
                  <dict-tag
                    :options="dict.type.sys_yujing"
                    :value="scope.row.excep"
                  />
                </template>
              </el-table-column>
              <el-table-column
                label="处理意见"
                align="center"
                key="suggest"
                prop="suggest"
                width="120"
              >
                <template slot-scope="scope">
                  <dict-tag
                    :options="dict.type.sys_suggest"
                    :value="scope.row.suggest"
                  />
                </template>
              </el-table-column>
              <el-table-column
                prop="templatename"
                align="center"
                label="服务模板"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                prop="remark"
                align="center"
                label="服务记录"
                width="200"
                show-overflow-tooltip
              >
              </el-table-column>
              <el-table-column
                prop="bankcardno"
                align="center"
                label="呼叫状态"
                width="210"
              >
              </el-table-column>
              <el-table-column
                label="操作"
                fixed="right"
                align="center"
                width="200"
                class-name="small-padding fixed-width"
              >
                <template slot-scope="scope">
                  <el-button
                    size="medium"
                    type="text"
                    @click="SeedetailsgGo(scope.row)"
                    ><span class="button-zx"
                      ><i class="el-icon-s-order"></i>查看</span
                    ></el-button
                  >
                </template>
              </el-table-column>
            </el-table>
          </el-row>
          <pagination
            v-show="patienttotal > 0 && this.patientqueryParams.allhosp != 6"
            :total="patienttotal"
            :page.sync="patientqueryParams.pn"
            :limit.sync="patientqueryParams.ps"
            @pagination="Seedetails"
          />
        </div>
      </div>
    </el-dialog>
  </div>
</template>
@@ -907,10 +1130,14 @@
      activeName: "first", //侧边选择
      orgname: "",
      expands: [],
      infotitlelist: [],
      // 遮罩层
      loading: false,
      Seedloading: false,
      chartDialogVisible: false,
      infotitleVisible: false,
      infotitloading: false,
      infotitle: "",
      pieChart: null,
      barLineChart: null,
      // 选中数组
@@ -1330,6 +1557,11 @@
        this.Seedloading = false;
      });
    },
    viewDetails(row, title) {
      this.infotitleVisible = true;
      this.infotitle = title;
      this.infotitlelist = row;
    },
    SeedetailsgGo(row) {
      this.SeedetailsVisible = false;
      let type = "";
@@ -1478,8 +1710,32 @@
        const excelName = `${currentMonth}月出院随访统计表.xlsx`;
        // 创建新的工作簿和工作表
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet("随访统计");
        const worksheet = workbook.addWorksheet(`${currentMonth}月出院随访统计表`);
// 定义样式(新增总标题样式)
    const titleStyle = {
      font: {
        name: "微软雅黑",
        size: 16,
        bold: true,
        color: { argb: "FF000000" },
      },
      fill: {
        type: "pattern",
        pattern: "solid",
        fgColor: { argb: "FFE6F3FF" },
      },
      alignment: {
        vertical: "middle",
        horizontal: "center",
        wrapText: true,
      },
      border: {
        top: { style: "thin", color: { argb: "FFD0D0D0" } },
        left: { style: "thin", color: { argb: "FFD0D0D0" } },
        bottom: { style: "thin", color: { argb: "FFD0D0D0" } },
        right: { style: "thin", color: { argb: "FFD0D0D0" } },
      },
    };
        // 定义样式
        const headerStyle = {
          font: {
@@ -1547,7 +1803,12 @@
            right: { style: "thin", color: { argb: "FFD0D0D0" } },
          },
        };
// 1. 添加总标题行(第一行)
    worksheet.mergeCells(1, 1, 1, 23); // 合并A1到W1的所有列[1,4](@ref)
    const titleCell = worksheet.getCell(1, 1);
    titleCell.value = `${currentMonth}月出院随访统计表`; // 使用文件名作为总标题
    titleCell.style = titleStyle;
    worksheet.getRow(1).height = 35; // 设置总标题行高
        // 1. 首先,创建并设置第二行(子表头)的所有单元格
        const secondRowHeaders = [
          "", // A2 展开列占位(其值将由第一行合并后的主单元格决定)
@@ -1577,96 +1838,96 @@
          "微信",
        ];
        // 添加第二行并设置样式
        secondRowHeaders.forEach((header, index) => {
          // 注意:列索引从1开始,对应A列是1,B列是2,以此类推。
          const cell = worksheet.getCell(2, index + 1);
          cell.value = header;
          cell.style = headerStyle;
        });
        // 添加第二行(原第一行下移)
    secondRowHeaders.forEach((header, index) => {
      const cell = worksheet.getCell(3, index + 1); // 改为第3行
      cell.value = header;
      cell.style = headerStyle;
    });
        // 2. 然后,创建第一行的主标题单元格并设置样式,紧接着进行纵向合并
        // 合并 A1:A2 并设置值
        worksheet.mergeCells(1, 1, 2, 1); // 合并 A1 到 A2
        worksheet.getCell(1, 1).value = ""; // 设置主单元格(A1)的值
        worksheet.getCell(1, 1).style = headerStyle; // 设置主单元格样式
    // 3. 调整原合并单元格位置(原第1行合并单元格下移到第2行)
    // 合并 A2:A3
    worksheet.mergeCells(2, 1, 3, 1);
    worksheet.getCell(2, 1).value = "";
    worksheet.getCell(2, 1).style = headerStyle;
        // 合并 B1:B2 并设置值
        worksheet.mergeCells(1, 2, 2, 2); // 合并 B1 到 B2
        worksheet.getCell(1, 2).value = "出院病区";
        worksheet.getCell(1, 2).style = headerStyle;
    // 合并 B2:B3
    worksheet.mergeCells(2, 2, 3, 2);
    worksheet.getCell(2, 2).value = "出院病区";
    worksheet.getCell(2, 2).style = headerStyle;
        // 合并 C1:C2 并设置值
        worksheet.mergeCells(1, 3, 2, 3); // 合并 C1 到 C2
        worksheet.getCell(1, 3).value = "科室";
        worksheet.getCell(1, 3).style = headerStyle;
    // 合并 C2:C3
    worksheet.mergeCells(2, 3, 3, 3);
    worksheet.getCell(2, 3).value = "科室";
    worksheet.getCell(2, 3).style = headerStyle;
        // 合并 D1:D2 并设置值
        worksheet.mergeCells(1, 4, 2, 4); // 合并 D1 到 D2
        worksheet.getCell(1, 4).value = "出院人次";
        worksheet.getCell(1, 4).style = headerStyle;
    // 合并 D2:D3
    worksheet.mergeCells(2, 4, 3, 4);
    worksheet.getCell(2, 4).value = "出院人次";
    worksheet.getCell(2, 4).style = headerStyle;
        // 合并 E1:E2 并设置值
        worksheet.mergeCells(1, 5, 2, 5); // 合并 E1 到 E2
        worksheet.getCell(1, 5).value = "无需随访人次";
        worksheet.getCell(1, 5).style = headerStyle;
    // 合并 E2:E3
    worksheet.mergeCells(2, 5, 3, 5);
    worksheet.getCell(2, 5).value = "无需随访人次";
    worksheet.getCell(2, 5).style = headerStyle;
        // 合并 F1:F2 并设置值
        worksheet.mergeCells(1, 6, 2, 6); // 合并 F1 到 F2
        worksheet.getCell(1, 6).value = "应随访人次";
        worksheet.getCell(1, 6).style = headerStyle;
    // 合并 F2:F3
    worksheet.mergeCells(2, 6, 3, 6);
    worksheet.getCell(2, 6).value = "应随访人次";
    worksheet.getCell(2, 6).style = headerStyle;
        // 3. 设置第一行的横向合并标题(这些保持不变,因为只涉及第一行)
        // 首次出院随访(合并G1到O1)
        worksheet.mergeCells("G1:O1");
        worksheet.getCell("G1").value = "首次出院随访";
        worksheet.getCell("G1").style = headerStyle;
    // 4. 调整横向合并标题位置(下移到第2行)
    // 首次出院随访(合并G2:O2)
    worksheet.mergeCells(2, 7, 2, 15); // G2:O2
    worksheet.getCell(2, 7).value = "首次出院随访";
    worksheet.getCell(2, 7).style = headerStyle;
        // 再次出院随访(合并P1到W1)
        worksheet.mergeCells("P1:W1");
        worksheet.getCell("P1").value = "再次出院随访";
        worksheet.getCell("P1").style = headerStyle;
    // 再次出院随访(合并P2:W2)
    worksheet.mergeCells(2, 16, 2, 23); // P2:W2
    worksheet.getCell(2, 16).value = "再次出院随访";
    worksheet.getCell(2, 16).style = headerStyle;
        // 4. 设置行高(可选,但建议设置)
        worksheet.getRow(1).height = 28; // 第一行行高
        worksheet.getRow(2).height = 25; // 第二行行高
    // 5. 设置行高
    worksheet.getRow(1).height = 35; // 总标题行高
    worksheet.getRow(2).height = 28; // 原第一行下移
    worksheet.getRow(3).height = 25; // 原第二行下移
        // 添加数据行
        this.userList.forEach((item, rowIndex) => {
          const dataRow = worksheet.addRow([
            "", // 展开列
            item.leavehospitaldistrictname || "",
            item.deptname || "",
            item.dischargeCount || 0,
            item.nonFollowUp || 0,
            item.followUpNeeded || 0,
            // 首次出院随访数据
            item.needFollowUp || 0,
            item.pendingFollowUp || 0,
            item.followUpSuccess || 0,
            item.followUpFail || 0,
            item.followUpRate || "0%",
            item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%",
            item.manual || 0,
            item.sms || 0,
            item.weChat || 0,
            // 再次出院随访数据
            item.needFollowUpAgain || 0,
            item.pendingFollowUpAgain || 0,
            item.followUpSuccessAgain || 0,
            item.followUpFailAgain || 0,
            item.followUpRateAgain || "0%",
            item.manualAgain || 0,
            item.smsAgain || 0,
            item.weChatAgain || 0,
          ]);
    // 6. 添加数据行(注意行索引需要+1,因为上面插入了一行)
    this.userList.forEach((item, rowIndex) => {
      const dataRow = worksheet.addRow([
        "", // 展开列
        item.leavehospitaldistrictname || "",
        item.deptname || "",
        item.dischargeCount || 0,
        item.nonFollowUp || 0,
        item.followUpNeeded || 0,
        // 首次出院随访数据
        item.needFollowUp || 0,
        item.pendingFollowUp || 0,
        item.followUpSuccess || 0,
        item.followUpFail || 0,
        item.followUpRate || "0%",
        item.rate ? (Number(item.rate) * 100).toFixed(2) + "%" : "0%",
        item.manual || 0,
        item.sms || 0,
        item.weChat || 0,
        // 再次出院随访数据
        item.needFollowUpAgain || 0,
        item.pendingFollowUpAgain || 0,
        item.followUpSuccessAgain || 0,
        item.followUpFailAgain || 0,
        item.followUpRateAgain || "0%",
        item.manualAgain || 0,
        item.smsAgain || 0,
        item.weChatAgain || 0,
      ], rowIndex + 4); // 从第4行开始添加数据(原第3行)
          // 应用数据行样式
          dataRow.eachCell((cell) => {
            cell.style = cellStyle;
          });
          dataRow.height = 24;
        });
      // 应用数据行样式
      dataRow.eachCell((cell) => {
        cell.style = cellStyle;
      });
      dataRow.height = 24;
    });
        // 添加合计行
        const summaries = this.getSummaries({
src/views/sfstatistics/percentage/satisfaction.vue
@@ -61,7 +61,7 @@
                </el-select>
              </el-form-item>
              <el-form-item label="统计题目" prop="userName">
              <el-form-item label="服务类型" prop="userName">
                <el-select
                  v-model="queryParams.serviceType"
                  multiple
@@ -224,10 +224,8 @@
              >
                <template slot-scope="scope">
                  <span class="button-zx"
                      >{{
                        (Number(scope.row.joyTotal) * 100).toFixed(2)
                      }}%</span
                    >
                    >{{ (Number(scope.row.joyTotal) * 100).toFixed(2) }}%</span
                  >
                </template>
              </el-table-column>
              <el-table-column
@@ -250,13 +248,13 @@
              </el-table-column>
            </el-table>
            <!-- <pagination
            <pagination
              v-show="total > 0"
              :total="total"
              :page.sync="queryParams.pageNum"
              :limit.sync="queryParams.pageSize"
              @pagination="getList"
            /> -->
            />
          </el-col>
        </el-row>
      </div>
@@ -510,7 +508,7 @@
      </div>
    </el-dialog>
    <!-- 单科室统计详情 -->
     <el-dialog :visible.sync="topicVisible" width="45%">
    <el-dialog :visible.sync="topicVisible" width="45%">
      <div class="topicdia">
        <div class="top-text">{{ topicvalue.name }}</div>
        <div class="top-mintext">随访完成数{{ topicvalue.number }}</div>
@@ -549,8 +547,8 @@
} from "@/api/system/label";
import store from "@/store";
import {
  getSfStatisticsJoydetails,
  getSfStatisticsJoy,
  getSfStatisticsJoyInfo,
  selectTimelyRate,
} from "@/api/system/user";
@@ -641,7 +639,7 @@
          type: 1,
        },
      ],
        tableData: [
      tableData: [
        {
          date: "好",
          name: 12,
@@ -752,6 +750,7 @@
    getList() {
      // 处理查询参数
      const params = {
        configKey: "joyCount",
        ...this.queryParams,
        // 如果选择了"全部",则传所有病区/科室代码
        leavehospitaldistrictcodes:
@@ -768,7 +767,7 @@
      delete params.deptcodes.all;
      getSfStatisticsJoy(params).then((response) => {
        console.log(response);
        // this.total = response.total;
        this.total = response.total;
        this.userList = response.data;
      });
    },
@@ -887,7 +886,25 @@
    // 调起详情
    getinfo(row) {
      this.topicVisible = true;
      // 处理查询参数
      const params = {
        configKey: "joyCount",
        ...this.queryParams,
      };
      if (this.queryParams.statisticaltype == 1) {
        params.leavehospitaldistrictcodes = [row.leavehospitaldistrictcode];
      } else {
        params.deptcodes = [row.deptcode];
      }
      // 移除可能存在的"all"值
      delete params.leavehospitaldistrictcodes.all;
      delete params.deptcodes.all;
      getSfStatisticsJoyInfo(params).then((response) => {
        console.log(response);
        this.total = response.total;
        this.userList = response.data;
      });
    },
    // 添加/修改标签
    Maintenancetag() {
vue.config.js
@@ -36,10 +36,10 @@
      // detail: https://cli.vuejs.org/config/#devserver-proxy
      [process.env.VUE_APP_BASE_API]: {
        // target: `https://www.health-y.cn/lssf`,
        target: `http://192.168.100.10:8096`,
        // target: `http://192.168.100.10:8096`,
        // target: `http://192.168.100.10:8094`,//省立同德
        // target: `http://192.168.100.10:8095`,//新华
        // target:`http://localhost:8095`,
        target:`http://localhost:8095`,
        // target:`http://35z1t16164.qicp.vip`,
        // target: `http://192.168.100.111:8095`,
        // target: `http://192.168.101.166:8093`,
wailian.zip
Binary files differ