WXL (wul)
2025-11-13 de147dda682f8ac597bbcc8555b57acbdf45dba2
测试完成
已修改65个文件
已添加31个文件
33040 ■■■■■ 文件已修改
SLTD-WL.zip 补丁 | 查看 | 原始文档 | blame | 历史
src/api/AiCentre/EChartsdata.js 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/AiCentre/index.js 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/AiCentre/phoneCall.js 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/AiCentre/satisfaction.js 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/serviceGroup.js 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/system/user.js 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/order/BHostClient.exe 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/order/Xftp-8.0.0084p.exe 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/element-variables.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/sidebar.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/styles/variables.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CallButton/index.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CallCenterLs/index.vue 717 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/PatientSelection/index.vue 585 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/SortCheckbox/index.vue 289 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/layout/components/TagsView/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main.js 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/permission.js 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/smartor/dataobject/dw_patouthosp_base.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/smartor/dataobject/dw_patouthosp_list.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/getters.js 125 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 369 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/directives/preventReClick.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/request.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService-cs.js 363 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService.js 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/complaint/complaintlist/index.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/complaint/complaintmy/index.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/OutpatientAgain/index.vue 1845 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/SpecificDisease/index.vue 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/again/index.vue 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/complaint/index.vue 1963 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/ClickCall copy.vue 1258 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/ClickCall.vue 1502 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/index.vue 203 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/js/cti-manager.js 202 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/js/json2.js 506 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/js/prototype.js 4874 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/js/websocket.js 826 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/discharge/outpatientService.vue 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/linem/index.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/mzsatisfaction/index.vue 1976 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/outpatient/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/detailpage/index.vue 1062 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/index.vue 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/physical/index.vue 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/tasklist/index.vue 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/technology/index.vue 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/zbAgain/index.vue 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/zysatisfaction/index.vue 1967 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/groupManagement/PatientGroup/index.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/groupManagement/PatientGroup/particulars.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/groupManagement/PersonnelGroup/index.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/groupManagement/PersonnelGroup/particulars.vue 补丁 | 查看 | 原始文档 | blame | 历史
src/views/groupManagement/serviceGroup/index.vue 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/groupManagement/serviceGroup/particulars.vue 518 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/knowledge/education/compilequer/index copy.vue 1322 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/knowledge/education/compilequer/index.vue 447 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/knowledge/education/index.vue 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/knowledge/questionbank/particulars/index.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/knowledge/questionnaire/compilequer/index.vue 473 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/knowledge/questionnaire/index.vue 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login.vue 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/loginSSO.vue 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/outsideChainnew.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/outsideChainwt.vue 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/outsideChainwtnew.vue 963 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/outsideChainxjnew.vue 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/follow/index.vue 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/medtechnician/PatientChart.vue 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/AwaitingAdmission.vue 1257 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/ExternalPatient.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/behospitalized.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/hospital.vue 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/index.vue 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/indexls.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/operation.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/outpatient.vue 57 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/patient/physical.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/physical/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/propaganda/Missioncreation.vue 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/propaganda/Missionotice.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/propaganda/QuestionnaireTask.vue 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/propaganda/index.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/propaganda/particty.vue 374 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/questionnaire/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/shadow/index.vue 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/patient/subsequent/index.vue 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/satisfaction.vue 1188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sfstatistics/percentage/index.vue 1639 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sfstatistics/percentage/satisfaction.vue 1533 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/shortmessage/healthinformation/compilequer/index.vue 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/system/user/index.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
vue.config.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
SLTD-WL.zip
Binary files differ
src/api/AiCentre/EChartsdata.js
@@ -59,3 +59,19 @@
    data: data,
  });
}
// èŽ·å–orgid
// å†æ¬¡éšè®¿æœåŠ¡
export function getorganization(data) {
  return request({
    url: "/smartor/organization/list",
    method: "get",
  });
}
// å‘送短信
export function sendMsg(data) {
  return request({
    url: "/sms/send",
    method: "post",
    data: data
  });
}
src/api/AiCentre/index.js
@@ -1,12 +1,14 @@
export * from './indicator'
export * from './Problemspeaking'
export * from './Followup'
export * from './general'
export * from './publicity'
export * from './Qtemplate'
export * from './questionnaire'
export * from './SingleTask'
export * from './external'
export * from './patientexternal'
export * from './EChartsdata'
export * from './satisfactionse'
export * from "./indicator";
export * from "./Problemspeaking";
export * from "./Followup";
export * from "./general";
export * from "./publicity";
export * from "./Qtemplate";
export * from "./questionnaire";
export * from "./SingleTask";
export * from "./external";
export * from "./patientexternal";
export * from "./EChartsdata";
export * from "./satisfactionse";
export * from "./satisfaction";
export * from "./phoneCall";
src/api/AiCentre/phoneCall.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
import request from "@/utils/request";
// åˆ é™¤å¤–部患者表
export function CallgetList() {
  return request({
    url: "/smartor/ServiceTelInfo/getList",
    method: "get",
    params: {
      orgid: localStorage.getItem("orgid"),
    },
  });
}
// æŸ¥è¯¢å¤–部患者表
export function CallsetState(data) {
  return request({
    url: "/smartor/ServiceTelInfo/setState",
    method: "get",
    params: data,
  });
}
src/api/AiCentre/satisfaction.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
import request from "@/utils/request";
// å¤–链获取随访
export function getScriptByCondition(data) {
  return request({
    url: "/smartor/servicetask/getScriptByCondition",
    method: "post",
    data: data
  });
}
// ç¼“存问卷
export function saveMYDQuestionAnswer(data) {
  return request({
    url: "/smartor/subtaskAnswer/saveMYDQuestionAnswer",
    method: "post",
    data: data
  });
}
// ç¼“存问卷
export function WLgetDept(orgid) {
  return request({
    url: "/getDept/" + orgid,
    method: "get",
  });
}
src/api/system/serviceGroup.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
import request from '@/utils/request'
// æŸ¥è¯¢æœåŠ¡ç»„åˆ—è¡¨
export function listServiceGroup(query) {
  return request({
    url: '/system/serviceGroup/list',
    method: 'get',
    params: query
  })
}
// æŸ¥è¯¢æœåŠ¡ç»„è¯¦ç»†
export function getServiceGroup(groupId) {
  return request({
    url: '/system/serviceGroup/' + groupId,
    method: 'get'
  })
}
// æ–°å¢žæœåŠ¡ç»„
export function addServiceGroup(data) {
  return request({
    url: '/system/serviceGroup',
    method: 'post',
    data: data
  })
}
// ä¿®æ”¹æœåŠ¡ç»„
export function updateServiceGroup(data) {
  return request({
    url: '/system/serviceGroup',
    method: 'put',
    data: data
  })
}
// åˆ é™¤æœåŠ¡ç»„
export function delServiceGroup(groupId) {
  return request({
    url: '/system/serviceGroup/' + groupId,
    method: 'delete'
  })
}
// æŸ¥è¯¢æœåŠ¡ç»„å…³è”çš„ä»»åŠ¡åˆ—è¡¨
export function getGroupTasks(groupId) {
  return request({
    url: '/system/serviceGroup/' + groupId + '/tasks',
    method: 'get'
  })
}
// æŸ¥è¯¢æœåŠ¡ç»„å…³è”çš„æ‚£è€…åˆ—è¡¨
export function getGroupPatients(groupId) {
  return request({
    url: '/system/serviceGroup/' + groupId + '/patients',
    method: 'get'
  })
}
// å…³è”任务到服务组
export function addTaskToGroup(data) {
  return request({
    url: '/system/serviceGroup/tasks',
    method: 'post',
    data: data
  })
}
// ä»ŽæœåŠ¡ç»„ç§»é™¤ä»»åŠ¡
export function removeTaskFromGroup(groupId, taskId) {
  return request({
    url: '/system/serviceGroup/' + groupId + '/tasks/' + taskId,
    method: 'delete'
  })
}
// å…³è”患者到服务组
export function addPatientToGroup(data) {
  return request({
    url: '/system/serviceGroup/patients',
    method: 'post',
    data: data
  })
}
// ä»ŽæœåŠ¡ç»„ç§»é™¤æ‚£è€…
export function removePatientFromGroup(groupId, patientId) {
  return request({
    url: '/system/serviceGroup/' + groupId + '/patients/' + patientId,
    method: 'delete'
  })
}
src/api/system/user.js
@@ -94,6 +94,21 @@
    data: data,
  });
}
// æ»¡æ„åº¦æ˜Žç»†æŸ¥è¯¢
export function getSfStatisticsJoydetails(data) {
  return request({
    url: "/smartor/serviceSubtask/getSfStatisticsJoydetails",
    method: "post",
    data: data,
  });
}// æ»¡æ„åº¦ç»Ÿè®¡
export function getSfStatisticsJoy(data) {
  return request({
    url: "/smartor/serviceSubtask/getSfStatisticsJoy",
    method: "post",
    data: data,
  });
}
// æœªåŠæ—¶æŸ¥è¯¢
export function selectTimelyRate(data) {
  return request({
@@ -103,6 +118,17 @@
  });
}
// ä»»åŠ¡éšè®¿çŽ‡ç»Ÿè®¡è¡¨çš„ä¸‹é’»æ˜Žç»†
// sendstate = 2 å¾…随访 5 å¾…随访失败
// preachform = ä»»åŠ¡å½¢å¼(1,人工 2,纸质  3,电话  4,短信  5.微信公众号 6.微信小程序 7.支付宝小程序  8.智能机器人  9.钉钉)
export function querySubtaskList(data) {
  return request({
    url: "/smartor/serviceSubtask/querySubtaskList",
    method: "post",
    data: data,
  });
}
// ç”¨æˆ·å¯†ç é‡ç½®
export function updateUserPwd(oldPassword, newPassword) {
  const data = {
src/assets/order/BHostClient.exe
Binary files differ
src/assets/order/Xftp-8.0.0084p.exe
Binary files differ
src/assets/styles/element-variables.scss
@@ -4,8 +4,8 @@
**/
/* theme color */
$--color-primary: #3ba2f7;
$--color-success: #13ce66;
$--color-primary: #3664D9;
$--color-success: #0ABC54;
$--color-warning: #ffba00;
$--color-danger: #ff4949;
// $--color-info: #1E1E1E;
src/assets/styles/sidebar.scss
@@ -15,7 +15,7 @@
    -webkit-transition: width .28s;
    transition: width 0.28s;
    width: $base-sidebar-width !important;
    background: -webkit-linear-gradient(bottom, #3d6df8, #5b8cff);
    // background: -webkit-linear-gradient(bottom, #3d6df8, #5b8cff);
    height: 100%;
    position: fixed;
@@ -102,7 +102,8 @@
    & .theme-dark .nest-menu .el-submenu>.el-submenu__title,
    & .theme-dark .el-submenu .el-menu-item {
      background-color: $base-sub-menu-background !important;
font-size: 16px;
font-family: 'Verdana', sans-serif;
      &:hover {
        background-color: $base-sub-menu-hover !important;
      }
src/assets/styles/variables.scss
@@ -21,7 +21,7 @@
$base-logo-light-title-color: #001529;
$base-sub-menu-background:#99baed;
$base-sub-menu-hover:#001528;
$base-sub-menu-hover:#5c7acd;
// è‡ªå®šä¹‰æš—色菜单风格
/**
src/components/CallButton/index.vue
@@ -28,6 +28,7 @@
<script>
import sipService from "@/utils/sipService";
import { CallsetState, CallgetList } from "@/api/AiCentre/index";
export default {
  props: {
@@ -37,17 +38,20 @@
    },
  },
  data() {
    const randomNum = Math.floor(Math.random() * 20) + 1000; // å†…部定义
    const randomNum = Math.floor(Math.random() * 20) + 1000; // å®šä¹‰éšæœºåˆ†æœºå·
    return {
      isCalling: false,
      randomNum: randomNum,
      randomID: null,
      callStatus: "idle", // idle, calling, connected, ended
      sipStatus: "未连接",
      sipStatusClass: "status-disconnected",
      sipConfig: {
        wsUrl: "wss://192.168.10.124:7443",
        sipUri: `${randomNum}` + "@192.168.10.124",
        sipUri: "",
        password: "Smartor@2023",
        displayName: "Web å°é¾™",
        // realm: "9.208.5.18:8090",
      },
    };
  },
@@ -61,6 +65,15 @@
      };
      return statusMap[this.callStatus];
    },
    countdownText() {
      if (this.sipStatus !== "已注册") return "";
      const { canCall, reason } = sipService.canMakeCall();
      if (!canCall && reason.includes("等待")) {
        return reason;
      }
      return "";
    },
    callStatusClass() {
      return `status-${this.callStatus}`;
    },
@@ -68,13 +81,22 @@
      return this.isCalling ? "通话中..." : "一键呼叫";
    },
  },
  mounted() {
    console.log('当前分机号',this.sipConfig);
  created() {
    // CallgetList();
  },
  async mounted() {
    await this.CallgetList();
    sipService.init(this.sipConfig);
    // è®¾ç½®çŠ¶æ€å›žè°ƒ
    sipService.onStatusChange = (status) => {
      this.sipStatus = status.text;
      this.sipStatusClass = `status-${status.type}`;
      // å¤„理注册失败和断开连接情况
      if (status.type === "failed" || status.type === "disconnected") {
        this.overCallsetState(); // é‡Šæ”¾åˆ†æœºå·
      }
    };
    // ç›‘听通话状态变化
@@ -94,23 +116,98 @@
      }
      try {
        // å…ˆæ£€æŸ¥æ˜¯å¦å¯ä»¥å‘¼å«
        const { canCall, reason } = sipService.canMakeCall();
        if (!canCall) {
          const { canCall, reason } = sipService.canMakeCall();
          //this.$message.warning(reason);
          //return;
        }
        this.callStatus = "calling";
        this.isCalling = true;
        console.log("开始呼叫:", sipService);
        await sipService.makeCall(this.phoneNumber);
      } catch (error) {
        console.error("呼叫失败:", error);
        this.callStatus = "ended";
        this.isCalling = false;
        this.$message.error(`呼叫失败: ${error.message}`);
        let registrationTime = Date.now(); // è®°å½•注销成功时间
        console.log(registrationTime, "呼叫失败时间");
        console.error("呼叫失败1:", error);
        // this.callStatus = "ended";
        // this.isCalling = false;
        //this.$message.error(`呼叫失败: ${error.message}`);
        try {
          // å…ˆæ£€æŸ¥æ˜¯å¦å¯ä»¥å‘¼å«
          const { canCall, reason } = sipService.canMakeCall();
          if (!canCall) {
            const { canCall, reason } = sipService.canMakeCall();
          }
          this.callStatus = "calling";
          this.isCalling = true;
          console.log("开始呼叫:", sipService);
          await sipService.makeCall("0" + this.phoneNumber);
        } catch (error) {
          this.callStatus = "ended";
          this.isCalling = false;
        }
      }
    },
    // æŸ¥è¯¢å¯ç”¨åˆ†æœºå·
    async CallgetList() {
      try {
        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`;
        this.startCallsetState();
      } catch (error) {
        console.error("获取分机号失败:", error);
        // this.updateStatus("failed", "获取分机号失败");
      }
    },
    async startCallsetState() {
      try {
        await CallsetState({ id: this.randomID, state: 1 });
        console.log("分机号状态更新为使用中");
      } catch (error) {
        console.error("更新分机号状态失败:", error);
      }
    },
    async overCallsetState() {
      try {
        if (this.randomID) {
          await CallsetState({ id: this.randomID, state: 0 });
          console.log("分机号状态更新为可用");
        }
      } catch (error) {
        console.error("释放分机号失败:", error);
      }
    },
    endCall() {
      sipService.endCall();
      this.callStatus = "ended";
      this.isCalling = false;
    },
    cleanupResources() {
      // ç»“束通话
      if (this.isCalling) {
        sipService.endCall();
      }
      // é‡Šæ”¾åˆ†æœºå·
      this.overCallsetState();
      // æ–­å¼€ SIP è¿žæŽ¥
      if (sipService.ua) {
        sipService.ua.stop();
      }
    },
  },
  beforeUnmount() {
    // ç»„件销毁时确保释放资源
    this.cleanupResources();
  },
};
</script>
src/components/CallCenterLs/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,717 @@
<template>
  <div class="call-center-container">
    <!-- ä¸»æŽ§åˆ¶åŒº -->
    <div class="control-section">
      <div class="phone-control-card">
        <h3 class="section-title">电话控制</h3>
        <div class="input-group">
          <div class="form-field">
            <label class="form-label">客户电话号码</label>
            <input
              v-model="customerPhone"
              type="text"
              placeholder="请输入电话号码"
              :disabled="isCalling"
              class="phone-input"
            />
          </div>
          <div class="button-group">
            <button
              @click="handleCall"
              :class="['call-btn', callButtonClass]"
              :disabled="!canMakeCall"
            >
              <span class="btn-icon">📞</span>
              {{ callButtonText }}
            </button>
            <button
              @click="handleSeatLogout"
              :class="[
                'seat-btn',
                'logout',
                { 'in-call': isInCall || isCalling },
              ]"
              :disabled="!canLogout"
            >
              <span class="btn-icon">🚪</span>
              {{ isInCall || isCalling ? "强制签出" : "签出" }}
            </button>
            <button
              @click="handleHangup"
              class="hangup-btn"
              :disabled="!canHangup"
            >
              <span class="btn-icon">📵</span>
              æŒ‚æ–­
            </button>
          </div>
        </div>
      </div>
      <!-- çŠ¶æ€æ˜¾ç¤ºåŒº -->
      <div class="status-card">
        <h3 class="section-title">状态监控</h3>
        <div class="status-grid">
          <div class="status-item">
            <span class="status-label">座席状态:</span>
            <span :class="['status-indicator', seatStatusClass]">
              <span class="status-dot"></span>
              {{ seatStatusText }}
            </span>
          </div>
          <div class="status-item">
            <span class="status-label">通话状态:</span>
            <span :class="['status-indicator', callStatusClass]">
              <span class="status-dot"></span>
              {{ callStatusText }}
            </span>
          </div>
          <div class="status-item" v-if="callDuration">
            <span class="status-label">通话时长:</span>
            <span class="duration-display"> â±ï¸ {{ callDuration }} </span>
          </div>
        </div>
      </div>
    </div>
    <!-- è°ƒè¯•面板 -->
    <div class="debug-section">
      <el-collapse accordion>
        <el-collapse-item name="debug">
          <template slot="title">
            <div class="debug-header">
              <span class="debug-title">呼叫调试日志</span>
              <span class="debug-subtitle">点击查看详细通话信息</span>
            </div>
          </template>
          <div class="debug-content">
            <WebsocketDemo
              ref="callComponent"
              :customer-phone="customerPhone"
              :auto-login="true"
              @status-change="onSeatStatusChange"
              @call-status="onCallStatusChange"
              @error="onCallError"
              class="call-component"
            />
          </div>
        </el-collapse-item>
      </el-collapse>
    </div>
  </div>
</template>
<script>
import WebsocketDemo from "../../views/followvisit/discharge/ClickCall.vue";
export default {
  name: "CallCenterModal",
  components: {
    WebsocketDemo,
  },
  props: {
    initialPhone: {
      type: String,
      default: "",
    },
  },
  data() {
    return {
      customerPhone: "",
      isSeatLoggedIn: false,
      callStatus: "idle",
      callStartTime: null,
      callDuration: "00:00",
      durationTimer: null,
      lastError: null,
    };
  },
  computed: {
    isCalling() {
      return this.callStatus === "calling";
    },
    isInCall() {
      return this.callStatus === "connected";
    },
    canLogout() {
      // åªæœ‰åœ¨å·²ç­¾å…¥çŠ¶æ€æ‰å…è®¸ç­¾å‡ºï¼ˆæ— è®ºæ˜¯å¦åœ¨é€šè¯ä¸­ï¼‰
      return this.isSeatLoggedIn && !this.isSeatLoggingOut;
    },
    canMakeCall() {
      return (
        this.isSeatLoggedIn &&
        this.customerPhone &&
        this.callStatus === "idle" &&
        !this.lastError
      );
    },
    canHangup() {
      return this.isCalling || this.isInCall;
    },
    callButtonClass() {
      if (!this.canMakeCall) return "disabled";
      return this.isCalling ? "calling" : "idle";
    },
    callButtonText() {
      if (this.isCalling) return "呼叫中...";
      if (this.isInCall) return "通话中";
      return "开始呼叫";
    },
    seatStatusClass() {
      return this.isSeatLoggedIn ? "success" : "error";
    },
    seatStatusText() {
      return this.isSeatLoggedIn ? "已签入" : "未签入";
    },
    callStatusClass() {
      switch (this.callStatus) {
        case "connected":
          return "success";
        case "calling":
          return "warning";
        default:
          return "idle";
      }
    },
    callStatusText() {
      switch (this.callStatus) {
        case "connected":
          return "通话中";
        case "calling":
          return "呼叫中";
        default:
          return "空闲";
      }
    },
  },
  watch: {
    initialPhone: {
      immediate: true,
      handler(newVal) {
        if (newVal) {
          this.customerPhone = newVal;
        }
      },
    },
  },
  methods: {
    handleCall() {
      if (!this.canMakeCall) return;
      this.$refs.callComponent.callout(this.customerPhone);
      this.startCallTimer();
    },
    async handleSeatLogout() {
      if (!this.canLogout) return;
      try {
        // å¦‚果正在通话中,先挂断
        if (this.isInCall || this.isCalling) {
          // æ˜¾ç¤ºç¡®è®¤å¯¹è¯æ¡†
          const confirm = await this.$confirm(
            "确定要签出吗?签出将结束当前通话",
            "提示",
            {
              confirmButtonText: "确定",
              cancelButtonText: "取消",
              type: "warning",
            }
          );
          await this.handleHangup();
        }
        console.log(2);
        // æ‰§è¡Œç­¾å‡º
        await this.$refs.callComponent.handleSeatLogout();
        console.log(3);
        this.isSeatLoggedIn = false;
        this.$message.success("座席签出成功");
      } catch (error) {
        if (error !== "cancel") {
          // å¿½ç•¥ç”¨æˆ·å–消的情况
          console.error("签出失败:", error);
          this.$message.error("座席签出失败");
        }
      }
    },
    handleHangup() {
      this.$refs.callComponent.hangup();
      this.stopCallTimer();
      this.callStatus = "idle";
    },
    onSeatStatusChange(status) {
      this.isSeatLoggedIn = status.isLoggedIn;
      // å¦‚果座席签出,重置通话状态
      if (!status.isLoggedIn) {
        this.callStatus = "idle";
        this.stopCallTimer();
      }
    },
    onCallStatusChange(status) {
      this.callStatus = status.status;
      if (status.status == "connected") {
        this.startCallTimer();
      } else if (status.status == "idle") {
        this.stopCallTimer();
      }
    },
    onCallError(error) {
      this.lastError = error;
      this.$emit("error", error);
    },
    startCallTimer() {
      this.callStartTime = new Date();
      this.durationTimer = setInterval(() => {
        if (this.callStartTime) {
          const duration = Math.floor((new Date() - this.callStartTime) / 1000);
          const minutes = Math.floor(duration / 60)
            .toString()
            .padStart(2, "0");
          const seconds = (duration % 60).toString().padStart(2, "0");
          this.callDuration = `${minutes}:${seconds}`;
        }
      }, 1000);
    },
    stopCallTimer() {
      if (this.durationTimer) {
        clearInterval(this.durationTimer);
        this.durationTimer = null;
      }
      this.callDuration = "00:00";
      this.callStartTime = null;
    },
    handleSeatBusy() {
      this.$refs.callComponent.afk();
    },
    handleSeatReady() {
      this.$refs.callComponent.online();
    },
    handleHold() {
      this.$refs.callComponent.hold();
    },
    handleResume() {
      this.$refs.callComponent.holdresume();
    },
    // æä¾›ç»™çˆ¶ç»„件调用的方法
    setPhoneNumber(phone) {
      this.customerPhone = phone;
    },
    autoCall(phone) {
      this.setPhoneNumber(phone);
      this.$nextTick(() => {
        this.handleCall();
      });
    },
  },
  beforeUnmount() {
    this.stopCallTimer();
    // ç»„件销毁前尝试签出
    if (this.isSeatLoggedIn) {
      this.handleSeatLogout().catch(console.error);
    }
  },
};
</script>
<style lang="scss" scoped>
.call-center-container {
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 0;
  background: #f8fafc;
}
// æŽ§åˆ¶åŒºåŸŸæ ·å¼
.control-section {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  padding: 20px;
  padding-bottom: 0;
  @media (max-width: 1024px) {
    grid-template-columns: 1fr;
  }
}
.seat-btn.logout {
  background: linear-gradient(135deg, #f97316, #ea580c);
  color: white;
  &:hover:not(:disabled) {
    box-shadow: 0 6px 16px rgba(249, 115, 22, 0.4);
  }
  // é€šè¯ä¸­çš„特殊样式
  &.in-call {
    background: linear-gradient(135deg, #ef4444, #dc2626);
    animation: pulse 1.5s infinite;
    &:hover:not(:disabled) {
      box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);
    }
  }
}
// é€šè¯ä¸­ç­¾å‡ºæŒ‰é’®çš„特殊动画
@keyframes pulse-red {
  0% {
    box-shadow: 0 4px 12px rgba(239, 68, 68, 0.5);
  }
  50% {
    box-shadow: 0 4px 20px rgba(239, 68, 68, 0.8);
  }
  100% {
    box-shadow: 0 4px 12px rgba(239, 68, 68, 0.5);
  }
}
.section-title {
  font-size: 16px;
  font-weight: 600;
  color: #1e293b;
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  &::before {
    content: "";
    width: 3px;
    height: 16px;
    background: #3b82f6;
    margin-right: 8px;
    border-radius: 2px;
  }
}
// å¡ç‰‡é€šç”¨æ ·å¼
.phone-control-card,
.status-card {
  background: white;
  padding: 20px;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  border: 1px solid #e2e8f0;
}
// è¡¨å•控件样式
.form-field {
  margin-bottom: 20px;
}
.form-label {
  display: block;
  font-weight: 500;
  color: #475569;
  margin-bottom: 8px;
  font-size: 14px;
}
.phone-input {
  width: 100%;
  padding: 12px;
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  font-size: 14px;
  transition: all 0.3s ease;
  &:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);
  }
  &:disabled {
    background-color: #f8fafc;
    color: #94a3b8;
    cursor: not-allowed;
  }
}
.button-group {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
// æŒ‰é’®åŸºç¡€æ ·å¼
.call-btn,
.hangup-btn {
  padding: 12px 28px; /* å¢žåŠ å†…è¾¹è·ï¼Œä½¿æŒ‰é’®æ›´å¤§æ–¹ */
  border: none;
  border-radius: 12px; /* ä½¿ç”¨æ›´å¤§çš„圆角,创造“药丸”形 */
  cursor: pointer;
  font-size: 14px;
  font-weight: 600; /* å­—体加粗 */
  transition: all 0.3s ease; /* å¹³æ»‘过渡所有属性 */
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px; /* å›¾æ ‡å’Œæ–‡å­—的间距 */
  min-width: 120px; /* è®¾ç½®æœ€å°å®½åº¦ */
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15), 0 2px 4px rgba(0, 0, 0, 0.1); /* å¤šå±‚阴影增强立体感 */
}
.call-btn {
  background: linear-gradient(135deg, #10b981, #059669); /* ç»¿è‰²æ¸å˜ */
  color: white;
}
.call-btn:hover:not(.disabled) {
  transform: translateY(-2px); /* æ‚¬åœæ—¶è½»å¾®ä¸Šæµ® */
  box-shadow: 0 6px 16px rgba(16, 185, 129, 0.4); /* æ‚¬åœæ—¶é˜´å½±æ›´æ˜Žæ˜¾ */
}
.call-btn.calling {
  background: linear-gradient(
    135deg,
    #f59e0b,
    #d97706
  ); /* å‘¼å«ä¸­çŠ¶æ€æ”¹ä¸ºæ©™è‰²æ¸å˜ */
  animation: pulse 1.5s infinite; /* å‘¼å«ä¸­æ·»åŠ å‘¼å¸è„‰å†²åŠ¨ç”» */
}
.hangup-btn {
  background: linear-gradient(135deg, #ef4444, #dc2626); /* çº¢è‰²æ¸å˜ */
  color: white;
}
.hangup-btn:hover:not(:disabled) {
  transform: translateY(-2px);
  box-shadow: 0 6px 16px rgba(239, 68, 68, 0.4);
}
/* ç¦ç”¨çŠ¶æ€ */
.call-btn.disabled,
.hangup-btn:disabled {
  background: #cbd5e1 !important;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}
/* è„‰å†²åŠ¨ç”»å®šä¹‰ */
@keyframes pulse {
  0% {
    box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5);
  }
  50% {
    box-shadow: 0 4px 20px rgba(245, 158, 11, 0.8);
  }
  100% {
    box-shadow: 0 4px 12px rgba(245, 158, 11, 0.5);
  }
}
// çŠ¶æ€æ˜¾ç¤ºæ ·å¼
.status-grid {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.status-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px;
  background: #f8fafc;
  border-radius: 8px;
}
.status-label {
  font-size: 14px;
  color: #475569;
  font-weight: 500;
}
.status-indicator {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  font-weight: 500;
  padding: 4px 12px;
  border-radius: 20px;
}
.status-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  animation: pulse 2s infinite;
}
.status-indicator.success {
  background: #f0fdf4;
  color: #059669;
  .status-dot {
    background: #059669;
  }
}
.status-indicator.error {
  background: #fef2f2;
  color: #dc2626;
  .status-dot {
    background: #dc2626;
  }
}
.status-indicator.warning {
  background: #fffbeb;
  color: #d97706;
  .status-dot {
    background: #f59e0b;
  }
}
.status-indicator.idle {
  background: #f8fafc;
  color: #64748b;
  .status-dot {
    background: #94a3b8;
  }
}
.duration-display {
  font-family: "Courier New", monospace;
  font-weight: 600;
  color: #059669;
  font-size: 14px;
}
// è°ƒè¯•面板样式
.debug-section {
  background: white;
  margin: 0 20px 20px;
  padding: 20px;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
.debug-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  padding-right: 20px;
}
.debug-title {
  font-size: 16px;
  font-weight: 600;
  color: #1e293b;
  margin-bottom: 16px;
  display: flex;
  align-items: center;
  &::before {
    content: "";
    width: 3px;
    height: 16px;
    background: #3b82f6;
    margin-right: 8px;
    border-radius: 2px;
  }
}
.debug-subtitle {
  font-size: 12px;
  color: #64748b;
}
.debug-content {
  padding: 0;
}
.call-component {
  min-height: 200px;
  border-top: 1px solid #e2e8f0;
}
// åŠ¨ç”»å®šä¹‰
@keyframes pulse {
  0% {
    opacity: 1;
  }
  50% {
    opacity: 0.5;
  }
  100% {
    opacity: 1;
  }
}
// å“åº”式设计
@media (max-width: 768px) {
  .control-section {
    padding: 16px;
    grid-template-columns: 1fr;
  }
  .phone-control-card,
  .status-card {
    padding: 16px;
  }
  .button-group {
    flex-direction: column;
  }
  .status-item {
    flex-direction: column;
    gap: 8px;
    align-items: flex-start;
  }
  .debug-section {
    margin: 0 16px 16px;
  }
}
@media (max-width: 480px) {
  .call-center-container {
    gap: 12px;
  }
  .control-section {
    padding: 12px;
  }
}
</style>
src/components/PatientSelection/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,585 @@
<template>
  <div>
    <el-dialog
      title="派发患者选择"
      :visible.sync="dialogVisiblepatient"
      width="70%"
      :before-close="handleClosehz"
    >
      <div class="examine-jic">
        <div style="margin: 0 10px 20px 10px">
          <el-card class="box-card">
            <el-tag
              v-for="item in overallCase"
              :key="item.icdid"
              type="primary"
              closables
              @close="handleClose(item)"
            >
              {{ item.name }}
            </el-tag>
            <div style="margin-top: 20px; text-align: right">
              å…±é€‰æ‹©<span
                style="font-size: 18px; color: #409eff; margin: 0 10px"
                >{{ overallCase.length }}</span
              >位患者
            </div>
          </el-card>
        </div>
        <div class="jic-value">
          <el-row :gutter="20">
            <!--用户数据-->
            <el-form
              :model="patientqueryParams"
              ref="queryForm"
              size="small"
              :inline="true"
              label-width="98px"
            >
              <el-form-item label="患者:">
                <el-input
                  v-model="patientqueryParams.name"
                  @keyup.enter.native="handleQuery"
                ></el-input>
              </el-form-item>
              <el-form-item label="患者类型">
                <el-select
                  v-model="patientqueryParams.allhosp"
                  placeholder="请选择新增类型"
                >
                  <el-option
                    v-for="item in taskoptions"
                    :label="item.label"
                    :value="item.value"
                  >
                  </el-option>
                </el-select>
              </el-form-item>
              <el-form-item label="患者诊断:">
                <el-input
                  v-model="patientqueryParams.leavediagname"
                  @keyup.enter.native="handleQuery"
                ></el-input>
              </el-form-item>
              <el-form-item label="主治医生:">
                <el-input
                  v-model="patientqueryParams.drname"
                  @keyup.enter.native="handleQuery"
                ></el-input>
              </el-form-item>
              <el-form-item>
                <el-button
                  type="primary"
                  icon="el-icon-search"
                  size="medium"
                  @click="handleQuery"
                  >搜索</el-button
                >
                <el-button
                  icon="el-icon-refresh"
                  size="medium"
                  @click="resetQuery"
                  >重置</el-button
                >
              </el-form-item>
            </el-form>
            <!-- é€‰æ‹©æ‚£è€…列表 -->
            <el-table
              ref="multipleTable"
              :data="donorchargeList"
              tooltip-effect="dark"
              style="width: 100%"
              @selection-change="handleSelectionChange"
            >
              <el-table-column
                fixed="left"
                class="checkall"
                type="selection"
                width="55"
              >
              </el-table-column>
              <div v-for="(item, index) in tableLabel">
                <el-table-column
                  v-if="item.label == '出院日期'"
                  :key="index"
                  :prop="item.prop"
                  :width="item.width"
                  :label="item.label"
                  :formatter="formatData"
                >
                  <template slot-scope="scope">
                    <span>{{ formatTime(scope.row.endtime) }}</span>
                  </template>
                </el-table-column>
                <el-table-column
                  v-if="item.label == '性别'"
                  :key="index"
                  :prop="item.prop"
                  :width="item.width"
                  :label="item.label"
                  :formatter="formatData"
                >
                  <template slot-scope="scope">
                    <span>{{ scope.row.sex == 1 ? "男" : "女" }}</span>
                  </template>
                </el-table-column>
                <el-table-column
                  v-if="item.label != '性别' && item.label != '出院日期'"
                  :key="index"
                  :prop="item.prop"
                  :width="item.width"
                  :label="item.label"
                  :formatter="formatData"
                >
                </el-table-column>
              </div>
            </el-table>
          </el-row>
          <pagination
            v-show="patienttotal > 0"
            :total="patienttotal"
            :page.sync="patientqueryParams.pageNum"
            :limit.sync="patientqueryParams.pageSize"
            @pagination="handleQuery"
          />
        </div>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="$emit('addoption')">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="AddDispatchpatients"
          >确定添加</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>
<script>
import {
  getillnesslist,
  deltargetillness,
  getTaskpatientQC,
  Questionnairetaskgetson,
  Externallist,
  Editsingletask,
} from "@/api/AiCentre/index";
export default {
  name: "Diseasetotality",
  data() {
    return {
      patienttotal: 0, //
      allpids: [],
      // å½“前页选中数据
      multipleSelection: [],
      overallpatin: [],
      form: {},
      patientqueryParams: {
        pageNum: 1,
        pageSize: 10,
        allhosp: "4",
        pids: null,
      },
      donorchargeList: [],
      donorchargeanlList: [], //案例列表
      taskoptions: [
        {
          value: "4",
          label: "出院病人",
        },
        {
          value: "1",
          label: "在院病人",
        },
        {
          value: "2",
          label: "门诊病人",
        },
        {
          value: "3",
          label: "体检病人",
        },
        {
          value: "6",
          label: "院外患者",
        },
        {
          value: "5",
          label: "手术病人",
        },
      ],
      overallCase:[],
      tableLabel: [
        { label: "出院日期", width: "150", prop: "inhosptime" },
        { label: "患者", width: "", prop: "name" },
        { label: "患者诊断", width: "", prop: "leavediagname" },
        { label: "身份证", width: "200", prop: "idcardno" },
        { label: "性别", width: "", prop: "sex" },
        { label: "年龄", width: "", prop: "age" },
        { label: "责任护士", width: "", prop: "nurseName" },
        { label: "主治医生", width: "", prop: "drname" },
        { label: "科室", width: "240", prop: "dept" },
        { label: "病区", width: "240", prop: "leavehospitaldistrictname" },
      ],
    };
  },
  props: {
    dialogVisiblepatient: {
      type: Boolean,
      default: () => {
        return false;
      },
    },
    // 5问卷模板、3随访模板、6宣教
    type: {
      type: Number,
      default: () => {
        return 5;
      },
    },
  },
  created() {},
  mounted() {
    // this.handleAddpatient();
  },
  watch: {},
  methods: {
    //   æ•°æ®è¿‡æ»¤
    formatData(row, column, cellValue) {
      if (column.property === "createType") {
        if (cellValue === 1) {
          return "自动";
        }
        return "手动";
      }
      if (
        column.property === "createTime" ||
        column.property === "inhosptime"
      ) {
        if (cellValue === null) {
          return "";
        }
        return dayjs(cellValue).format("YYYY-MM-DD ");
      }
      return cellValue;
    },
    handleClosehz() {
      this.$emit("addoption");
    },
    // é€‰æ‹©æ•°æ®æ ‡ç­¾åˆ é™¤äº‹ä»¶
    handleClose(item) {
      console.log(item);
      if (this.multipleSelection.indexOf(item) == -1 && !item.outid) {
        this.overallCase.splice(this.overallCase.indexOf(item), 1);
      } else if (item.outid) {
        this.$modal
          .confirm('是否确认删除名称为"' + item.icd10name + '"的数据项?')
          .then(() => {
            this.multipleSelection.splice(
              this.multipleSelection.indexOf(item),
              1
            );
            deltargetillness(item.id).then((res) => {
              if (res.code == 200) {
                this.$modal.msgSuccess("删除成功");
                this.overallCase.splice(this.overallCase.indexOf(item), 1);
              }
            });
            // å–消挂载状态
            this.$refs.multipleTable.toggleRowSelection(item, false);
          })
          .catch(() => {});
      } else {
        this.overallCase.splice(this.overallCase.indexOf(item), 1);
        this.multipleSelection.splice(this.multipleSelection.indexOf(item), 1);
        // å–消挂载状态
        this.$refs.multipleTable.toggleRowSelection(item, false);
      }
    },
    resetQuery() {
      this.patientqueryParams.name = null;
      this.handleAddpatient();
    },
    // è§¦å‘查询事件
    handleAddpatients() {
      this.$emit("kkoption");
      getillnesslist(this.patientqueryParams).then((res) => {
        this.donorchargeList = res.rows;
        this.patienttotal = res.total;
        this.Restorecheck();
      });
    },
    // è§¦å‘查询事件
    handleAddpatient(taskid) {
      this.$emit("kkoption");
      if (taskid != this.taskid) {
        this.recover();
      }
      if (taskid) {
        this.taskid = taskid;
        Questionnairetaskgetson({ taskid: this.taskid }).then((res) => {
          if (res.code == 200) {
            this.form = res.data;
            this.overallpatin = this.form.patTaskRelevances.concat();
            this.allpids = [];
            this.overallpatin.forEach((item) => {
              this.allpids.push(item.patid);
            });
            if (this.allpids[0]) {
              this.patientqueryParams.pids = this.allpids;
            } else {
              this.patientqueryParams.pids = null;
            }
            this.handleQuery();
          }
        });
      }
    },
    handleQuery() {
      // èŽ·å–å¤–éƒ¨æ‚£è€…
      if (this.patientqueryParams.allhosp == 6) {
        this.Externallist();
        return;
      }
      console.log(11);
      if (this.patientqueryParams.allhosp == 4) {
        this.tableLabel = [
          // { label: "入院日期", width: "170", prop: "starttime" },
          { label: "出院日期", width: "150", prop: "endtime" },
          { label: "患者", width: "", prop: "name" },
          { label: "患者诊断", width: "", prop: "leavediagname" },
          { label: "身份证", width: "200", prop: "idcardno" },
          { label: "性别", width: "", prop: "sex" },
          { label: "年龄", width: "", prop: "age" },
          { label: "出院天数", width: "", prop: "endDay" },
          { label: "责任护士", width: "", prop: "nurseName" },
          { label: "医生", width: "", prop: "drname" },
          { label: "科室", width: "180", prop: "dept" },
          { label: "病区", width: "150", prop: "leavehospitaldistrictname" },
        ];
      } else if (this.patientqueryParams.allhosp == 1) {
        this.tableLabel = [
          { label: "入院日期", width: "150", prop: "starttime" },
          { label: "患者", width: "", prop: "name" },
          { label: "患者诊断", width: "", prop: "leavediagname" },
          { label: "身份证", width: "200", prop: "idcardno" },
          { label: "性别", width: "", prop: "sex" },
          { label: "年龄", width: "", prop: "age" },
          { label: "出院天数", width: "", prop: "endDay" },
          { label: "责任护士", width: "", prop: "nurseName" },
          { label: "医生", width: "", prop: "drname" },
          { label: "科室", width: "180", prop: "dept" },
          { label: "病区", width: "150", prop: "leavehospitaldistrictname" },
        ];
      }
      console.log(this.patientqueryParams.allhosp);
      // èŽ·å–æ‚£è€…æ•°æ®
      getTaskpatientQC(this.patientqueryParams).then((response) => {
        this.donorchargeList = response.rows;
        this.donorchargeList.forEach((item) => {
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
        });
        this.Restorecheck();
        this.patienttotal = response.total;
      });
    },
    // èŽ·å–å¤–éƒ¨æ‚£è€…å¯¼å…¥åˆ—è¡¨
    Externallist() {
      this.tableLabel = [
        { label: "患者", width: "", prop: "name" },
        { label: "身份证", width: "200", prop: "idcardno" },
        { label: "性别", width: "", prop: "sex" },
        { label: "年龄", width: "", prop: "age" },
        { label: "出院天数", width: "", prop: "endDay" },
      ];
      this.patientqueryParams.pageSize = 1000;
      Externallist(this.patientqueryParams).then((response) => {
        this.donorchargeList = response.rows;
        this.donorchargeList.forEach((item) => {
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
        });
        // this.dialogVisiblepatient = true;
        this.patienttotal = response.total;
      });
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    handleSelectionChange(selection) {
      if (this.decision) return;
      // åˆ¤æ–­æ˜¯å¦æœ‰åˆ é™¤
      if (this.multipleSelection.length <= selection.length) {
        this.multipleSelection = selection;
      } else {
        this.multipleSelection.forEach((item) => {
          if (selection.includes(item)) {
          } else {
            if (this.multipleSelection.length == 1) {
              this.multipleSelection = [];
            } else {
              this.multipleSelection.splice(
                this.multipleSelection.indexOf(item),
                1
              );
            }
            if (this.overallCase.length == 1) {
              this.overallCase = [];
            } else {
              this.overallCase.splice(this.overallCase.indexOf(item), 1);
            }
          }
        });
      }
      // èµ‹å€¼ç»™æ•´ä½“选中数组
      this.multipleSelection.forEach((item) => {
        if (this.overallCase.every((obj) => obj.name != item.name)) {
          item.isoperation = 1;
          item.patid = item.id;
          item.hospType = this.patientqueryParams.allhosp;
          item.sfzh = item.idcardno;
          item.deptCode = item.deptcode;
          item.deptName = item.dept;
          item.admindate = item.inhosptime;
          item.diagname = item.leavediagname;
          item.sfzh = item.idcardno;
          if (this.patientqueryParams.allhosp == 6) {
            item.patfrom = 1;
          }
          this.overallCase.push(item);
        }
      });
      console.log(this.multipleSelection, "触发选择后multipleSelection");
    },
    // åˆ‡æ¢é¡µåŽæ¢å¤é€‰ä¸­
    Restorecheck() {
      console.log(this.overallCase, "this.overallCase");
      const allid = this.overallCase.map((item) => item.name);
      const overlap = this.donorchargeList.filter((value) => {
        return allid.includes(value.name);
      });
      // ä¿æŒids和当前页面的同步性
      this.multipleSelection = overlap;
      console.log(this.multipleSelection, "进入分页multipleSelection");
      this.toggleSelection(overlap);
    },
    // æŒ‚载选择状态
    toggleSelection(rows) {
      if (rows) {
        this.decision = true;
        this.$nextTick(() => {
          rows.forEach((row) => {
            this.$refs.multipleTable.toggleRowSelection(row, true);
          });
          this.decision = false;
        });
        console.log(123);
      } else {
        this.$refs.multipleTable.clearSelection();
      }
    },
    AddDispatchpatients() {
      console.log(this.overallCase);
      this.form.patTaskRelevances.push(...this.overallCase);
      console.log(this.form.patTaskRelevances);
      this.form.isoperation=2;
      Editsingletask(this.form).then((res) => {
        if (res.code == 200) {
          if (this.form.taskid) {
            this.$modal.msgSuccess("修改成功");
          } else {
            this.$modal.msgSuccess("新增成功");
          }
          this.recover();
      this.$emit("addoption");
        }
      });
      // this.dialogVisiblepatient = false;
      // this.$emit("addoption");
    },
    recover() {
      this.donorchargeList = [];
      this.donorchargeanlList = [];
      this.multipleSelection = [];
      this.allpids = [];
      this.overallpatin = [];
      this.form = {};
      this.patientqueryParams = {
        pageNum: 1,
        pageSize: 10,
        allhosp: "4",
        pids: null,
      };
      this.overallCase = [];
      // this.dialogVisiblepatient = false;
    },
  },
};
</script>
<style lang="scss" scoped>
.examine-jic {
  .headline {
    font-size: 24px;
    border-left: 5px solid #41a1be;
    padding-left: 5px;
    margin-bottom: 10px;
    display: flex;
    justify-content: space-between;
    .Add-details {
      font-size: 18px;
      color: #02a7f0;
      cursor: pointer;
    }
  }
  .jic-value {
    font-size: 20px;
    border-top: 1px solid #a7abac;
    padding: 10px;
    margin-bottom: 10px;
    .details-jic {
      padding: 10px 15px;
      border: 1px solid #dcdfe6;
      -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
        0 0 6px 0 rgba(0, 0, 0, 0.04);
      .details-title {
        display: flex;
        justify-content: space-between;
        margin-bottom: 10px;
        div:nth-child(2) {
          color: #02a7f0;
          cursor: pointer;
        }
      }
      .details-renw {
        background: #e4ebfc;
        padding: 15px 5px;
        border-radius: 5px;
        margin-bottom: 20px;
      }
    }
  }
}
::v-deep.el-table .el-table__header th:first-child .el-checkbox {
  display: none;
}
.el-tag + .el-tag {
  margin-left: 10px;
  margin-bottom: 10px;
}
</style>
src/components/SortCheckbox/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,289 @@
<template>
  <div class="ordered-checkbox-container">
    <!-- æ¨ªå‘排列的多选框组 -->
    <el-checkbox-group
      v-model="checkedValues"
      class="horizontal-checkbox-group"
    >
      <el-checkbox
        v-for="option in options"
        :key="getValue(option)"
        :label="getValue(option)"
      >
        {{ getLabel(option) }}
      </el-checkbox>
    </el-checkbox-group>
    <!-- é€‰ä¸­é¡ºåºå±•示区域 -->
    <div v-if="selectedOrder.length > 0" class="selection-order-display">
      <span class="order-label">服务执行顺序:</span>
      <span
        v-for="(item, index) in selectedOrder"
        :key="item.value"
        class="order-item"
      >
        {{ getSelectedIndex(index) }}.{{ getLabelByValue(item.value) }}
        <el-tooltip content="设置补偿时间" placement="top">
          <el-input-number
            v-model="item.compensateTime"
            :min="0"
            :max="60"
            size="mini"
            controls-position="right"
            class="compensate-time-input"
            @change="handleCompensateTimeChange(item.value, $event)"
          />
        </el-tooltip>
        <span v-if="index < selectedOrder.length - 1">、</span>
      </span>
    </div>
    <div v-else class="selection-order-display">
      <span class="order-label">暂无选中项</span>
    </div>
  </div>
</template>
<script>
export default {
  name: "OrderedCheckboxGroup",
  props: {
    options: {
      type: Array,
      default: () => [],
    },
    value: {
      type: Array,
      default: () => [],
    },
    initialselectedOrder: {
      type: Array,
      default: () => [],
    },
    valueKey: {
      type: String,
      default: "value",
    },
    labelKey: {
      type: String,
      default: "label",
    },
    // æ–°å¢žï¼šé»˜è®¤è¡¥å¿æ—¶é—´
    defaultCompensateTime: {
      type: Number,
      default: 0,
    },
  },
  data() {
    return {
      checkedValues: [],
      selectedOrder: [], // çŽ°åœ¨æ ¼å¼ä¸º [{value, compensateTime}]
    };
  },
  watch: {
    value: {
      immediate: true,
      handler(newVal) {
        if (
          Array.isArray(newVal) &&
          newVal.length > 0 &&
          typeof newVal[0] === "object"
        ) {
          console.log(this.selectedOrder, "111");
          // 1. ä¼ å…¥çš„æ˜¯å¯¹è±¡æ•°ç»„ [{ sort, preachform, compensateTime }]
          this.checkedValues = newVal.map((item) => item.preachform); // æå– preachform ç»„成选中值数组
          // æž„建 selectedOrder,优先使用传入的 compensateTime,否则用默认值
          this.selectedOrder = newVal.map((item) => ({
            value: item.preachform,
            compensateTime: item.hasOwnProperty("compensateTime")
              ? item.compensateTime
              : this.defaultCompensateTime,
          }));
        } else {
          // 2. ä¼ å…¥çš„æ˜¯å­—符串数组 (如 ["1", "3", "4"],兼容之前的用法)
          if (JSON.stringify(newVal) !== JSON.stringify(this.checkedValues)) {
            this.checkedValues = [...newVal];
            console.log(this.selectedOrder, "222");
            console.log(this.newVal, "22");
            // æž„建或更新 selectedOrder,保留已有的 compensateTime
            const newOrder = [];
            newVal.forEach((value) => {
              const existingItem = this.selectedOrder.find(
                (item) => item.value === value
              );
              if (existingItem) {
                newOrder.push(existingItem);
              } else {
                newOrder.push({
                  value,
                  compensateTime: this.hasOwnProperty(value)
                    ? this.hasOwnProperty(value)
                    : this.defaultCompensateTime,
                });
              }
            });
            this.selectedOrder = newOrder;
          }
        }
      },
      deep: true, // å»ºè®®æ·»åŠ  deep: true ä»¥ç¡®ä¿å¯¹è±¡æ•°ç»„内的变化能被捕获
    },
    checkedValues(newVal, oldVal) {
      console.log(this.selectedOrder, "333");
      // å¤„理选中项的变化
      const added = newVal.filter((item) => !oldVal.includes(item));
      const removed = oldVal.filter((item) => !newVal.includes(item));
      added.forEach((value) => {
        if (!this.selectedOrder.find((item) => item.value === value)) {
          this.selectedOrder.push({
            value,
            compensateTime: this.defaultCompensateTime,
          });
        }
      });
      removed.forEach((value) => {
        const index = this.selectedOrder.findIndex(
          (item) => item.value === value
        );
        if (index > -1) {
          this.selectedOrder.splice(index, 1);
        }
      });
      // æ›´æ–°çˆ¶ç»„ä»¶çš„ v-model ç»‘定值(选中值数组)
      this.$emit("input", [...newVal]);
      // è§¦å‘ change äº‹ä»¶ï¼Œä¼ é€’完整的业务数据
      this.emitChangeEvent();
    },
  },
  methods: {
    getValue(option) {
      return typeof option === "object" ? option[this.valueKey] : option;
    },
    getLabel(option) {
      return typeof option === "object" ? option[this.labelKey] : option;
    },
    getLabelByValue(value) {
      const option = this.options.find((opt) => this.getValue(opt) === value);
      return option ? this.getLabel(option) : value;
    },
    getSelectedIndex(index) {
      if (index < 20) {
        return String.fromCharCode(0x2460 + index);
      } else {
        return `(${index + 1})`;
      }
    },
    // å¤„理补偿时间变化
    handleCompensateTimeChange(value, newTime) {
      const item = this.selectedOrder.find((item) => item.value === value);
      if (item) {
        item.compensateTime = newTime;
        // è¡¥å¿æ—¶é—´å˜åŒ–时,只触发 change äº‹ä»¶ï¼Œä¸è§¦åЍ v-model
        this.emitChangeEvent();
      }
    },
    hasOwnProperty(patfrom) {
      console.log(patfrom);
      console.log(this.initialselectedOrder);
      // ä½¿ç”¨find方法查找匹配的对象
      const foundObject = this.initialselectedOrder.find(
        (item) => item.preachform === patfrom
      );
      // å¦‚果找到对象,返回其compensateTime;否则返回false
      return foundObject ? foundObject.compensateTime : false;
    },
    // å‘射变化事件
    emitChangeEvent() {
      // è½¬æ¢æ•°æ®æ ¼å¼ä¸ºçˆ¶ç»„件需要的格式
      const outputData = this.selectedOrder.map((item, index) => ({
        sort: index + 1,
        preachform: item.value,
        compensateTime: item.compensateTime,
      }));
      this.$emit("change", outputData); // å‘å°„ change äº‹ä»¶ï¼Œä¼ é€’完整数据
    },
    // èŽ·å–å½“å‰é€‰æ‹©é¡ºåºå’Œè¡¥å¿æ—¶é—´
    getSelectionOrder() {
      return this.selectedOrder.map((item, index) => ({
        sort: index + 1,
        preachform: item.value,
        compensateTime: item.compensateTime,
      }));
    },
    // è®¾ç½®é€‰æ‹©é¡ºåºå’Œè¡¥å¿æ—¶é—´
    setSelectionOrder(orderedValues) {
      this.selectedOrder = orderedValues.map((item) => ({
        value: item.preachform,
        compensateTime: item.compensateTime || this.defaultCompensateTime,
      }));
      this.checkedValues = orderedValues.map((item) => item.preachform);
      this.emitChangeEvent();
    },
  },
};
</script>
<style scoped>
.ordered-checkbox-container {
  display: flex;
  flex-direction: column;
  gap: 16px;
}
.horizontal-checkbox-group {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
.selection-order-display {
  padding: 12px;
  background-color: #f5f7fa;
  border-radius: 4px;
  border: 1px solid #ebeef5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}
.order-label {
  font-weight: bold;
  color: #606266;
  margin-right: 8px;
}
.order-item {
  color: #409eff;
  font-weight: 500;
  display: inline-flex;
  align-items: center;
  margin-right: 8px;
}
.compensate-time-input {
  width: 90px;
  margin-left: 8px;
}
/* å“åº”式设计:小屏幕时换行 */
@media (max-width: 768px) {
  .horizontal-checkbox-group {
    gap: 12px;
  }
  .selection-order-display {
    padding: 8px;
    flex-direction: column;
    align-items: flex-start;
  }
  .order-item {
    margin-bottom: 8px;
  }
}
</style>
src/layout/components/TagsView/index.vue
@@ -265,9 +265,9 @@
        margin-right: 15px;
      }
      &.active {
        background-color: #42b983;
        background-color: #3664D9;
        color: #fff;
        border-color: #42b983;
        border-color: #3664D9;
        &::before {
          content: '';
          background: #fff;
src/main.js
@@ -25,6 +25,8 @@
import { codemirror } from "vue-codemirror";
import "@/utils/cm-setting.js";
Vue.component("codemirror", codemirror);
import preventReClick from '@/utils/directives/preventReClick';
Vue.use(preventReClick);
import { Quill } from "vue-quill-editor";
window.Quill = Quill;
src/permission.js
@@ -1,55 +1,68 @@
import router from './router'
import store from './store'
import { Message } from 'element-ui'
import NProgress from 'nprogress'
import 'nprogress/nprogress.css'
import { getToken } from '@/utils/auth'
import { isRelogin } from '@/utils/request'
import router from "./router";
import store from "./store";
import { Message } from "element-ui";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
import { getToken } from "@/utils/auth";
import { isRelogin } from "@/utils/request";
NProgress.configure({ showSpinner: false })
NProgress.configure({ showSpinner: false });
const whiteList = ['/login', '/auth-redirect', '/bind', '/register','/wt','/xj','/sf', '/outsideChain','/outsideChainwt','/outsideChainxj']
const whiteList = [
  "/login",
  "/auth-redirect",
  "/bind",
  "/register",
  "/wt",
  "/xj",
  "/sf",
  "/satisfaction",
  "/loginSSO",
];
router.beforeEach((to, from, next) => {
  NProgress.start()
  NProgress.start();
  if (getToken()) {
    to.meta.title && store.dispatch('settings/setTitle', to.meta.title)
    to.meta.title && store.dispatch("settings/setTitle", to.meta.title);
    /* has token*/
    if (to.path === '/login') {
      next({ path: '/' })
      NProgress.done()
    if (to.path === "/login") {
      next({ path: "/" });
      NProgress.done();
    } else {
      if (store.getters.roles.length === 0) {
        isRelogin.show = true
        isRelogin.show = true;
        // åˆ¤æ–­å½“前用户是否已拉取完user_info信息
        store.dispatch('GetInfo').then(() => {
          isRelogin.show = false
          store.dispatch('GenerateRoutes').then(accessRoutes => {
            // æ ¹æ®roles权限生成可访问的路由表
            router.addRoutes(accessRoutes) // åŠ¨æ€æ·»åŠ å¯è®¿é—®è·¯ç”±è¡¨
            next({ ...to, replace: true }) // hack方法 ç¡®ä¿addRoutes已完成
        store
          .dispatch("GetInfo")
          .then(() => {
            isRelogin.show = false;
            store.dispatch("GenerateRoutes").then((accessRoutes) => {
              // æ ¹æ®roles权限生成可访问的路由表
              router.addRoutes(accessRoutes); // åŠ¨æ€æ·»åŠ å¯è®¿é—®è·¯ç”±è¡¨
              next({ ...to, replace: true }); // hack方法 ç¡®ä¿addRoutes已完成
            });
          })
        }).catch(err => {
            store.dispatch('LogOut').then(() => {
              Message.error(err)
              next({ path: '/' })
            })
          })
          .catch((err) => {
            store.dispatch("LogOut").then(() => {
              Message.error(err);
              next({ path: "/" });
            });
          });
      } else {
        next()
        next();
      }
    }
  } else {
    // æ²¡æœ‰token
    if (whiteList.indexOf(to.path) !== -1) {
      // åœ¨å…ç™»å½•白名单,直接进入
      next()
      next();
    } else {
      next(`/login?redirect=${to.fullPath}`) // å¦åˆ™å…¨éƒ¨é‡å®šå‘到登录页
      NProgress.done()
      next(`/login?redirect=${to.fullPath}`); // å¦åˆ™å…¨éƒ¨é‡å®šå‘到登录页
      NProgress.done();
    }
  }
})
});
router.afterEach(() => {
  NProgress.done()
})
  NProgress.done();
});
src/router/index.js
@@ -46,6 +46,11 @@
    component: () => import('@/views/login'),
    hidden: true
  },
    {
    path: '/loginSSO',
    component: () => import('@/views/loginSSO'),
    hidden: true
  },
  {
    path: '/sf',
    component: () => import('@/views/outsideChainnew'),
@@ -62,6 +67,11 @@
    hidden: true
  },
  {
    path: '/satisfaction',
    component: () => import('@/views/satisfaction'),
    hidden: true
  },
  {
    path: '/outsideChain',
    component: () => import('@/views/outsideChain'),
    hidden: true
src/smartor/dataobject/dw_patouthosp_base.vue
@@ -20,4 +20,4 @@
        </el-form-item>
     </el-form>
    </div>
</template>
</template>
src/smartor/dataobject/dw_patouthosp_list.vue
@@ -21,4 +21,4 @@
</template>
<script>
</script>
</script>
src/store/getters.js
@@ -637,6 +637,131 @@
      },
    },
  ],
  // éšè®¿ç±»åž‹
  tasktypes: (state) => [
    // {
    //   value: 16,
    //   label: "课题随访",
    //   raw: {
    //     cssClass: "",
    //     listClass: "",
    //   },
    // },
    {
      value: 2,
      label: "出院随访",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 3,
      label: "门诊随访",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 4,
      label: "宣教关怀",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 5,
      label: "复诊管理",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 6,
      label: "住院满意度",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 1,
      label: "监测评估",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 14,
      label: "门诊满意度",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 15,
      label: "投诉建议",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 7,
      label: "患者报告",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    {
      value: 9,
      label: "体检随访",
      raw: {
        cssClass: "",
        listClass: "",
      },
    },
    // {
    //   value: 1",
    //   label: "医技随访",
    //   raw: {
    //   cssClass: "",
    //   listClass: "",
    // },
    // },
    // {
    //   value: 11,
    //   label: "影像随访",
    //   raw: {
    //     cssClass: "",
    //     listClass: "",
    //   },
    // },
    // {
    //   value: 12,
    //   label: "心电随访",
    //   raw: {
    //     cssClass: "",
    //     listClass: "",
    //   },
    // },
    {
      value: "13",
      label: "专病随访",
      raw: {
      cssClass: "",
      listClass: "",
    },
    },
  ],
};
export default getters;
src/store/modules/user.js
@@ -1,226 +1,265 @@
import { login, logout, getInfo } from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import { login, logout, getInfo } from "@/api/login";
import { getToken, setToken, removeToken } from "@/utils/auth";
const user = {
  state: {
    token: getToken(),
    name: '',
    nickName:'',
    Id: '',
    avatar: '',
    hisUserId:'',
    belongWards:[],
    belongDepts:[],
    name: "",
    nickName: "",
    Id: "",
    avatar: "",
    hisUserId: "",
    belongWards: [],
    belongDepts: [],
    roles: [],
    permissions: [],
    // æœåŠ¡ç±»åž‹
  Serviceauthority:  [
    {
      value: "1",
      label: "患者报告",
      route: "followvisit/QuestionnaireTask",
      raw: {
        cssClass: "",
        listClass: "",
    Serviceauthority: [
      {
        value: "1",
        label: "患者报告",
        route: "followvisit/QuestionnaireTask",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
    {
      value: "2",
      label: "心电随访",
      route: "followvisit/QuestionnaireTask",
      raw: {
        cssClass: "",
        listClass: "",
      {
        value: "2",
        label: "心电随访",
        route: "followvisit/QuestionnaireTask",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
    {
      value: "3",
      label: "体检随访",
      route: "followvisit/particty",
      raw: {
        cssClass: "",
        listClass: "",
      {
        value: "3",
        label: "体检随访",
        route: "followvisit/particty",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
    {
      value: "4",
      label: "宣教关怀",
      route: "followvisit/Missioncreation",
      raw: {
        cssClass: "",
        listClass: "",
      {
        value: "4",
        label: "宣教关怀",
        route: "followvisit/Missioncreation",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
    {
      value: "5",
      label: "出院随访",
      route: "followvisit/particty",
      raw: {
        cssClass: "",
        listClass: "",
      {
        value: "5",
        label: "出院随访",
        route: "followvisit/particty",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
    {
      value: "6",
      label: "门诊随访",
      route: "followvisit/particty",
      raw: {
        cssClass: "",
        listClass: "",
      {
        value: "6",
        label: "门诊随访",
        route: "followvisit/particty",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
    {
      value: "7",
      label: "通知服务",
      route: "followvisit/Missioncreation",
      raw: {
        cssClass: "",
        listClass: "",
      {
        value: "7",
        label: "通知服务",
        route: "followvisit/Missioncreation",
        raw: {
          cssClass: "",
          listClass: "",
        },
      },
    },
  ],
    ],
  },
  mutations: {
    SET_TOKEN: (state, token) => {
      state.token = token
      state.token = token;
    },
    SET_NAME: (state, name) => {
      state.name = name
      state.name = name;
    },
    SET_nickNAME: (state, name) => {
      state.nickName = name
      state.nickName = name;
    },
    SET_Id: (state, Id) => {
      state.Id = Id
      console.log(state.Id,'user2');
      state.Id = Id;
    },
    SET_AVATAR: (state, avatar) => {
      state.avatar = avatar
      state.avatar = avatar;
    },
    SET_ROLES: (state, roles) => {
      state.roles = roles
      state.roles = roles;
    },
    SET_PERMISSIONS: (state, permissions) => {
      state.permissions = permissions
      state.permissions = permissions;
    },
    SET_Serviceauthority: (state, Serviceauthority) => {
      state.Serviceauthority = Serviceauthority
      state.Serviceauthority = Serviceauthority;
    },
    SET_leavehospitaldistrictcodes: (state, belongWards) => {
      state.belongWards = belongWards
      state.belongWards = belongWards;
    },
    SET_hisUserId: (state, hisUserId) => {
      state.hisUserId = hisUserId
      state.hisUserId = hisUserId;
    },
    SET_leaveldeptcodes: (state, belongDepts) => {
      state.belongDepts = belongDepts
    }
      state.belongDepts = belongDepts;
    },
  },
  actions: {
    // ç™»å½•
    Login({ commit }, userInfo) {
      console.log(userInfo,'user info');
      console.log(userInfo, "user info");
      const username = userInfo.username.trim()
      const password = userInfo.password
      const code = userInfo.code
      const username = userInfo.username.trim();
      const password = userInfo.password;
      const code = userInfo.code;
      const orgid = userInfo.orgid
      const orgid = userInfo.orgid;
      return new Promise((resolve, reject) => {
        login(username, password, code, orgid).then(res => {
          setToken(res.token)
          commit('SET_TOKEN', res.token)
          localStorage.setItem('orgid', orgid);
          if (orgid=='47255004333112711A1001') {
          localStorage.setItem('orgname', '景宁畲族自治县人民医院');
          localStorage.setItem('ZuHuID', '1400361376454545408');
          localStorage.setItem('deptCode', '1017');
          }else if (orgid=='47231022633110211A2101') {
          localStorage.setItem('orgname', '丽水市中医院');
          localStorage.setItem('ZuHuID', '1400360867068907520');
          localStorage.setItem('deptCode', '01040201');
          }else if (orgid=='47246102433112211A2101') {
          localStorage.setItem('orgname', '缙云县中医医院');
          localStorage.setItem('ZuHuID', '1429338802177000002');
          localStorage.setItem('deptCode', '');
          }else if (orgid=='47240018433118111A2101') {
          localStorage.setItem('orgname', '龙泉市中医医院');
          localStorage.setItem('ZuHuID', '1429338802177000003');
          localStorage.setItem('deptCode', '');
          }else if (orgid=='47243006833112611A2101') {
          localStorage.setItem('orgname', '庆元县中医医院');
          localStorage.setItem('ZuHuID', '1429338802177000004');
          localStorage.setItem('deptCode', '');
          }else if (orgid=='47234002X33112111A2101') {
          localStorage.setItem('orgname', '青田县中医医院');
          localStorage.setItem('ZuHuID', '1429338802177000005');
          localStorage.setItem('deptCode', '');
          }else if (orgid=='47240004533118111A1001') {
          localStorage.setItem('orgname', '龙泉市人民医院');
          localStorage.setItem('ZuHuID', '1429338802177000007');
          localStorage.setItem('deptCode', '');
          }
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
        login(username, password, code, orgid)
          .then((res) => {
            setToken(res.token);
            commit("SET_TOKEN", res.token);
            localStorage.setItem("orgid", orgid);
            if (orgid == "H41010500003") {
              localStorage.setItem("orgname", "河南中医药大学第一附属医院");
              localStorage.setItem("ZuHuID", "1400361376454545408"); // è¯·æ›¿æ¢ä¸ºå®žé™…çš„ZuHuID
              localStorage.setItem("deptCode", "1017"); // è¯·æ›¿æ¢ä¸ºå®žé™…的科室代码
            } else if (orgid == "H41082200269") {
              localStorage.setItem("orgname", "博爱县人民医院");
              localStorage.setItem("ZuHuID", "1400360867068907520"); // è¯·æ›¿æ¢ä¸ºå®žé™…çš„ZuHuID
              localStorage.setItem("deptCode", "01040201"); // è¯·æ›¿æ¢ä¸ºå®žé™…的科室代码
            } else if (orgid == "H41078202417") {
              localStorage.setItem("orgname", "辉县市中医院");
              localStorage.setItem("ZuHuID", "1429338802177000002"); // è¯·æ›¿æ¢ä¸ºå®žé™…çš„ZuHuID
              localStorage.setItem("deptCode", ""); // è¯·æ›¿æ¢ä¸ºå®žé™…的科室代码
            } else if (orgid == "47255004333112711A1001") {
              localStorage.setItem("orgname", "景宁畲族自治县人民医院");
              localStorage.setItem("ZuHuID", "1400361376454545408");
              localStorage.setItem("deptCode", "1017");
              localStorage.setItem("YongHuID", "1400466972205912064");
              localStorage.setItem("YongHuXM", "JNRMYY");
            } else if (orgid == "47231022633110211A2101") {
              localStorage.setItem("orgname", "丽水市中医院");
              localStorage.setItem("ZuHuID", "1400360867068907520");
              localStorage.setItem("deptCode", "01040201");
              localStorage.setItem("YongHuID", "1400398571877961728");
              localStorage.setItem("YongHuXM", "LSZYY");
            } else if (orgid == "47246102433112211A2101") {
              localStorage.setItem("orgname", "缙云县中医医院");
              localStorage.setItem("ZuHuID", "1429338802177000002");
              localStorage.setItem("deptCode", "");
              localStorage.setItem("YongHuID", "1462582201425072129");
              localStorage.setItem("YongHuXM", "JYZYY");
            } else if (orgid == "47240018433118111A2101") {
              localStorage.setItem("orgname", "龙泉市中医医院");
              localStorage.setItem("ZuHuID", "1429338802177000003");
              localStorage.setItem("deptCode", "");
              localStorage.setItem("YongHuID", "1462599660370661377");
              localStorage.setItem("YongHuXM", "LQZYY");
            } else if (orgid == "47243006833112611A2101") {
              localStorage.setItem("orgname", "庆元县中医医院");
              localStorage.setItem("ZuHuID", "1429338802177000004");
              localStorage.setItem("deptCode", "");
              localStorage.setItem("YongHuID", "1462585966286868480");
              localStorage.setItem("YongHuXM", "QYZYY");
            } else if (orgid == "47234002X33112111A2101") {
              localStorage.setItem("orgname", "青田县中医医院");
              localStorage.setItem("ZuHuID", "1429338802177000005");
              localStorage.setItem("deptCode", "");
              localStorage.setItem("YongHuID", "1462614919332499458");
              localStorage.setItem("YongHuXM", "QTHCZYY");
            } else if (orgid == "47240004533118111A1001") {
              localStorage.setItem("orgname", "龙泉市人民医院");
              localStorage.setItem("ZuHuID", "1429338802177000007");
              localStorage.setItem("deptCode", "");
              localStorage.setItem("YongHuID", "1478905109432766464");
              localStorage.setItem("YongHuXM", "LQRMYY");
            } else if (orgid == "20001001") {
              localStorage.setItem("orgname", "省立同德翠苑院区");
              localStorage.setItem("ZuHuID", "");
              localStorage.setItem("deptCode", "");
              localStorage.setItem("YongHuID", "");
              localStorage.setItem("YongHuXM", "");
            }
            resolve();
          })
          .catch((error) => {
            reject(error);
          });
      });
    },
    // èŽ·å–ç”¨æˆ·ä¿¡æ¯
    GetInfo({ commit, state }) {
      return new Promise((resolve, reject) => {
        getInfo().then(res => {
          const user = res.user
          const avatar = (user.avatar == "" || user.avatar == null) ? require("@/assets/images/profile.jpg") : process.env.VUE_APP_BASE_API + user.avatar;
          if (res.roles && res.roles.length > 0) { // éªŒè¯è¿”回的roles是否是一个非空数组
            commit('SET_ROLES', res.roles)
            commit('SET_PERMISSIONS', res.permissions)
          } else {
            commit('SET_ROLES', ['ROLE_DEFAULT'])
          }
          commit('SET_NAME', user.userName)
          commit('SET_nickNAME', user.nickName)
          commit('SET_Id', user.userId)
          commit('SET_hisUserId', user.hisUserId)
          commit('SET_leavehospitaldistrictcodes', user.belongWards)
          commit('SET_leaveldeptcodes', user.belongDepts)
          commit('SET_AVATAR', avatar)
          resolve(res)
        }).catch(error => {
          reject(error)
        })
      })
        getInfo()
          .then((res) => {
            const user = res.user;
            const avatar =
              user.avatar == "" || user.avatar == null
                ? require("@/assets/images/profile.jpg")
                : process.env.VUE_APP_BASE_API + user.avatar;
            if (res.roles && res.roles.length > 0) {
              // éªŒè¯è¿”回的roles是否是一个非空数组
              commit("SET_ROLES", res.roles);
              commit("SET_PERMISSIONS", res.permissions);
            } else {
              commit("SET_ROLES", ["ROLE_DEFAULT"]);
            }
            commit("SET_NAME", user.userName);
            commit("SET_nickNAME", user.nickName);
            commit("SET_Id", user.userId);
            commit("SET_hisUserId", user.hisUserId);
            commit("SET_leavehospitaldistrictcodes", user.belongWards);
            commit("SET_leaveldeptcodes", user.belongDepts);
            commit("SET_AVATAR", avatar);
            resolve(res);
          })
          .catch((error) => {
            reject(error);
          });
      });
    },
    // é€€å‡ºç³»ç»Ÿ
    LogOut({ commit, state }) {
      return new Promise((resolve, reject) => {
        logout(state.token).then(() => {
          commit('SET_TOKEN', '')
          commit('SET_ROLES', [])
          commit('SET_PERMISSIONS', [])
          removeToken()
          resolve()
        }).catch(error => {
          reject(error)
        })
      })
        logout(state.token)
          .then(() => {
            commit("SET_TOKEN", "");
            commit("SET_ROLES", []);
            commit("SET_PERMISSIONS", []);
            removeToken();
            resolve();
          })
          .catch((error) => {
            reject(error);
          });
      });
    },
    // å‰ç«¯ ç™»å‡º
    FedLogOut({ commit }) {
      return new Promise(resolve => {
        commit('SET_TOKEN', '')
        removeToken()
        resolve()
      })
    }
  }
}
      return new Promise((resolve) => {
        commit("SET_TOKEN", "");
        removeToken();
        resolve();
      });
    },
  },
};
export default user
export default user;
src/utils/directives/preventReClick.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
export default {
  install(Vue) {
    Vue.directive('preventReClick', {
      inserted(el, binding) {
        el.addEventListener('click', () => {
          if (!el.disabled) {
            el.disabled = true;
            // å¯ä»¥æ·»åŠ ç¦ç”¨æ ·å¼ï¼ˆå¯é€‰ï¼‰
            el.style.pointerEvents = 'none';
            el.style.opacity = '0.6';
            // è®¾å®šæ¢å¤æ—¶é—´ï¼Œbinding.value æ˜¯æŒ‡ä»¤ä¼ å…¥çš„值(如:v-prevent-reclick="2000")
            setTimeout(() => {
              el.disabled = false;
              el.style.pointerEvents = 'all';
              el.style.opacity = '1';
            }, binding.value || 1500); // é»˜è®¤1.5秒
          }
        });
      }
    });
  }
};
src/utils/request.js
@@ -110,7 +110,7 @@
          }
        )
          .then(() => {
          this.$router.replace("/login");
          })
          .catch(() => {
            isRelogin.show = false;
src/utils/sipService-cs.js
@@ -4,174 +4,259 @@
  constructor() {
    this.ua = null;
    this.currentSession = null;
    this.onStatusChange = null; // çŠ¶æ€å˜åŒ–å›žè°ƒ
    this.onCallStatusChange = null; // æ–°å¢žé€šè¯çŠ¶æ€å›žè°ƒ
    this.onStatusChange = null;
    this.onCallStatusChange = null;
    this.onIncomingCall = null;
    this.isRegistered = false; // æ–°å¢žæ³¨å†ŒçŠ¶æ€æ ‡å¿—
    this.registrationTime = null; // æ–°å¢žæ³¨å†ŒæˆåŠŸæ—¶é—´æˆ³
  }
  // åˆå§‹åŒ–SIP客户端
  init(config) {
    try {
      this.updateStatus("connecting", "连接中...");
      this.updateStatus("connecting", "连接中;...");
      this.ua = new JsSIP.UA({
        sockets: [new JsSIP.WebSocketInterface(config.wsUrl)],
        uri: config.sipUri,
        password: config.password,
        display_name: config.displayName,
        iceservers: [],
        // realm: config.realm,
        iceServers: [],
        register: true,
        session_expires: 180,
        sessionTimersExpires: 300, // è®¾ç½® Session-Expires=120(必须 >= Min-SE)
        extraHeaders: [
          "Min-SE: 120", // å¯é€‰ï¼šæ˜¾å¼å‘Šè¯‰æœåŠ¡å™¨ä½ æ”¯æŒçš„æœ€å°å€¼
        ],
        register_expires: 300, // æ³¨å†Œæœ‰æ•ˆæœŸ(秒)
        connection_recovery_min_interval: 2, // æœ€å°é‡è¿žé—´éš”
        connection_recovery_max_interval: 30, // æœ€å¤§é‡è¿žé—´éš”
        sessionExpires: 1800,
        minSessionExpires: 90,
        register_expires: 300,
      });
      this.ua.start();
      // æ³¨å†Œäº‹ä»¶ç›‘听
      // äº‹ä»¶ç›‘听
      this.ua.on("registered", () => {
        this.isRegistered = true;
        this.registrationTime = Date.now(); // è®°å½•注册成功时间
        console.log(this.registrationTime, "注册时间");
        this.updateStatus("registered", "已注册");
      });
      this.ua.on("registrationFailed", (e) => {
        this.isRegistered = false;
        this.updateStatus("failed", `注册失败: ${e.cause}`);
      });
      this.ua.on("disconnected", () => {
        this.updateStatus("disconnected", "连接断开");
      this.ua.on("unregistered", () => {
        this.isRegistered = false;
        let registrationTime = Date.now(); // è®°å½•注销成功时间
        console.log(registrationTime, "注销时间");
        this.updateStatus("disconnected", "已注销");
      });
      this.ua.on("connected", () => {
        this.updateStatus("connecting", "重新连接中...");
      });
      // ç›‘听来电
      this.ua.on("newRTCSession", (data) => {
        this.handleIncomingCall(data.session);
      });
      this.ua.on("disconnected", () =>
        this.updateStatus("disconnected", "连接断开")
      );
      this.ua.on("connected", () =>
        this.updateStatus("connecting", "重新连接中...")
      );
      this.ua.on("newRTCSession", (data) =>
        this.handleIncomingCall(data.session)
      );
    } catch (error) {
      this.updateStatus("failed", `初始化失败: ${error.message}`);
      console.error("SIP初始化失败:", error);
      throw error;
    }
  }
  handleIncomingCall(session) {
    if (session.direction === "incoming") {
      console.log("来电:", session.remote_identity.uri.toString());
      // å¯ä»¥åœ¨è¿™é‡Œè§¦å‘ UI é€šçŸ¥
      if (this.onIncomingCall) {
        this.onIncomingCall(session);
      }
  // æ–°å¢žæ–¹æ³•:检查是否可以呼叫
  canMakeCall(minDelay = 2000) {
    if (!this.isRegistered) {
      return { canCall: false, reason: "SIP未注册,无法呼叫" };
    }
  }
  // æ›´æ–°çŠ¶æ€å¹¶é€šçŸ¥UI
  updateStatus(type, text) {
    console.log(`SIP状态更新: ${type} - ${text}`);
    if (this.onStatusChange) {
      this.onStatusChange({ type, text });
    }
  }
    const now = Date.now();
    const timeSinceRegistration = now - this.registrationTime;
  // ä¸€é”®æ‹¨å· - å¢žåŠ æ³¨å†ŒçŠ¶æ€æ£€æŸ¥
    if (timeSinceRegistration < minDelay) {
      const remaining = minDelay - timeSinceRegistration;
      return {
        canCall: false,
        reason: `注册成功,请等待 ${Math.ceil(remaining / 1000)} ç§’后再呼叫`,
      };
    }
    return { canCall: true, reason: "" };
  }
  makeCall(targetNumber) {
    if (!this.ua) {
      throw new Error("SIP客户端未初始化");
    const { canCall, reason } = this.canMakeCall();
    if (!canCall) {
      return Promise.reject(new Error(reason));
    }
    return new Promise((resolve, reject) => {
      try {
        if (!this.ua) {
          throw new Error("SIP客户端未初始化");
        }
    if (!this.ua.isRegistered()) {
      throw new Error("SIP未注册,无法呼叫");
    }
        if (!this.ua.isRegistered()) {
          throw new Error("SIP未注册,无法呼叫");
        }
    const options = {
      sessionTimers: true,
      sessionTimersExpires: 300,
      extraHeaders: [
        "Min-SE: 120",
        "Route: <sip:@192.168.100.6>",
        "Accept: application/sdp",
        "Supported: replaces, timer",
        "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS",
      ],
      eventHandlers: {
        progress: (e) => {
          this.updateCallStatus("calling", "呼叫中...");
        },
        failed: (e) => {
          this.updateCallStatus("ended", `呼叫失败: ${e.cause}`);
        },
        ended: (e) => {
          this.updateCallStatus("ended", "通话结束");
        },
        confirmed: (e) => {
          this.updateCallStatus("connected", "通话已接通");
        },
      },
      mediaConstraints: {
        audio: true,
        video: false,
      },
      rtcOfferConstraints: {
        offerToReceiveAudio: 1,
        offerToReceiveVideo: 0,
        mandatory: {
          OfferToReceiveAudio: true,
          OfferToReceiveVideo: false,
        },
      },
      pcConfig: {
        iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
        iceTransportPolicy: "all",
        bundlePolicy: "balanced",
        rtcpMuxPolicy: "require",
        codecs: {
          audio: [
            { name: "PCMU", clockRate: 8000, payloadType: 0 },
            { name: "PCMA", clockRate: 8000, payloadType: 8 },
        const options = {
          sessionTimers: true,
          sessionTimersExpires: 300,
          extraHeaders: [
            "Min-SE: 120",
            "Route: <sip:@192.168.100.6>",
            "Accept: application/sdp",
            "Supported: replaces, timer",
            "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS",
          ],
          video: [],
        },
      },
    };
          mediaConstraints: {
            audio: true,
            video: false,
          },
          rtcOfferConstraints: {
            offerToReceiveAudio: 1,
            offerToReceiveVideo: 0,
            mandatory: {
              OfferToReceiveAudio: true,
              OfferToReceiveVideo: false,
            },
          },
          eventHandlers: {
            progress: () => this.updateCallStatus("calling", "呼叫中..."),
            failed: (e) => {
              this.handleCallFailure(e, reject);
            },
            ended: () => this.updateCallStatus("ended", "通话结束"),
            confirmed: () => {
              this.updateCallStatus("connected", "通话已接通");
              resolve();
            },
          },
          pcConfig: {
            iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
            iceTransportPolicy: "all",
            bundlePolicy: "balanced",
            rtcpMuxPolicy: "require",
            codecs: {
              audio: [
                { name: "PCMU", clockRate: 8000, payloadType: 0 },
                { name: "PCMA", clockRate: 8000, payloadType: 8 },
              ],
              video: [],
            },
          },
        };
    this.currentSession = this.ua.call(
      `sip:${targetNumber}@192.168.100.6`,
      options
    );
    // åœ¨ä¼šè¯åˆ›å»ºåŽä¿®æ”¹ SDP
    this.currentSession.on("peerconnection", (pc) => {
       this.updateCallStatus('calling', '呼叫中...');
      pc.createOffer = (offerOptions) => {
        return RTCPeerConnection.prototype.createOffer
          .call(pc, offerOptions)
          .then((offer) => {
            const modifiedSdp = offer.sdp
              .replace(/c=IN IP4 192\.168\.100\.10/g, "c=IN IP4 192.168.100.6")
              .replace(/m=audio \d+ RTP\/AVP.*/, "m=audio 7078 RTP/AVP 0 8");
            return new RTCSessionDescription({
              type: "offer",
              sdp: modifiedSdp,
            });
          });
        this.currentSession = this.ua.call(
          `sip:${targetNumber}@192.168.100.6`,
          options
        );
        // åœ¨ä¼šè¯åˆ›å»ºåŽä¿®æ”¹ SDP
        this.currentSession.on("peerconnection", (pc) => {
          this.updateCallStatus("calling", "呼叫中...");
          pc.createOffer = (offerOptions) => {
            return RTCPeerConnection.prototype.createOffer
              .call(pc, offerOptions)
              .then((offer) => {
                const modifiedSdp = offer.sdp
                  .replace(
                    /c=IN IP4 192\.168\.100\.10/g,
                    "c=IN IP4 192.168.100.6"
                  )
                  .replace(
                    /m=audio \d+ RTP\/AVP.*/,
                    "m=audio 7078 RTP/AVP 0 8"
                  );
                return new RTCSessionDescription({
                  type: "offer",
                  sdp: modifiedSdp,
                });
              });
          };
        });
        this.setupPeerConnection(this.currentSession);
        this.setupAudio(this.currentSession);
      } catch (error) {
        this.updateCallStatus("failed", `呼叫失败22: ${error.message}`);
        reject(error);
      }
    });
  }
  setupPeerConnection(session) {
    session.on("peerconnection", (pc) => {
      const originalCreateOffer = pc.createOffer.bind(pc);
      pc.createOffer = async (offerOptions) => {
        try {
          const offer = await originalCreateOffer(offerOptions);
          return this.normalizeSDP(offer);
        } catch (error) {
          console.error("创建Offer失败:", error);
          throw error;
        }
      };
    });
    this.currentSession.on('failed', (e) => {
        this.updateCallStatus('failed', `呼叫失败2: ${e}`);
      });
      this.currentSession.on('ended', () => {
        this.updateCallStatus('ended', '通话已结束');
      });
      this.currentSession.on('confirmed', () => {
        this.updateCallStatus('connected', '通话已接通');
      });
    this.setupAudio(this.currentSession);
  }
  normalizeSDP(offer) {
    let sdp = offer.sdp;
    // æ ‡å‡†åŒ–SDP
    sdp = sdp.replace(/c=IN IP4.*\r\n/, "c=IN IP4 0.0.0.0\r\n");
    sdp = sdp.replace(
      /m=audio \d+.*\r\n/,
      "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n"
    );
    // ç¡®ä¿åŒ…含基本编解码器
    if (!sdp.includes("PCMU/8000")) sdp += "a=rtpmap:0 PCMU/8000\r\n";
    if (!sdp.includes("PCMA/8000")) sdp += "a=rtpmap:8 PCMA/8000\r\n";
    // æ·»åŠ å¿…è¦å±žæ€§
    sdp += "a=rtcp-mux\r\n";
    sdp += "a=sendrecv\r\n";
    console.log("标准化后的SDP:", sdp);
    return new RTCSessionDescription({
      type: offer.type,
      sdp: sdp,
    });
  }
  handleCallFailure(e, reject) {
    if (e.response?.status_code === 422) {
      const serverMinSE = e.response.headers["Min-SE"]?.[0]?.raw || "未知";
      console.error(`服务器要求 Min-SE â‰¤ ${serverMinSE},当前设置: 120`);
    }
    console.error("呼叫失败详情:", {
      cause: e.cause,
      message: e.message,
      response: e.response && {
        status: e.response.status_code,
        reason: e.response.reason_phrase,
      },
    });
    let errorMessage = "呼叫失败";
    switch (e.cause) {
      case "Incompatible SDP":
        errorMessage = "媒体协商失败,请检查编解码器配置";
        break;
      case "488":
      case "606":
        errorMessage = "对方设备不支持当前媒体配置";
        break;
      case "422":
        errorMessage = "会话参数不满足服务器要求";
        break;
      default:
        errorMessage = `呼叫失败3: ${e.cause || e.message}`;
    }
    this.updateCallStatus("failed55", errorMessage);
    reject(new Error(errorMessage));
  }
  setupAudio(session) {
    session.connection.addEventListener("addstream", (e) => {
      const audioElement = document.getElementById("remoteAudio");
@@ -180,21 +265,37 @@
      }
    });
  }
  // æŒ‚断当前通话
  endCall() {
  if (this.currentSession) {
    if (this.currentSession) {
      this.currentSession.terminate();
      this.updateCallStatus('ended', '通话已结束');
      this.updateCallStatus("ended", "通话已结束");
      this.currentSession = null;
    }
  }
  // æ–°å¢žæ–¹æ³•:更新通话状态
  updateStatus(type, text) {
    console.log(`SIP状态更新: ${type} - ${text}`);
    if (this.onStatusChange) {
      this.onStatusChange({ type, text });
    }
  }
  updateCallStatus(type, text) {
    console.log(`通话状态更新: ${type} - ${text}`);
    if (this.onCallStatusChange) {
      this.onCallStatusChange({ type, text });
    }
  }
  handleIncomingCall(session) {
    if (session.direction === "incoming") {
      console.log("来电:", session.remote_identity.uri.toString());
      if (this.onIncomingCall) {
        this.onIncomingCall(session);
      }
    }
  }
}
export default new SipService();
src/utils/sipService.js
@@ -7,6 +7,8 @@
    this.onStatusChange = null;
    this.onCallStatusChange = null;
    this.onIncomingCall = null;
    this.isRegistered = false; // æ–°å¢žæ³¨å†ŒçŠ¶æ€æ ‡å¿—
    this.registrationTime = null; // æ–°å¢žæ³¨å†ŒæˆåŠŸæ—¶é—´æˆ³
  }
  init(config) {
@@ -28,12 +30,25 @@
      this.ua.start();
      // äº‹ä»¶ç›‘听
      this.ua.on("registered", () =>
        this.updateStatus("registered", "已注册56")
      );
      this.ua.on("registrationFailed", (e) =>
        this.updateStatus("failed", `注册失败11: ${e.cause}`)
      );
      this.ua.on("registered", () => {
        this.isRegistered = true;
        this.registrationTime = Date.now(); // è®°å½•注册成功时间
        console.log(this.registrationTime, "注册时间");
        this.updateStatus("registered", "已注册");
      });
      this.ua.on("registrationFailed", (e) => {
        this.isRegistered = false;
        this.updateStatus("failed", `注册失败: ${e.cause}`);
      });
      this.ua.on("unregistered", () => {
        this.isRegistered = false;
        let registrationTime = Date.now(); // è®°å½•注销成功时间
        console.log(registrationTime, "注销时间");
        this.updateStatus("disconnected", "已注销");
      });
      this.ua.on("disconnected", () =>
        this.updateStatus("disconnected", "连接断开")
      );
@@ -49,8 +64,30 @@
      throw error;
    }
  }
  // æ–°å¢žæ–¹æ³•:检查是否可以呼叫
  canMakeCall(minDelay = 2000) {
    if (!this.isRegistered) {
      return { canCall: false, reason: "SIP未注册,无法呼叫" };
    }
    const now = Date.now();
    const timeSinceRegistration = now - this.registrationTime;
    if (timeSinceRegistration < minDelay) {
      const remaining = minDelay - timeSinceRegistration;
      return {
        canCall: false,
        reason: `注册成功,请等待 ${Math.ceil(remaining / 1000)} ç§’后再呼叫`,
      };
    }
    return { canCall: true, reason: "" };
  }
  makeCall(targetNumber) {
    const { canCall, reason } = this.canMakeCall();
    if (!canCall) {
      return Promise.reject(new Error(reason));
    }
    return new Promise((resolve, reject) => {
      try {
        if (!this.ua) {
@@ -165,7 +202,7 @@
        errorMessage = "会话参数不满足服务器要求";
        break;
      default:
        errorMessage = `呼叫失败: ${e.cause || e.message}`;
        errorMessage = `呼叫失败3: ${e.cause || e.message}`;
    }
    this.updateCallStatus("failed55", errorMessage);
src/views/complaint/complaintlist/index.vue
@@ -80,7 +80,6 @@
          <el-col :span="1.5">
            <el-button
              type="primary"
              plain
              icon="el-icon-plus"
              size="medium"
              @click="handleAdd"
@@ -91,7 +90,6 @@
          <el-col :span="1.5">
            <el-button
              type="success"
              plain
              icon="el-icon-edit"
              size="medium"
              :disabled="single"
src/views/complaint/complaintmy/index.vue
@@ -51,7 +51,6 @@
          <el-col :span="1.5">
            <el-button
              type="primary"
              plain
              icon="el-icon-plus"
              size="medium"
              @click="handleAdd"
src/views/followvisit/OutpatientAgain/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1845 @@
<template>
  <div class="app-container">
    <div class="leftvlue" style="margin-bottom: 20px">
      <el-row :gutter="10">
        <el-col :span="2.5" v-for="(item, index) in cardlist" :key="index">
          <el-card
            shadow="hover"
            :body-style="item.router ? ' cursor: pointer' : 'cursor: default'"
          >
            <div style="padding: 8px" @click="$router.push(item.router)">
              <span>{{ item.name }}</span>
              <div
                style="
                  text-align: center;
                  font-size: 18px;
                  margin-top: 10px;
                  font-weight: 600;
                "
              >
                {{ item.value ? item.value : 0 }}
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="2.5">
          <div class="ysfleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>表单已发送</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ yfsvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
        <el-col :span="2.5">
          <div class="errleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>异常</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ ycvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
      </el-row>
    </div>
    <el-row :gutter="20">
      <!--用户数据-->
      <el-form
        :model="topqueryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        v-show="showSearch"
        label-width="98px"
      >
        <el-form-item label="任务名称">
          <el-input
            v-model="topqueryParams.taskName"
            placeholder="请选择任务名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="门诊时间">
          <el-date-picker
            v-model="dateRange"
            style="width: 240px"
            value-format="yyyy-MM-dd"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="应随访时间">
          <el-date-picker
            v-model="dateRangefs"
            style="width: 240px"
            value-format="yyyy-MM-dd"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="患者姓名" prop="sendname">
          <el-input
            v-model="topqueryParams.sendname"
            placeholder="请输入患者姓名"
          ></el-input>
        </el-form-item>
        <el-form-item label="诊断名称" prop="leavediagname">
          <el-input
            v-model="topqueryParams.leavediagname"
            placeholder="请输入诊断名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="患者范围" prop="status">
          <el-cascader
            v-model="topqueryParams.scopetype"
            placeholder="默认全部"
            :options="sourcetype"
            :props="{ expandTrigger: 'hover' }"
            @change="handleChange"
          ></el-cascader>
        </el-form-item>
        <el-form-item label="任务状态" prop="status">
          <el-select v-model="topqueryParams.sendstate" placeholder="请选择">
            <el-option
              v-for="item in topicoptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="排序方式" prop="status">
          <el-select v-model="topqueryParams.sort" placeholder="请选择">
            <el-option
              v-for="item in topicoptionssort"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="medium"
            @click="handleQuery(1)"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
      <el-divider></el-divider>
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-upload2"
                size="medium"
                @click="handleExport"
                >导出</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="primary"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
          >
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-warning-outline"
                size="medium"
                @click="toleadExport(1)"
                >执行失败</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="danger"
                plain
                icon="el-icon-warning"
                size="medium"
                @click="toleadExport(2)"
                >结果异常</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="success"
                plain
                size="medium"
                @click="buidegetTasklist()"
                >待办服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="primary"
                plain
                size="medium"
                @click="affiliation()"
                >本人所属服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >今日服务</el-button
              >
            </div>
          </div>
        </el-col>
      </el-row>
      <el-table
        v-loading="loading"
        :data="userList"
        :row-class-name="tableRowClassName"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column
          label="任务名称"
          fixed
          width="150"
          show-overflow-tooltip
          align="center"
          key="taskName"
          prop="taskName"
        />
        <!-- <el-table-column label="序号" fixed align="center" key="id" prop="id" /> -->
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          key="sendname"
          prop="sendname"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                gettoken360(scope.row.sfzh, scope.row.drcode, scope.row.drname)
              "
              ><span class="button-textsc">{{
                scope.row.sendname
              }}</span></el-button
            >
          </template>
        </el-table-column>
        <el-table-column
          label="任务状态"
          align="center"
          key="sendstate"
          prop="sendstate"
          width="120"
        >
          <template slot-scope="scope">
            <el-tooltip
              class="item"
              effect="dark"
              :content="scope.row.remark"
              placement="top-start"
            >
              <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>
            </el-tooltip>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务异常说明"
          width="120"
          align="center"
          key="remark"
          prop="remark" -->
        />
        <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
          label="随访完成时间"
          sortable
          align="center"
          prop="finishtime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.finishtime) }}</span>
          </template>
        </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="200"
          align="center"
          key="longSendTime"
          prop="longSendTime"
        >
          <template slot-scope="scope">
            <span>{{ formatTime(scope.row.longSendTime) }}</span>
          </template></el-table-column
        >
        <el-table-column
          label="主治医生"
          width="120"
          align="center"
          key="drname"
          prop="drname"
        />
        <!-- <el-table-column
          label="门诊天数"
          width="120"
          align="center"
          key="endDay"
          prop="endDay"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.endDay ? scope.row.endDay + "天" : "" }}</span>
          </template>
        </el-table-column> -->
        <el-table-column
          label="身份证号码"
          width="200"
          align="center"
          key="sfzh"
          prop="sfzh"
        />
        <el-table-column
          label="联系电话"
          width="200"
          align="center"
          key="phone"
          prop="phone"
        />
        <el-table-column
          label="责任护士"
          width="120"
          align="center"
          key="nurseName"
          prop="nurseName"
        />
        <!-- <el-table-column
          label="病历号"
          align="center"
          sortable
          key="medicalRecordNo"
          prop="medicalRecordNo"
          width="120"
        /> -->
        <!-- <el-table-column label="年龄" align="center" key="age" prop="age" /> -->
        <!-- <el-table-column label="性别"width="100" align="center" key="sex" prop="sex" /> -->
        <!-- <el-table-column label="床号" align="center" key="badNo" prop="badNo" /> -->
        <el-table-column
          label="首次随访科室"
          align="center"
          key="deptname"
          prop="deptname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="本次科室"
          align="center"
          key="visitDeptName"
          prop="visitDeptName"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="病区"
          align="center"
          key="leavehospitaldistrictname"
          prop="leavehospitaldistrictname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="诊断名称"
          align="center"
          key="leavediagname"
          prop="leavediagname"
          width="120"
          :show-overflow-tooltip="true"
        >
        </el-table-column>
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="门诊随访模板名称"
          align="center"
          key="templatename"
          prop="templatename"
          width="200"
        />
        <el-table-column
          label="任务执行方式"
          align="center"
          key="preachform"
          prop="preachform"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.preachform">{{ item }}、 </span>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务发送流程"
          align="center"
          key="serviceSubtaskRecordList"
          prop="serviceSubtaskRecordList"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.serviceSubtaskRecordList"
              >{{ item.remark }}、
            </span>
          </template>
        </el-table-column> -->
        <el-table-column
          label="任务结果说明"
          width="220"
          align="center"
          key="remark"
          prop="remark"
        >
          <template slot-scope="scope" v-if="scope.row.remark">
            <el-tooltip
              :content="scope.row.remark"
              placement="top"
              effect="dark"
            >
              <el-tag
                type="warning"
                v-if="scope.row.sendstate != 5 && scope.row.sendstate != 4"
                >{{ scope.row.remark }}</el-tag
              >
              <el-tag type="warning" v-else>{{ scope.row.remark }}</el-tag>
            </el-tooltip>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
          fixed="right"
          width="300"
          class-name="small-padding fixed-width"
        >
          <template slot-scope="scope">
            <!-- <el-tooltip
              class="item"
              effect="dark"
              content="再次随访"
              placement="top"
            >
              <el-button
                size="medium"
                type="text"
                v-if="scope.row.isVisitAgain!=2"
                @click="followupvisit(scope.row)"
                ><span class="button-bb"
                  ><i class="el-icon-s-promotion"></i>再次随访</span
                ></el-button
              >
            </el-tooltip>
            <el-tooltip
              v-if="scope.row.sendstate == 1 || scope.row.sendstate == 2"
              class="item"
              effect="dark"
              content="暂停服务"
              placement="top"
            >
              <el-button
                size="medium"
                type="text"
                @click="handlestop(scope.row)"
                v-hasPermi="['system:user:edit']"
                ><span class="button-sc"
                  ><i class="el-icon-remove-outline"></i>暂停服务</span
                ></el-button
              >
            </el-tooltip> -->
            <el-button size="medium" type="text" @click="Seedetails(scope.row)"
              ><span class="button-zx"
                ><i class="el-icon-s-order"></i>查看详情</span
              ></el-button
            >
            <el-button
              size="medium"
              type="text"
              @click="handleUpdate(scope.row)"
              ><span class="button-textxga"
                ><i class="el-icon-edit"></i>患者过滤</span
              ></el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="topqueryParams.pageNum"
        :limit.sync="topqueryParams.pageSize"
        @pagination="getList"
      />
    </el-row>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹å½±åƒéšè®¿å¯¹è¯æ¡† -->
    <el-dialog
      :title="amendtag ? '修改患者信息' : '新增患者'"
      :visible.sync="Labelchange"
      width="900px"
    >
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="8">
            <el-form-item label="姓名" width="100" prop="name">
              <el-input
                v-model="form.name"
                placeholder="请输入姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="性别" width="100" prop="sex">
              <el-select v-model="form.sex" placeholder="请选择性别">
                <el-option
                  v-for="dict in sextype"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="年龄" prop="age">
              <el-input
                v-model="form.age"
                placeholder="请输入年龄"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
<el-row >
          <el-col :span="8">
            <el-form-item label="过滤医生" width="100" prop="filterDrname">
              <el-input
                v-model="form.filterDrname"
                placeholder="请输入医生姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="过滤原因">
              <el-input
                v-model="form.notrequiredreason"
                type="textarea"
                placeholder="请输入过滤原因"
              ></el-input>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
        <el-button @click="cancel">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
    <!-- ä¿®æ”¹å‘送时间对话框 -->
    <el-dialog
      title="发送时间设置"
      :visible.sync="modificationVisible"
      width="45%"
    >
      <div style="margin-bottom: 20px; color: red">
        ç»Ÿä¸€ä¿®æ”¹å½“天未发送的任务时间
      </div>
      <el-form
        :model="ruleForm"
        :rules="rules"
        ref="ruleForm"
        label-width="120px"
        class="demo-ruleForm"
      >
        <el-form-item label="发送日期">
          <el-date-picker
            v-model="ruleForm.value1"
            type="date"
            placeholder="选择日期"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="时间段" prop="type">
          <el-checkbox-group v-model="ruleForm.type">
            <el-checkbox label="上午" name="type"></el-checkbox>
            <el-checkbox label="下午" name="type"></el-checkbox>
            <el-checkbox label="晚上" name="type"></el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="上午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value2"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="下午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value3"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="晚上时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value4"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="modificationVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="modificationVisible = false"
          >ç¡® å®š</el-button
        >
      </span>
    </el-dialog>
    <!-- å†æ¬¡éšè®¿ -->
    <el-dialog title="患者再次随访" :visible.sync="dialogFormVisible">
      <el-form ref="zcform" :rules="zcrules" :model="zcform" label-width="80px">
        <el-form-item label="任务名称">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.taskName"
          ></el-input>
        </el-form-item>
        <el-form-item label="患者名称">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.sendname"
          ></el-input>
        </el-form-item>
        <el-form-item label="年龄">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.age"
          ></el-input>
        </el-form-item>
        <el-form-item label="科室">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.deptname"
          ></el-input>
        </el-form-item>
        <el-form-item label="病区">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.leavehospitaldistrictname"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访方式" prop="resource">
          <el-radio-group v-model="zcform.resource">
            <el-radio label="1">本病区随访</el-radio>
            <el-radio label="2">随访中心随访</el-radio>
          </el-radio-group>
        </el-form-item>
        <!-- <el-form-item label="即刻发送">
          <el-switch v-model="zcform.delivery"></el-switch>
        </el-form-item> -->
        <el-form-item label="门诊时间">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.endtime"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访完成时间" prop="date1">
          <el-date-picker
            type="date"
            placeholder="选择日期"
            v-model="zcform.date1"
            style="width: 100%"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="随访记录">
          <el-input type="textarea" v-model="zcform.remark"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="setupsubtask">确认创建服务</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import {
  delUser,
  addUser,
  updateUser,
  resetUserPwd,
  changeUserStatus,
} from "@/api/system/user";
import {
  getTaskservelist,
  buidegetTasklist,
  addserviceSubtask,
  query360PatInfo,
} from "@/api/AiCentre/index";
import { alterpatient, particularpatient } from "@/api/patient/homepage";
import Treeselect from "@riophae/vue-treeselect";
import store from "@/store";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
  name: "Discharge",
  dicts: ["sys_normal_disable", "sys_user_sex", "sys_yujing", "sys_suggest"],
  components: { Treeselect },
  data() {
    return {
      // é®ç½©å±‚
      loading: true,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      dialogFormVisible: false,
      // æ€»æ¡æ•°
      total: 0,
      // ç”¨æˆ·è¡¨æ ¼æ•°æ®
      userList: null,
      // å¼¹å‡ºå±‚标题
      title: "新增影像随访",
      // æ˜¯å¦æ˜¾ç¤ºä¿®æ”¹ã€æ·»åŠ å¼¹å‡ºå±‚
      addalteropen: false,
      // ä¿®æ”¹å‘送时间对话框
      modificationVisible: false,
      // éƒ¨é—¨åç§°
      deptName: undefined,
      // é»˜è®¤å¯†ç 
      initPassword: undefined,
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      dateRangefs: [],
      // å²—位选项
      postOptions: [],
      ruleForm: {
        type: [],
      },
      zcform: {},
      dynamicTags: ["选项一", "选项二", "选项三"], //选项
      inputVisible: false,
      Labelchange: false,
      ycvalue: "",
      yfsvalue: "",
      inputValue: "",
      preachform: "",
      previewVisible: false, //影像随访预览弹框
      radio: "",
      radios: [],
      total: 0, // æ€»æ¡æ•°
      ImportQuantity: 999, //导影像随访数量
      //预览影像随访信息
      previewvalue: {
        username: "这个医生对你怎么样",
      },
      value: [],
      list: [],
      sourcetype: [
        {
          value: 1,
          label: "科室",
          children: [],
        },
        {
          value: 2,
          label: "病区",
          children: [],
        },
        {
          value: 3,
          label: "全部",
        },
      ],
      loading: false,
      cardlist: [
        {
          name: "门诊服务总量",
          value: 0,
        },
        // {
        //   name: "患者过滤",
        //   value: 0,
        // },
        {
          name: "需随访",
          value: 0,
        },
        {
          name: "发送失败",
          value: 0,
        },
        {
          name: "待随访",
          value: 0,
        },
        // {
        //   name: "已发送",
        //   value: 0,
        // },
        // {
        //   name: "表单已发送",
        //   value: 0,
        // },
      ],
      zcrules: {
        date1: [
          { required: true, message: "请选择随访方式", trigger: "change" },
        ],
        resource: [
          { required: true, message: "请选择随访时间", trigger: "blur" },
        ],
      },
      // è¡¨å•参数
      form: {
        phonenumber: "",
        totagid: "",
        types: "",
        nickName: "",
        qystatus: "",
        btstatus: "",
      },
      topicoptionssort: [
        {
          value: 0,
          label: "门诊时间(正序)",
        },
        {
          value: 1,
          label: "门诊时间(倒序)",
        },
        {
          value: 2,
          label: "发送时间(正序)",
        },
        {
          value: 3,
          label: "发送时间(倒序)",
        },
        {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: 2,
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 é—¨è¯Šæ—¶é—´(正序)    1 é—¨è¯Šæ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 3,
        searchscope: 3,
        visitCount: 2,
        scopetype: [],
        visitDeptCodes: [],
        leaveldeptcodes:[],
        leavehospitaldistrictcodes: [],
      },
      propss: { multiple: true },
      options: [],
      topicoptions: [
        {
          value: null,
          label: "全部",
        },
        {
          value: 1,
          label: "表单已领取",
        },
        {
          value: 2,
          label: "待随访",
        },
        {
          value: 3,
          label: "表单已发送",
        },
        {
          value: 4,
          label: "不执行",
        },
        {
          value: 5,
          label: "发送失败",
        },
        {
          value: 6,
          label: "已完成",
        },
      ],
      sextype: [
        {
          value: 1,
          label: "男",
        },
        {
          value: 2,
          label: "女",
        },
      ],
      topicoptionsyj: [
        {
          value: 1,
          label: "异常",
        },
        {
          value: 0,
          label: "正常",
        },
      ],
      url: "http://9.208.2.190:8090/smartor/serviceExternal/query360PatInfo",
      postData: {
        XiaoXiTou: {
          FaSongFCSJC: "ZJHES",
          FaSongJGID: localStorage.getItem("orgid"),
          FaSongJGMC: localStorage.getItem("orgname"),
          FaSongSJ: "2025-01-09 17:29:36",
          FaSongXTJC: "SUIFANGXT",
          FaSongXTMC: "随访系统",
          XiaoXiID: "5FA92AFB-9833-4608-87C7-F56A654AC171",
          XiaoXiLX: "SC_LC_360STCX",
          XiaoXiMC: "360 视图查询",
          ZuHuID: localStorage.getItem("ZuHuID"),
          ZuHuMC: localStorage.getItem("orgname"),
        },
        YeWuXX: {
          BingRenXX: {
            ZhengJianHM: "",
            ZhengJianLXDM: "01",
            ZhengJianLXMC: "居民身份证",
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
          },
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
          },
        },
      },
      amendtag: false,
      errtype: "",
      leavehospitaldistrictcode: "",
      serviceState: [],
      checkboxlist: [],
      // è¡¨å•校验
      rules: {},
    };
  },
  watch: {},
  created() {
    this.serviceState = store.getters.serviceState;
    this.checkboxlist = store.getters.checkboxlist;
    this.errtype = this.$route.query.errtype;
    this.leavehospitaldistrictcode =
      this.$route.query.leavehospitaldistrictcode;
    this.sourcetype[0].children = store.getters.belongDepts.map((dept) => {
      return {
        label: dept.deptName,
        value: dept.deptCode,
      };
    });
    this.sourcetype[1].children = store.getters.belongWards.map((dept) => {
      return {
        label: dept.districtName,
        value: dept.districtCode,
      };
    });
    if (this.errtype) {
      this.toleadExport(2);
    } else {
      this.getList(1);
    }
    this.getConfigKey("sys.user.initPassword").then((response) => {
      this.initPassword = response.msg;
    });
  },
  activated() {
    this.getList(1);
  },
  methods: {
    /** æŸ¥è¯¢éšè®¿æœåŠ¡åˆ—è¡¨ */
    getList(refresh) {
      // é»˜è®¤å…¨éƒ¨
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.visitDeptCodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
        console.log(this.topqueryParams.leavehospitaldistrictcodes, "11");
      }
      this.loading = true;
      if (
        this.topqueryParams.leavehospitaldistrictcodes[0] &&
        this.topqueryParams.visitDeptCodes[0]&&this.topqueryParams.leaveldeptcodes[0]
      ) {
        this.topqueryParams.deptOrDistrict = 2;
      } else {
        this.topqueryParams.deptOrDistrict = 1;
      }
      getTaskservelist(this.topqueryParams).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          // this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[1].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[2].value = response.rows[0].fssb;
          this.cardlist[3].value = response.rows[0].dsf;
          // this.cardlist[4].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    affiliation() {
      this.topqueryParams.managementDoctorCode= store.getters.hisUserId;
      this.getList(1);
    },
    onthatday() {
      this.topqueryParams.startSendDateTime = this.getCurrentDate();
      this.topqueryParams.endSendDateTime = this.getCurrentDate();
      this.getList(1);
    },
    getCurrentDate() {
      const now = new Date();
      return now.toISOString().slice(0, 10); // æˆªå–前10个字符,即 YYYY-MM-DD
    },
    buidegetTasklist(type) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.visitDeptCodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
          this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
      }
      let obj = {
        pageNum: 1,
        pageSize: 10,
        leavehospitaldistrictcodes:
          this.topqueryParams.leavehospitaldistrictcodes,
        sendstates: [2, 3],
        visitDeptCodes: this.topqueryParams.visitDeptCodes,
        leaveldeptcodes: this.topqueryParams.leaveldeptcodes,
      };
      buidegetTasklist(obj).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[2].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[3].value = response.rows[0].fssb;
          this.cardlist[4].value = response.rows[0].dsf;
          // this.cardlist[5].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    // æŸ¥çœ‹é—¨è¯Šéšè®¿è¯¦æƒ…
    Referencequestion(row) {
      this.previewVisible = true;
    },
    // æ·»åŠ å¼¹æ¡†æœç´¢
    remoteMethod(query) {
      if (query !== "") {
        this.loading = true;
        setTimeout(() => {
          this.loading = false;
          this.options = this.list.filter((item) => {
            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
          });
        }, 200);
      } else {
        this.options = [];
      }
    },
    // å½±åƒéšè®¿çŠ¶æ€ä¿®æ”¹
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
      this.$modal
        .confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
        .then(function () {
          return changeUserStatus(row.userId, row.status);
        })
        .then(() => {
          this.$modal.msgSuccess(text + "成功");
        })
        .catch(function () {
          row.status = row.status === "0" ? "1" : "0";
        });
    },
    // è¡¨å•重置
    reset() {
      this.form = {
        userId: undefined,
        deptId: undefined,
        userName: undefined,
        nickName: undefined,
        password: undefined,
        phonenumber: undefined,
        email: undefined,
        sex: undefined,
        status: "0",
        remark: undefined,
        postIds: [],
        roleIds: [],
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery(refresh) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.visitDeptCodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      this.topqueryParams.pageNum = 1;
      this.topqueryParams.startOutHospTime = this.dateRange[0];
      this.topqueryParams.endOutHospTime = this.dateRange[1];
      this.topqueryParams.startSendDateTime = this.dateRangefs[0];
      this.topqueryParams.endSendDateTime = this.dateRangefs[1];
      console.log("2");
      this.getList(refresh);
    },
    // æ‚£è€…范围处理
    handleChange(value) {
      let type = value[0];
      let code = value.slice(-1)[0];
      this.topqueryParams.leavehospitaldistrictcodes = [];
      this.topqueryParams.visitDeptCodes = [];
      this.topqueryParams.leaveldeptcodes = [];
      if (type == 1) {
        this.topqueryParams.visitDeptCodes.push(code);
        this.topqueryParams.leaveldeptcodes.push(code);
        this.topqueryParams.leavehospitaldistrictcodes = [];
        this.topqueryParams.searchscope = 1;
      } else if (type == 2) {
        this.topqueryParams.leavehospitaldistrictcodes.push(code);
        this.topqueryParams.visitDeptCodes = [];
        this.topqueryParams.leaveldeptcodes = [];
        this.topqueryParams.searchscope = 2;
      } else {
        this.topqueryParams.searchscope = 3;
      }
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.dateRange = [];
      this.dateRangefs = [];
      this.topqueryParams = {
        pageNum: 1,
        pageSize: 10,
        sendstate: 2,
        sort: 2, //0 é—¨è¯Šæ—¶é—´(正序)    1 é—¨è¯Šæ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        serviceType: 3,
        searchscope: 3,
        visitCount: 2,
        scopetype: [],
        visitDeptCodes: [],
        leaveldeptcodes:[],
        leavehospitaldistrictcodes: [],
      };
      this.handleQuery(1);
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    handleSelectionChange(selection) {
      this.ids = selection.map((item) => item.userId);
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    },
    //删除选项
    handleClose(tag) {
      this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1);
    },
    //触发新增输入
    showInput() {
      this.inputVisible = true;
      this.$nextTick((_) => {
        this.$refs.saveTagInput.$refs.input.focus();
      });
    },
    //获取失去焦点触发
    handleInputConfirm() {
      let inputValue = this.inputValue;
      if (inputValue) {
        this.dynamicTags.push(inputValue);
      }
      this.inputVisible = false;
      this.inputValue = "";
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
      this.$router.push({
        path: "/followvisit/QuestionnaireTask",
        query: {
          type: 2,
          serviceType: 3,
        },
      });
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
          // this.linkUrl = res.data.url;
        } else {
          this.$modal.msgWarning("360查询无结果");
        }
      });
    },
    /** é‡ç½®å¯†ç æŒ‰é’®æ“ä½œ */
    handleResetPwd(row) {
      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        closeOnClickModal: false,
        inputPattern: /^.{5,20}$/,
        inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
      })
        .then(({ value }) => {
          resetUserPwd(row.userId, value).then((response) => {
            this.$modal.msgSuccess("修改成功,新密码是:" + value);
          });
        })
        .catch(() => {});
    },
    // å–消按钮
    cancel() {
      this.Labelchange = false;
      this.reset();
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function () {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          this.form.isoperation = 2;
          this.form.notrequiredFlag = 1;
          alterpatient(this.form)
            .then((response) => {
              console.log(response);
            })
              .then(() => {
              this.getList(1);
              this.$modal.msgSuccess("患者过滤成功");
            });
          this.reset();
          this.Labelchange = false;
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const userIds = row.userId || this.ids;
      this.$modal
        .confirm('是否确认删除用户编号为"' + userIds + '"的数据项?')
        .then(function () {
          return delUser(userIds);
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨åœæ­¢
    AllStop() {
      this.$modal
        .confirm("是否停止全部任务?")
        .then(function () {
          return console.log("停止成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgWarning("停止成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨å¼€å§‹
    AllStarted() {
      this.$modal
        .confirm("是否开启全部任务?")
        .then(function () {
          return console.log("开启成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("开启成功");
        })
        .catch(() => {});
    },
    // ä»»åŠ¡é‡ç½®
    TaskReset() {
      this.$modal
        .confirm("是否重置选中的任务项?")
        .then(function () {
          return console.log("选中成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("重置成功");
        })
        .catch(() => {});
    },
    // è®¾ç½®å‘送时间
    Sendtimesetting() {
      this.modificationVisible = true;
    },
    // è·³è½¬è¯¦æƒ…页
    Seedetails(row) {
    let type = "";
      console.log(row, "rwo");
        if (row.type == 1) {
          type = 1;
        }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
          taskid: row.taskid,
          patid: row.patid,
          id: row.id,
          Voicetype: type,
          visitCount: this.topqueryParams.visitCount,
        },
      });
    },
    // å†æ¬¡éšè®¿
    followupvisit(row) {
      this.zcform = row;
      this.zcform.endtime = this.formatTime(this.zcform.endtime);
      this.dialogFormVisible = true;
    },
    onSubmit() {},
    // æš‚停服务
    handlestop(row) {
      let objson = row;
      this.$modal
        .confirm(
          '是否确认暂停任务名称为"' +
            row.taskName +
            '患者名称为"' +
            row.sendname +
            '"的数据项?'
        )
        .then(() => {
          getTaskservelist({
            patid: row.patid,
            taskid: row.taskid,
          }).then((res) => {
            if (res.code == 200) {
              objson.sendstate = 4;
              objson.remark = "服务暂停";
              Editsingletaskson(objson).then((res) => {
                if (res.code) {
                  this.$modal.msgSuccess("记录成功");
                  this.getList(1);
                }
              });
            }
          });
        })
        .catch(() => {});
    },
    // æ‚£è€…过滤触发
    handleUpdate(row) {
      particularpatient(row.patid).then((response) => {
        this.form = response.data;
        this.form.filterDrname = store.getters.nickName;
      });
      this.amendtag = true;
      this.Labelchange = true;
    },
    // ä¾¿æ·æŒ‰é’®
    toleadExport(too) {
      if (too == 1) {
        this.topqueryParams.sendstate = 4;
        this.topqueryParams.excep = null;
      } else if (too == 2) {
        this.topqueryParams.excep = 1;
      }
      this.handleQuery();
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      this.topqueryParams.pageNum = null;
      this.topqueryParams.pageSize = null;
      this.download(
        "smartor/serviceSubtask/patItemExport",
        {
          ...this.topqueryParams,
        },
        `user_${new Date().getTime()}.xlsx`
      );
    },
    // å¼‚常列渲染
    tableRowClassName({ row, rowIndex }) {
      if (row.excep == 1) {
        return "warning-row";
      }
      return "";
    },
    // åˆ›å»ºå†æ¬¡éšè®¿æœåŠ¡
    setupsubtask() {
      this.$refs["zcform"].validate((valid) => {
        if (valid) {
          this.zcform.remark =
            this.zcform.remark + "【" + this.getCurrentTime() + "】";
          let form = structuredClone(this.zcform);
          form.longSendTime = this.formatTime(form.date1);
          form.finishtime = "";
          if (form.resource) {
            if (form.resource == 2) {
              form.serviceType = 13;
            }
          } else {
            this.$modal.msgError("未选择随访方式");
          }
          form.id = null;
          form.sendstate = 2;
          form.preachform = form.preachformson;
          form.longTask = 0;
          addserviceSubtask(form).then((res) => {
            if (res.code == 200) {
              this.$modal.msgSuccess("创建成功");
            } else {
              this.$modal.msgError("创建失败");
            }
            this.dialogFormVisible = false;
          });
        }
      });
    },
    getCurrentTime() {
      const now = new Date();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, "0");
      const day = String(now.getDate()).padStart(2, "0");
      const hours = String(now.getHours()).padStart(2, "0");
      const minutes = String(now.getMinutes()).padStart(2, "0");
      const seconds = String(now.getSeconds()).padStart(2, "0");
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
  },
};
</script>
<style lang="scss" scoped>
.el-button--primary.is-plain {
  color: #ffffff;
  background: #409eff;
  border-color: #4fabe9;
}
.document {
  // width: 100px;
  height: 50px;
}
::v-deep.el-table .warning-row {
  background: #eec4c4;
}
.documentf {
  display: flex;
  justify-content: flex-end;
}
.download {
  text-align: center;
  .el-upload__tip {
    font-size: 23px;
  }
  .el-upload__text {
    font-size: 23px;
  }
}
.uploading {
  margin-top: 20px;
  margin: 20px;
  padding: 30px;
  background: #ffffff;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
}
.el-tag + .el-tag {
  margin-left: 10px;
}
.button-new-tag {
  margin-left: 10px;
  height: 32px;
  line-height: 30px;
  padding-top: 0;
  padding-bottom: 0;
}
.input-new-tag {
  width: 90px;
  margin-left: 10px;
  vertical-align: bottom;
}
.drexamine {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  background: #daeaf5;
  img {
    width: 100px;
    height: 100px;
  }
}
.qrcode-dialo {
  // text-align: center;
  //   display: flex;
  margin: 20px;
  padding: 30px;
  background: #edf1f7;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
  .topic-dev {
    margin-bottom: 25px;
    font-size: 20px !important;
    .dev-text {
      margin-bottom: 10px;
    }
  }
}
::v-deep.leftvlue .el-card__body {
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
  background: #fdd0d7;
}
::v-deep.errleftvlue .el-card__body:hover {
  background: #f88d96;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.ysfleftvlue .el-card__body {
  background: #d0fdd8;
}
::v-deep.ysfleftvlue .el-card__body:hover {
  background: #8df8a4;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-bb {
  font-weight: 500;
  background-color: #2ba05c;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-xq {
  font-weight: 500;
  background-color: #409eff;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-sc {
  font-weight: 500;
  background-color: #b3a21f;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-zx {
  background: #4fabe9;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-textxga {
  color: #de7897;
}
::v-deep.el-radio-group {
  span {
    font-size: 24px;
  }
}
// é€‰é¡¹å­—体放大
// ::v-deep.el-checkbox-group {
//   span {
//     font-size: 24px;
//   }
// }
</style>
src/views/followvisit/SpecificDisease/index.vue
@@ -138,8 +138,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -791,8 +790,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -826,7 +825,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
@@ -1017,11 +1016,10 @@
    },
     //患者360跳转
     gettoken360(sfzh,drcode,drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC=='丽水市中医院') {
        this.postData.YeWuXX.YongHuXX.YongHuID = '1400398571877961728';
        this.postData.YeWuXX.YongHuXX.YongHuXM = 'LSZYY';
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
           window.open(res.data.url, '_blank');
@@ -1266,14 +1264,11 @@
    },
    // è·³è½¬è¯¦æƒ…页
    Seedetails(row) {
      let type = "";
     let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
          console.log(type, "rwo");
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1445,10 +1440,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
src/views/followvisit/again/index.vue
@@ -180,8 +180,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -246,7 +245,7 @@
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >当日服务</el-button
                >今日服务</el-button
              >
            </div>
          </div>
@@ -397,13 +396,7 @@
          key="drname"
          prop="drname"
        />
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="出院天数"
          width="120"
@@ -483,6 +476,13 @@
          :show-overflow-tooltip="true"
        >
        </el-table-column>
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="出院随访模板名称"
@@ -928,7 +928,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
        {
@@ -983,13 +983,21 @@
          value: 3,
          label: "发送时间(倒序)",
        },
        {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: 2,
        sort: 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 2,
        searchscope: 3,
        visitCount: 2,
@@ -1077,8 +1085,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -1197,7 +1205,6 @@
      });
    },
    affiliation() {
      this.topqueryParams.managementDoctorCode= store.getters.hisUserId;
      this.getList(1);
@@ -1427,11 +1434,10 @@
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1473,7 +1479,7 @@
            .then((response) => {
              console.log(response);
            })
            .then(() => {
              .then(() => {
              this.getList(1);
              this.$modal.msgSuccess("患者过滤成功");
            });
@@ -1542,13 +1548,11 @@
    },
    // è·³è½¬è¯¦æƒ…页
    Seedetails(row) {
      let type = "";
    let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1774,10 +1778,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
@@ -1822,7 +1828,9 @@
  border-radius: 1px;
  color: #ffffff;
}
.button-textxga {
  color: #de7897;
}
::v-deep.el-radio-group {
  span {
    font-size: 24px;
src/views/followvisit/complaint/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1963 @@
<template>
  <div class="app-container">
    <div class="leftvlue" style="margin-bottom: 20px">
      <el-row :gutter="10">
        <el-col :span="2.5" v-for="(item, index) in cardlist" :key="index">
          <el-card
            shadow="hover"
            :body-style="item.router ? ' cursor: pointer' : 'cursor: default'"
          >
            <div style="padding: 8px" @click="$router.push(item.router)">
              <span>{{ item.name }}</span>
              <div
                style="
                  text-align: center;
                  font-size: 18px;
                  margin-top: 10px;
                  font-weight: 600;
                "
              >
                {{ item.value ? item.value : 0 }}
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="2.5">
          <div class="ysfleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>表单已发送</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ yfsvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
        <el-col :span="2.5">
          <div class="errleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>异常</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ ycvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
      </el-row>
    </div>
    <el-row :gutter="20">
      <!--用户数据-->
      <el-form
        :model="topqueryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        v-show="showSearch"
        label-width="98px"
      >
        <el-form-item label="任务名称">
          <el-input
            v-model="topqueryParams.taskName"
            placeholder="请选择任务名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="出院时间">
          <el-date-picker
            v-model="dateRange"
            style="width: 240px"
            value-format="yyyy-MM-dd"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="患者姓名" prop="sendname">
          <el-input
            v-model="topqueryParams.sendname"
            placeholder="请输入患者姓名"
          ></el-input>
        </el-form-item>
        <el-form-item label="诊断名称" prop="leavediagname">
          <el-input
            v-model="topqueryParams.leavediagname"
            placeholder="请输入诊断名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访人员" prop="updateBy">
          <el-input
            v-model="topqueryParams.updateBy"
            placeholder="请输入随访人员"
          ></el-input>
        </el-form-item>
        <el-form-item label="主治医生" prop="drname">
          <el-input
            v-model="topqueryParams.drname"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="经管医生" prop="managementDoctor">
          <el-input
            v-model="topqueryParams.managementDoctor"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="患者范围" prop="status">
          <el-cascader
            v-model="topqueryParams.scopetype"
            placeholder="默认全部"
            :options="sourcetype"
            :props="{ expandTrigger: 'hover' }"
            @change="handleChange"
          ></el-cascader>
        </el-form-item>
        <el-form-item label="任务状态" prop="status">
          <el-select v-model="topqueryParams.sendstate" placeholder="请选择">
            <el-option
              v-for="item in topicoptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="排序方式" prop="status">
          <el-select v-model="topqueryParams.sort" placeholder="请选择">
            <el-option
              v-for="item in topicoptionssort"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="medium"
            @click="handleQuery(1)"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
      <el-divider></el-divider>
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-upload2"
                size="medium"
                @click="handleExport"
                >导出</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="primary"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
          >
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-warning-outline"
                size="medium"
                @click="toleadExport(1)"
                >执行失败</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="danger"
                plain
                icon="el-icon-warning"
                size="medium"
                @click="toleadExport(2)"
                >结果异常</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="success"
                plain
                size="medium"
                @click="buidegetTasklist()"
                >待办服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="primary"
                plain
                size="medium"
                @click="affiliation()"
                >本人所属服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >今日服务</el-button
              >
            </div>
          </div>
        </el-col>
      </el-row>
      <el-table
        v-loading="loading"
        ref="userform"
        :data="userList"
        :row-class-name="tableRowClassName"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column
          label="任务名称"
          fixed
          width="150"
          show-overflow-tooltip
          align="center"
          key="taskName"
          prop="taskName"
        />
        <!-- <el-table-column label="序号" fixed align="center" key="id" prop="id" /> -->
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          key="sendname"
          prop="sendname"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                gettoken360(scope.row.sfzh, scope.row.drcode, scope.row.drname)
              "
              ><span class="button-textsc">{{
                scope.row.sendname
              }}</span></el-button
            >
          </template>
        </el-table-column>
        <el-table-column
          label="任务状态"
          align="center"
          key="sendstate"
          prop="sendstate"
          width="120"
        >
          <template slot-scope="scope">
            <el-tooltip
              class="item"
              effect="dark"
              :content="scope.row.remark"
              placement="top-start"
            >
              <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>
            </el-tooltip>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务异常说明"
          width="120"
          align="center"
          key="remark"
          prop="remark" -->
        />
        <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
          label="随访完成时间"
          sortable
          align="center"
          prop="finishtime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.finishtime) }}</span>
          </template>
        </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="drname"
          prop="drname"
        />
        <el-table-column
          label="经管医生"
          align="center"
          key="managementDoctor"
          prop="managementDoctor"
          width="120"
        />
        <el-table-column
          label="出院天数"
          width="120"
          align="center"
          key="endDay"
          prop="endDay"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.endDay ? scope.row.endDay + "天" : "" }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="身份证号码"
          width="200"
          align="center"
          key="sfzh"
          prop="sfzh"
        />
        <el-table-column
          label="联系电话"
          width="200"
          align="center"
          key="phone"
          prop="phone"
        />
        <el-table-column
          label="责任护士"
          width="120"
          align="center"
          key="nurseName"
          prop="nurseName"
        />
        <!-- <el-table-column
          label="病历号"
          align="center"
          sortable
          key="medicalRecordNo"
          prop="medicalRecordNo"
          width="120"
        /> -->
        <!-- <el-table-column label="年龄" align="center" key="age" prop="age" /> -->
        <!-- <el-table-column label="性别"width="100" align="center" key="sex" prop="sex" /> -->
        <!-- <el-table-column label="床号" align="center" key="badNo" prop="badNo" /> -->
        <el-table-column
          label="科室"
          align="center"
          key="deptname"
          prop="deptname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="病区"
          align="center"
          key="leavehospitaldistrictname"
          prop="leavehospitaldistrictname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="诊断名称"
          align="center"
          key="leavediagname"
          prop="leavediagname"
          width="120"
          :show-overflow-tooltip="true"
        >
        </el-table-column>
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="投诉建议模板名称"
          align="center"
          key="templatename"
          prop="templatename"
          width="200"
        />
        <el-table-column
          label="任务执行方式"
          align="center"
          key="preachform"
          prop="preachform"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.preachform">{{ item }}、 </span>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务发送流程"
          align="center"
          key="serviceSubtaskRecordList"
          prop="serviceSubtaskRecordList"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.serviceSubtaskRecordList"
              >{{ item.remark }}、
            </span>
          </template>
        </el-table-column> -->
        <el-table-column
          label="任务结果说明"
          width="220"
          align="center"
          key="remark"
          prop="remark"
        >
          <template slot-scope="scope" v-if="scope.row.remark">
            <el-tooltip
              :content="scope.row.remark"
              placement="top"
              effect="dark"
            >
              <el-tag
                type="warning"
                v-if="scope.row.sendstate != 5 && scope.row.sendstate != 4"
                >{{ scope.row.remark }}</el-tag
              >
              <el-tag type="warning" v-else>{{ scope.row.remark }}</el-tag>
            </el-tooltip>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
          fixed="right"
          width="300"
          class-name="small-padding fixed-width"
        >
          <template slot-scope="scope">
            <!-- <el-tooltip
              class="item"
              effect="dark"
              content="再次随访"
              placement="top"
            >
              <el-button
                size="medium"
                type="text"
                v-if="scope.row.isVisitAgain!=2"
                @click="followupvisit(scope.row)"
                ><span class="button-bb"
                  ><i class="el-icon-s-promotion"></i>再次随访</span
                ></el-button
              >
            </el-tooltip>
            <el-tooltip
              v-if="scope.row.sendstate == 1 || scope.row.sendstate == 2"
              class="item"
              effect="dark"
              content="暂停服务"
              placement="top"
            >
              <el-button
                size="medium"
                type="text"
                @click="handlestop(scope.row)"
                v-hasPermi="['system:user:edit']"
                ><span class="button-sc"
                  ><i class="el-icon-remove-outline"></i>暂停服务</span
                ></el-button
              >
            </el-tooltip> -->
            <el-button size="medium" type="text" @click="Seedetails(scope.row)"
              ><span class="button-zx"
                ><i class="el-icon-s-order"></i>查看详情</span
              ></el-button
            >
            <el-button
              size="medium"
              type="text"
              @click="handleUpdate(scope.row)"
              ><span class="button-textxga"
                ><i class="el-icon-edit"></i>患者过滤</span
              ></el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="topqueryParams.pageNum"
        :limit.sync="topqueryParams.pageSize"
        @pagination="getList"
      />
    </el-row>
    <!-- æ»¡æ„åº¦å¼¹æ¡† -->
    <el-dialog
      title="随访满意度评分"
      :visible.sync="scoreDialogVisible"
      width="80%"
      :close-on-click-modal="false"
    >
      <el-table :data="selectedRows" border style="width: 100%">
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          prop="sendname"
        />
        <el-table-column
          label="任务名称"
          width="180"
          align="center"
          prop="taskName"
        />
        <!-- æ–°å¢žè¯„分列 -->
        <el-table-column
          label="真实性(20)"
          align="center"
          key="authenticity"
          prop="authenticity"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.authenticity"
              :min="0"
              :max="20"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="一周内完成(20)"
          align="center"
          key="weekFinish"
          prop="weekFinish"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.weekFinish"
              :min="0"
              :max="20"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="规范性(10)"
          align="center"
          key="standard"
          prop="standard"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.standard"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="及时性(10)"
          align="center"
          key="timeliness"
          prop="timeliness"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.timeliness"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="宣教情况(10)"
          align="center"
          key="library"
          prop="library"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.library"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="环境满意度(10)"
          align="center"
          key="environment"
          prop="environment"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.environment"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="医生满意度(10)"
          align="center"
          key="doctorSatisfaction"
          prop="doctorSatisfaction"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.doctorSatisfaction"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="护士满意度(10)"
          align="center"
          key="nurseSatisfaction"
          prop="nurseSatisfaction"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.nurseSatisfaction"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="总分"
          align="center"
          key="total"
          prop="total"
          fixed="right"
        >
          <template slot-scope="scope">
            <span>{{ calculateTotal(scope.row) }}</span>
          </template>
        </el-table-column>
      </el-table>
      <div slot="footer" class="dialog-footer">
        <el-button @click="scoreDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveScores">保存</el-button>
      </div>
    </el-dialog>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹å½±åƒéšè®¿å¯¹è¯æ¡† -->
    <el-dialog
      :title="amendtag ? '修改患者信息' : '新增患者'"
      :visible.sync="Labelchange"
      width="900px"
    >
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="8">
            <el-form-item label="姓名" width="100" prop="name">
              <el-input
                v-model="form.name"
                placeholder="请输入姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="性别" width="100" prop="sex">
              <el-select v-model="form.sex" placeholder="请选择性别">
                <el-option
                  v-for="dict in sextype"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="年龄" prop="age">
              <el-input
                v-model="form.age"
                placeholder="请输入年龄"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row >
          <el-col :span="8">
            <el-form-item label="过滤医生" width="100" prop="filterDrname">
              <el-input
                v-model="form.filterDrname"
                placeholder="请输入医生姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="过滤原因">
              <el-input
                v-model="form.notrequiredreason"
                type="textarea"
                placeholder="请输入过滤原因"
              ></el-input>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
        <el-button @click="cancel">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
    <!-- ä¿®æ”¹å‘送时间对话框 -->
    <el-dialog
      title="发送时间设置"
      :visible.sync="modificationVisible"
      width="45%"
    >
      <div style="margin-bottom: 20px; color: red">
        ç»Ÿä¸€ä¿®æ”¹å½“天未发送的任务时间
      </div>
      <el-form
        :model="ruleForm"
        :rules="rules"
        ref="ruleForm"
        label-width="120px"
        class="demo-ruleForm"
      >
        <el-form-item label="发送日期">
          <el-date-picker
            v-model="ruleForm.value1"
            type="date"
            placeholder="选择日期"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="时间段" prop="type">
          <el-checkbox-group v-model="ruleForm.type">
            <el-checkbox label="上午" name="type"></el-checkbox>
            <el-checkbox label="下午" name="type"></el-checkbox>
            <el-checkbox label="晚上" name="type"></el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="上午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value2"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="下午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value3"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="晚上时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value4"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="modificationVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="modificationVisible = false"
          >ç¡® å®š</el-button
        >
      </span>
    </el-dialog>
    <!-- å†æ¬¡éšè®¿ -->
  </div>
</template>
<script>
import {
  delUser,
  addUser,
  updateUser,
  resetUserPwd,
  changeUserStatus,
} from "@/api/system/user";
import {
  getTaskservelist,
  buidegetTasklist,
  addserviceSubtask,
  query360PatInfo,
  addsatisfaction,
} from "@/api/AiCentre/index";
import { alterpatient, particularpatient } from "@/api/patient/homepage";
import Treeselect from "@riophae/vue-treeselect";
import store from "@/store";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
  name: "Discharge",
  dicts: ["sys_normal_disable", "sys_user_sex", "sys_yujing", "sys_suggest"],
  components: { Treeselect },
  data() {
    return {
      // é®ç½©å±‚
      loading: true,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // æ€»æ¡æ•°
      total: 0,
      // ç”¨æˆ·è¡¨æ ¼æ•°æ®
      userList: null,
      // å¼¹å‡ºå±‚标题
      title: "新增影像随访",
      // æ˜¯å¦æ˜¾ç¤ºä¿®æ”¹ã€æ·»åŠ å¼¹å‡ºå±‚
      addalteropen: false,
      // ä¿®æ”¹å‘送时间对话框
      modificationVisible: false,
      // éƒ¨é—¨åç§°
      deptName: undefined,
      // é»˜è®¤å¯†ç 
      initPassword: undefined,
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      dateRangefs: [],
      // å²—位选项
      postOptions: [],
      ruleForm: {
        type: [],
      },
      zcform: {},
      dynamicTags: ["选项一", "选项二", "选项三"], //选项
      inputVisible: false,
      Labelchange: false,
      ycvalue: "",
      yfsvalue: "",
      inputValue: "",
      preachform: "",
      previewVisible: false, //影像随访预览弹框
      radio: "",
      radios: [],
      previewtype: 2, //预览影像随访类型
      total: 0, // æ€»æ¡æ•°
      // æ»¡æ„åº¦è°ƒæŸ¥æ•°æ®
      scoreDialogVisible: false,
      selectedRows: [],
      value: [],
      list: [],
      sourcetype: [
        {
          value: 1,
          label: "科室",
          children: [],
        },
        {
          value: 2,
          label: "病区",
          children: [],
        },
        {
          value: 3,
          label: "全部",
        },
      ],
      loading: false,
      cardlist: [
        {
          name: "服务总量",
          value: 0,
        },
        // {
        //   name: "患者过滤",
        //   value: 0,
        // },
        {
          name: "需随访",
          value: 0,
        },
        {
          name: "发送失败",
          value: 0,
        },
        {
          name: "待随访",
          value: 0,
        },
        // {
        //   name: "已发送",
        //   value: 0,
        // },
        // {
        //   name: "表单已发送",
        //   value: 0,
        // },
      ],
      zcrules: {
        date1: [
          { required: true, message: "请选择随访方式", trigger: "change" },
        ],
        resource: [
          { required: true, message: "请选择随访时间", trigger: "blur" },
        ],
      },
      // è¡¨å•参数
      form: {
        phonenumber: "",
        totagid: "",
        types: "",
        nickName: "",
        qystatus: "",
        btstatus: "",
      },
      topicoptionssort: [
        {
          value: 0,
          label: "出院时间(正序)",
        },
        {
          value: 1,
          label: "出院时间(倒序)",
        },
         {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: 6,
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 15,
        searchscope: 3,
        visitCount: 1,
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      },
      propss: { multiple: true },
      options: [],
      topicoptions: [
        {
          value: null,
          label: "全部",
        },
        {
          value: 1,
          label: "表单已领取",
        },
        {
          value: 2,
          label: "待随访",
        },
        {
          value: 3,
          label: "表单已发送",
        },
        {
          value: 4,
          label: "不执行",
        },
        {
          value: 5,
          label: "发送失败",
        },
        {
          value: 6,
          label: "已完成",
        },
      ],
      sextype: [
        {
          value: 1,
          label: "男",
        },
        {
          value: 2,
          label: "女",
        },
      ],
      topicoptionsyj: [
        {
          value: 1,
          label: "异常",
        },
        {
          value: 0,
          label: "正常",
        },
      ],
      url: "http://9.208.2.190:8090/smartor/serviceExternal/query360PatInfo",
      postData: {
        XiaoXiTou: {
          FaSongFCSJC: "ZJHES",
          FaSongJGID: localStorage.getItem("orgid"),
          FaSongJGMC: localStorage.getItem("orgname"),
          FaSongSJ: "2025-01-09 17:29:36",
          FaSongXTJC: "SUIFANGXT",
          FaSongXTMC: "随访系统",
          XiaoXiID: "5FA92AFB-9833-4608-87C7-F56A654AC171",
          XiaoXiLX: "SC_LC_360STCX",
          XiaoXiMC: "360 视图查询",
          ZuHuID: localStorage.getItem("ZuHuID"),
          ZuHuMC: localStorage.getItem("orgname"),
        },
        YeWuXX: {
          BingRenXX: {
            ZhengJianHM: "",
            ZhengJianLXDM: "01",
            ZhengJianLXMC: "居民身份证",
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
          },
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
          },
        },
      },
      amendtag: false,
      errtype: "",
      leavehospitaldistrictcode: "",
      serviceState: [],
      checkboxlist: [],
      // è¡¨å•校验
      rules: {},
    };
  },
  watch: {},
  created() {
    this.serviceState = store.getters.serviceState;
    this.checkboxlist = store.getters.checkboxlist;
    this.errtype = this.$route.query.errtype;
    this.leavehospitaldistrictcode =
      this.$route.query.leavehospitaldistrictcode;
    this.sourcetype[0].children = store.getters.belongDepts.map((dept) => {
      return {
        label: dept.deptName,
        value: dept.deptCode,
      };
    });
    this.sourcetype[1].children = store.getters.belongWards.map((dept) => {
      return {
        label: dept.districtName,
        value: dept.districtCode,
      };
    });
    if (this.errtype) {
      this.toleadExport(2);
    } else {
      this.getList(1);
    }
    this.getConfigKey("sys.user.initPassword").then((response) => {
      this.initPassword = response.msg;
    });
  },
  activated() {
    this.getList(1);
  },
  methods: {
    /** æŸ¥è¯¢éšè®¿æœåŠ¡åˆ—è¡¨ */
    getList(refresh) {
      // é»˜è®¤å…¨éƒ¨
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
        console.log(this.topqueryParams.leavehospitaldistrictcodes, "11");
      }
      this.loading = true;
      if (
        this.topqueryParams.leavehospitaldistrictcodes[0] &&
        this.topqueryParams.leaveldeptcodes[0]
      ) {
        this.topqueryParams.deptOrDistrict = 2;
      } else {
        this.topqueryParams.deptOrDistrict = 1;
      }
      getTaskservelist(this.topqueryParams).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          // this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[1].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[2].value = response.rows[0].fssb;
          this.cardlist[3].value = response.rows[0].dsf;
          // this.cardlist[4].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    affiliation() {
            this.topqueryParams.managementDoctorCode= store.getters.hisUserId;
      this.getList(1);
    },
    onthatday() {
      this.topqueryParams.startSendDateTime = this.getCurrentDate();
      this.topqueryParams.endSendDateTime = this.getCurrentDate();
      this.getList(1);
    },
    getCurrentDate() {
      const now = new Date();
      return now.toISOString().slice(0, 10); // æˆªå–前10个字符,即 YYYY-MM-DD
    },
    buidegetTasklist(type) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
      }
      let obj = {
        pageNum: 1,
        pageSize: 10,
        leavehospitaldistrictcodes:
          this.topqueryParams.leavehospitaldistrictcodes,
        sendstates: [2, 3],
        leaveldeptcodes: this.topqueryParams.leaveldeptcodes,
      };
      buidegetTasklist(obj).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[2].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[3].value = response.rows[0].fssb;
          this.cardlist[4].value = response.rows[0].dsf;
          // this.cardlist[5].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    // æŸ¥çœ‹é—¨è¯Šéšè®¿è¯¦æƒ…
    Referencequestion(row) {
      this.previewVisible = true;
    },
    // æ·»åŠ å¼¹æ¡†æœç´¢
    remoteMethod(query) {
      if (query !== "") {
        this.loading = true;
        setTimeout(() => {
          this.loading = false;
          this.options = this.list.filter((item) => {
            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
          });
        }, 200);
      } else {
        this.options = [];
      }
    },
    // å½±åƒéšè®¿çŠ¶æ€ä¿®æ”¹
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
      this.$modal
        .confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
        .then(function () {
          return changeUserStatus(row.userId, row.status);
        })
        .then(() => {
          this.$modal.msgSuccess(text + "成功");
        })
        .catch(function () {
          row.status = row.status === "0" ? "1" : "0";
        });
    },
    // è¡¨å•重置
    reset() {
      this.form = {
        userId: undefined,
        deptId: undefined,
        userName: undefined,
        nickName: undefined,
        password: undefined,
        phonenumber: undefined,
        email: undefined,
        sex: undefined,
        status: "0",
        remark: undefined,
        postIds: [],
        roleIds: [],
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery(refresh) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      this.topqueryParams.pageNum = 1;
      this.topqueryParams.startOutHospTime = this.dateRange[0];
      this.topqueryParams.endOutHospTime = this.dateRange[1];
      this.topqueryParams.startSendDateTime = this.dateRangefs[0];
      this.topqueryParams.endSendDateTime = this.dateRangefs[1];
      this.getList(refresh);
    },
    // æ‚£è€…范围处理
    handleChange(value) {
      let type = value[0];
      let code = value.slice(-1)[0];
      this.topqueryParams.leavehospitaldistrictcodes = [];
      this.topqueryParams.leaveldeptcodes = [];
      if (type == 1) {
        this.topqueryParams.leaveldeptcodes.push(code);
        this.topqueryParams.leavehospitaldistrictcodes = [];
        this.topqueryParams.searchscope = 1;
      } else if (type == 2) {
        this.topqueryParams.leavehospitaldistrictcodes.push(code);
        this.topqueryParams.leaveldeptcodes = [];
        this.topqueryParams.searchscope = 2;
      } else {
        this.topqueryParams.searchscope = 3;
      }
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.dateRange = [];
      this.dateRangefs = [];
      this.topqueryParams = {
        pageNum: 1,
        pageSize: 10,
        sendstate: 6,
        sort: 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        serviceType: 15,
        searchscope: 3,
        visitCount: 1,
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      };
      this.handleQuery(1);
    },
    handleSelectionChange(rows) {
      this.selectedRows = rows.map((row) => {
        // åˆå§‹åŒ–评分字段
        return {
          ...row,
          authenticity: row.authenticity || 0,
          weekFinish: row.weekFinish || 0,
          standard: row.standard || 0,
          timeliness: row.timeliness || 0,
          library: row.library || 0,
          environment: row.environment || 0,
          doctorSatisfaction: row.doctorSatisfaction || 0,
          nurseSatisfaction: row.nurseSatisfaction || 0,
        };
      });
      if (this.selectedRows.length > 0) {
        this.multiple = false;
      } else {
        this.multiple = true;
      }
    },
    // è®¡ç®—总分
    calculateTotal(row) {
      return (
        (row.authenticity || 0) +
        (row.weekFinish || 0) +
        (row.standard || 0) +
        (row.timeliness || 0) +
        (row.library || 0) +
        (row.environment || 0) +
        (row.doctorSatisfaction || 0) +
        (row.nurseSatisfaction || 0)
      );
    },
    // ä¿å­˜è¯„分
    saveScores() {
      this.selectedRows.forEach((item) => {
        item.createBy = null;
        item.patName = item.sendname;
        item.hospitaldistrictname = item.leavehospitaldistrictname;
      });
      addsatisfaction(this.selectedRows).then((res) => {
        if (res.code == 200) {
          this.$message.success("评分保存成功");
          this.scoreDialogVisible = false;
          this.selectedRows=[];
          this.$refs.userform.clearSelection()
        } else {
          this.$modal.msgWarning("评分保存失败");
          this.scoreDialogVisible = false;
          this.selectedRows=[];
          this.$refs.userform.clearSelection()
        }
      });
      // è¿™é‡Œå¯ä»¥æ·»åŠ ä¿å­˜é€»è¾‘ï¼Œå¦‚è°ƒç”¨API保存评分
    },
    //删除选项
    handleClose(tag) {
      this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1);
    },
    //触发新增输入
    showInput() {
      this.inputVisible = true;
      this.$nextTick((_) => {
        this.$refs.saveTagInput.$refs.input.focus();
      });
    },
    //获取失去焦点触发
    handleInputConfirm() {
      let inputValue = this.inputValue;
      if (inputValue) {
        this.dynamicTags.push(inputValue);
      }
      this.inputVisible = false;
      this.inputValue = "";
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
      this.$router.push({
        path: "/followvisit/QuestionnaireTask",
        query: {
          type: 2,
          serviceType: 15,
        },
      });
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
          // this.linkUrl = res.data.url;
        } else {
          this.$modal.msgWarning("360查询无结果");
        }
      });
    },
    /** é‡ç½®å¯†ç æŒ‰é’®æ“ä½œ */
    handleResetPwd(row) {
      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        closeOnClickModal: false,
        inputPattern: /^.{5,20}$/,
        inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
      })
        .then(({ value }) => {
          resetUserPwd(row.userId, value).then((response) => {
            this.$modal.msgSuccess("修改成功,新密码是:" + value);
          });
        })
        .catch(() => {});
    },
    // å–消按钮
    cancel() {
      this.Labelchange = false;
      this.reset();
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function () {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          this.form.isoperation = 2;
          this.form.notrequiredFlag = 1;
          alterpatient(this.form)
            .then((response) => {
              console.log(response);
            })
            .then(() => {
              this.getList(1);
              this.$modal.msgSuccess("患者过滤成功");
            });
          this.reset();
          this.Labelchange = false;
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const userIds = row.userId || this.ids;
      this.$modal
        .confirm('是否确认删除用户编号为"' + userIds + '"的数据项?')
        .then(function () {
          return delUser(userIds);
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨åœæ­¢
    AllStop() {
      this.$modal
        .confirm("是否停止全部任务?")
        .then(function () {
          return console.log("停止成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgWarning("停止成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨å¼€å§‹
    AllStarted() {
      this.$modal
        .confirm("是否开启全部任务?")
        .then(function () {
          return console.log("开启成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("开启成功");
        })
        .catch(() => {});
    },
    // ä»»åŠ¡é‡ç½®
    TaskReset() {
      this.$modal
        .confirm("是否重置选中的任务项?")
        .then(function () {
          return console.log("选中成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("重置成功");
        })
        .catch(() => {});
    },
    // è®¾ç½®å‘送时间
    Sendtimesetting() {
      this.modificationVisible = true;
    },
    // è·³è½¬è¯¦æƒ…页
    Seedetails(row) {
   let type = "";
      console.log(row, "rwo");
        if (row.type == 1) {
          type = 1;
        }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
          taskid: row.taskid,
          patid: row.patid,
          id: row.id,
          Voicetype: type,
          visitCount: this.topqueryParams.visitCount,
        },
      });
    },
    onSubmit() {},
    // æš‚停服务
    handlestop(row) {
      let objson = row;
      this.$modal
        .confirm(
          '是否确认暂停任务名称为"' +
            row.taskName +
            '患者名称为"' +
            row.sendname +
            '"的数据项?'
        )
        .then(() => {
          getTaskservelist({
            patid: row.patid,
            taskid: row.taskid,
          }).then((res) => {
            if (res.code == 200) {
              objson.sendstate = 4;
              objson.remark = "服务暂停";
              Editsingletaskson(objson).then((res) => {
                if (res.code) {
                  this.$modal.msgSuccess("记录成功");
                  this.getList(1);
                }
              });
            }
          });
        })
        .catch(() => {});
    },
    // æ‚£è€…过滤触发
    handleUpdate(row) {
      particularpatient(row.patid).then((response) => {
        this.form = response.data;
        this.form.filterDrname = store.getters.nickName;
      });
      this.amendtag = true;
      this.Labelchange = true;
    },
    // ä¾¿æ·æŒ‰é’®
    toleadExport(too) {
      if (too == 1) {
        this.topqueryParams.sendstate = 4;
        this.topqueryParams.excep = null;
      } else if (too == 2) {
        this.topqueryParams.excep = 1;
      }
      this.handleQuery();
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      this.topqueryParams.pageNum = null;
      this.topqueryParams.pageSize = null;
      this.download(
        "smartor/serviceSubtask/patItemExport",
        {
          ...this.topqueryParams,
        },
        `user_${new Date().getTime()}.xlsx`
      );
    },
    // å¼‚常列渲染
    tableRowClassName({ row, rowIndex }) {
      if (row.excep == 1) {
        return "warning-row";
      }
      return "";
    },
    getCurrentTime() {
      const now = new Date();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, "0");
      const day = String(now.getDate()).padStart(2, "0");
      const hours = String(now.getHours()).padStart(2, "0");
      const minutes = String(now.getMinutes()).padStart(2, "0");
      const seconds = String(now.getSeconds()).padStart(2, "0");
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
  },
};
</script>
<style lang="scss" scoped>
.el-button--primary.is-plain {
  color: #ffffff;
  background: #409eff;
  border-color: #4fabe9;
}
.document {
  // width: 100px;
  height: 50px;
}
::v-deep.el-table .warning-row {
  background: #eec4c4;
}
.documentf {
  display: flex;
  justify-content: flex-end;
}
.download {
  text-align: center;
  .el-upload__tip {
    font-size: 23px;
  }
  .el-upload__text {
    font-size: 23px;
  }
}
.uploading {
  margin-top: 20px;
  margin: 20px;
  padding: 30px;
  background: #ffffff;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
}
.el-tag + .el-tag {
  margin-left: 10px;
}
.button-new-tag {
  margin-left: 10px;
  height: 32px;
  line-height: 30px;
  padding-top: 0;
  padding-bottom: 0;
}
.input-new-tag {
  width: 90px;
  margin-left: 10px;
  vertical-align: bottom;
}
.drexamine {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  background: #daeaf5;
  img {
    width: 100px;
    height: 100px;
  }
}
.qrcode-dialo {
  // text-align: center;
  //   display: flex;
  margin: 20px;
  padding: 30px;
  background: #edf1f7;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
  .topic-dev {
    margin-bottom: 25px;
    font-size: 20px !important;
    .dev-text {
      margin-bottom: 10px;
    }
  }
}
::v-deep.leftvlue .el-card__body {
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
  background: #fdd0d7;
}
::v-deep.errleftvlue .el-card__body:hover {
  background: #f88d96;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.ysfleftvlue .el-card__body {
  background: #d0fdd8;
}
::v-deep.ysfleftvlue .el-card__body:hover {
  background: #8df8a4;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-bb {
  font-weight: 500;
  background-color: #2ba05c;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-xq {
  font-weight: 500;
  background-color: #409eff;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-sc {
  font-weight: 500;
  background-color: #b3a21f;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-zx {
  background: #4fabe9;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
::v-deep.el-radio-group {
  span {
    font-size: 24px;
  }
}
.purple-button {
  background-color: #7e22ce;
  border-color: #7e22ce;
  color: #fff;
}
.purple-button:hover,
.purple-button:focus {
  background-color: #9333ea;
  border-color: #9333ea;
}
.purple-button:active {
  background-color: #6b21a8;
  border-color: #6b21a8;
}
.button-textxga {
  color: #de7897;
}
.purple-button.is-disabled {
  background-color: #d8b4fe;
  border-color: #d8b4fe;
  opacity: 1; /* ä¿æŒç¦ç”¨çŠ¶æ€é€æ˜Žåº¦ */
}
// é€‰é¡¹å­—体放大
// ::v-deep.el-checkbox-group {
//   span {
//     font-size: 24px;
//   }
// }
</style>
src/views/followvisit/discharge/ClickCall copy.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1258 @@
<template>
  <div class="websocket-demo">
    <div>
      <h3>Websocket接口测试DEMO</h3>
      <div class="config-area">
        <div class="input-group">
          <label>CTI_WS_URL</label>
          <input
            type="text"
            v-model="config.cti_ws_url"
            placeholder="ws://40.78.0.169:6688"
          />
          <label>坐席工号</label>
          <input type="text" v-model="config.seatname" placeholder="8000" />
          <label>坐席分机</label>
          <input type="text" v-model="config.seatnum" placeholder="8000" />
          <label>密码</label>
          <input type="text" v-model="config.password" placeholder="123456" />
        </div>
        <div class="input-group">
          <label>外线号码</label>
          <input type="text" v-model="config.phone" placeholder="10086" />
          <label>UUID</label>
          <input type="text" v-model="config.uuid" />
          <label>其他坐席</label>
          <input type="text" v-model="config.other" placeholder="8001" />
          <label>技能组</label>
          <input type="text" v-model="config.group" placeholder="a3" />
          <label>外呼参数id</label>
          <input type="text" v-model="config.paramid" placeholder="3" />
        </div>
      </div>
      <!-- æ“ä½œæŒ‰é’®åŒºåŸŸ -->
      <div class="button-area">
        <!-- ç¬¬ä¸€è¡ŒæŒ‰é’® -->
        <div class="button-row">
          <button @click="seatlogin">签入</button>
          <button @click="seatlogout">签出</button>
          <button @click="afk">示忙</button>
          <button @click="online">示闲</button>
          <button @click="pickup">代答</button>
        </div>
        <!-- ç¬¬äºŒè¡ŒæŒ‰é’® -->
        <div class="button-row">
          <button @click="hangup">挂机</button>
          <button @click="callout">外呼</button>
          <button @click="transfer">通话转移</button>
          <button @click="transferresume">通话转移收回</button>
          <button @click="hold">通话保持</button>
          <button @click="holdresume">通话保持收回</button>
          <button @click="remove">通话强拆</button>
          <button @click="insert">通话强插</button>
          <button @click="monitor">监听</button>
          <button @click="monitor_to_talk">监听转通话</button>
          <button @click="monitor_end">监听结束</button>
          <button @click="choosecall">选择</button>
          <button @click="replacecall">代接</button>
          <button @click="three">三方通话</button>
        </div>
        <!-- ç¬¬ä¸‰è¡ŒæŒ‰é’® -->
        <div class="button-row">
          <button @click="handoff_ready">咨询开始</button>
          <button @click="handoff_call">咨询呼叫</button>
          <button @click="handoff_resume">咨询收回</button>
          <button @click="handoff_transfer">咨询转移</button>
          <button @click="handoff_three">咨询三方</button>
          <button @click="record_start">开始通话录音</button>
          <button @click="record_stop">停止通话录音</button>
        </div>
        <!-- ç¬¬å››è¡ŒæŒ‰é’® -->
        <div class="button-row">
          <button @click="openseatlist">打开坐席状态</button>
          <button @click="closeseatlist">关闭坐席状态</button>
          <button @click="openqueues">打开队列信息</button>
          <button @click="closequeues">关闭队列信息</button>
          <button @click="opencalllist">打开通话信息</button>
          <button @click="closecalllist">关闭通话信息</button>
          <button @click="openroutelist">打开路由信息</button>
          <button @click="closeroutelist">关闭路由信息</button>
        </div>
        <!-- ç¬¬äº”行按钮 -->
        <div class="button-row">
          <button @click="seatlist">获取坐席信息</button>
          <button @click="queues">获取队列信息</button>
          <button @click="calllist">获取通话信息</button>
          <button @click="routelist">获取路由信息</button>
          <button @click="batch">获取外呼参数信息</button>
          <button @click="batch_start">开始外呼任务</button>
          <button @click="batch_stop">停止外呼任务</button>
        </div>
      </div>
      <!-- æ—¥å¿—显示区域 -->
      <h3>协议日志区<button @click="testclear">清除</button></h3>
      <div id="msg" class="log-area">{{ logs }}</div>
    </div>
  </div>
</template>
<script>
import { CallsetState, CallgetList } from "@/api/AiCentre/index";
export default {
  name: "WebsocketDemo",
  data() {
    return {
      config: {
        cti_ws_url: "wss://9.208.2.190:8092/cal-api/",
        seatname: "8000",
        seatnum: "8000",
        password: "123456",
        phone: "10086",
        uuid: "",
        other: "8001",
        group: "a3",
        paramid: "3",
      },
      randomNum: "",
      randomID: "",
      logs: "",
      ws: null,
      isConnected: false,
    };
  },
  mounted() {
    this.CallgetList();
    this.initializeWebSocket();
  },
  beforeUnmount() {
    this.disconnectWebSocket();
  },
  methods: {
    // åˆå§‹åŒ–WebSocket连接
    initializeWebSocket() {
      try {
        // æ ¹æ®å½“前页面协议自动选择WS协议
        const isHttps = window.location.protocol === "https:";
        this.config.cti_ws_url = isHttps
          ? "wss://9.208.2.190:8092/cal-api/"
          : "ws://40.78.0.169:6688";
        if (typeof window.WebSocket === "undefined") {
          this.addLog("错误: æµè§ˆå™¨ä¸æ”¯æŒWebSocket");
          return;
        }
        this.connectWebSocket();
      } catch (error) {
        this.addLog(`初始化WebSocket错误: ${error.message}`);
        // å°è¯•使用备用地址
        this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
        setTimeout(() => this.connectWebSocket(), 2000);
      }
    },
    // æŸ¥è¯¢å¯ç”¨åˆ†æœºå·
    async CallgetList() {
      try {
        const res = await CallgetList();
        this.randomNum = res.data[0].tel;
        this.randomID = res.data[0].id;
        // æ­£ç¡®è®¾ç½® sipUri
        this.config.seatname = randomNum;
        this.config.seatnum = randomNum;
        this.startCallsetState();
      } catch (error) {
        console.error("获取座席号失败:", error);
        // this.updateStatus("failed", "获取座席号失败");
      }
    },
    //使用座席号
    async startCallsetState() {
      try {
        await CallsetState({ id: this.randomID, state: 1 });
        console.log("座席号状态更新为使用中");
      } catch (error) {
        console.error("更新座席号状态失败:", error);
      }
    },
    //释放座席号
    async overCallsetState() {
      try {
        if (this.randomID) {
          await CallsetState({ id: this.randomID, state: 0 });
          console.log("座席号状态更新为可用");
        }
      } catch (error) {
        console.error("释放座席号失败:", error);
      }
    },
    // è¿žæŽ¥WebSocket
    connectWebSocket() {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        this.addLog("WebSocket已连接");
        return;
      }
      try {
        let wsUrl = this.config.cti_ws_url;
        // ç¡®ä¿HTTPS页面使用WSS
        if (
          window.location.protocol === "https:" &&
          wsUrl.startsWith("ws://")
        ) {
          wsUrl = wsUrl.replace("ws://", "wss://");
        }
        this.ws = new WebSocket(wsUrl);
        this.ws.onopen = () => {
          this.isConnected = true;
          this.addLog("WebSocket连接成功");
        };
        this.ws.onmessage = (event) => {
          this.handleWebSocketMessage(event);
        };
        this.ws.onclose = (event) => {
          this.isConnected = false;
          this.addLog(`WebSocket连接关闭: ${event.code} ${event.reason}`);
          // è‡ªåŠ¨é‡è¿ž
          setTimeout(() => this.connectWebSocket(), 3000);
        };
        this.ws.onerror = (error) => {
          this.addLog(`WebSocket错误: ${error.message}`);
          // å°è¯•备用URL
          if (!wsUrl.includes("9.208.2.190")) {
            this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
            setTimeout(() => this.connectWebSocket(), 3000);
          }
        };
      } catch (error) {
        this.addLog(`连接WebSocket失败: ${error.message}`);
        // å°è¯•备用URL
        this.config.cti_ws_url = "wss://9.208.2.190:8092/cal-api/";
        setTimeout(() => this.connectWebSocket(), 3000);
      }
    },
    // å¤„理WebSocket消息
    handleWebSocketMessage(event) {
      const reader = new FileReader();
      reader.onloadend = (e) => {
        const message = reader.result;
        this.addLog(`收到消息: ${message}`);
        try {
          const obj = JSON.parse(message);
          // å¤„理心跳包
          if (obj.cmd === "system" && obj.action === "keepalive") {
            this.keepalive(obj.seatname, obj.seatnum);
          }
          // è‡ªåŠ¨è®¾ç½®UUID
          if (obj.cmd === "control" && obj.action === "tp_callin") {
            this.config.uuid = obj.uuid;
            this.addLog(`自动设置UUID: ${obj.uuid}`);
          }
        } catch (error) {
          this.addLog(`消息解析错误: ${error.message}`);
        }
      };
      reader.readAsText(event.data);
    },
    // æ–­å¼€WebSocket连接
    disconnectWebSocket() {
      if (this.ws) {
        this.ws.close();
        this.ws = null;
        this.isConnected = false;
        this.addLog("WebSocket已断开");
      }
    },
    // å‘送WebSocket消息
    sendWebSocketMessage(message) {
      if (!this.isConnected || !this.ws) {
        this.addLog("错误: WebSocket未连接");
        return false;
      }
      try {
        const messageStr =
          typeof message === "string" ? message : JSON.stringify(message);
        this.ws.send(messageStr);
        this.addLog(`发送消息: ${messageStr}`);
        return true;
      } catch (error) {
        this.addLog(`发送消息失败: ${error.message}`);
        return false;
      }
    },
    // éªŒè¯å‚æ•°
    validateParams(params, requiredFields) {
      for (const field of requiredFields) {
        if (!params[field] || params[field].toString().trim() === "") {
          this.addLog(`错误: ${field} ä¸èƒ½ä¸ºç©º`);
          return false;
        }
      }
      return true;
    },
    // ==================== WebSocket.js åŠŸèƒ½æ•´åˆ ====================
    // ç­¾å…¥
    seatlogin() {
      const { seatname, seatnum, password, cti_ws_url } = this.config;
      if (
        !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
      ) {
        return;
      }
      // é‡æ–°è¿žæŽ¥WebSocket(原js文件中的逻辑)
      this.connectWebSocket();
      setTimeout(() => {
        const protocol = {
          cmd: "system",
          action: "seatlogin",
          seatname: seatname,
          seatnum: seatnum,
          password: password,
          timestamp: Date.now(),
        };
        this.sendWebSocketMessage(protocol);
      }, 1000);
    },
    // ç­¾å‡º
    seatlogout() {
      const { seatname, seatnum } = this.config;
      if (
        !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
      ) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "seatlogout",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
      this.ws.close();
    },
    // ç¤ºå¿™
    afk() {
      const { seatname, seatnum } = this.config;
      if (
        !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
      ) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "afk",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç¤ºé—²
    online() {
      const { seatname, seatnum } = this.config;
      if (
        !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
      ) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "online",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ä»£ç­”
    pickup() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "pickup",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æŒ‚机
    hangup() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "hangup",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¤–呼
    callout() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "callout",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯è½¬ç§»
    transfer() {
      const { seatname, seatnum, phone, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, phone, uuid }, [
          "seatnum",
          "phone",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "transfer",
        uuid: uuid,
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯è½¬ç§»æ”¶å›ž
    transferresume() {
      const { seatname, seatnum, phone, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, phone, uuid }, [
          "seatnum",
          "phone",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "transferresume",
        uuid: uuid,
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯ä¿æŒ
    hold() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "hold",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯ä¿æŒæ”¶å›ž
    holdresume() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "holdresume",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯å¼ºæ‹†
    remove() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "remove",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯å¼ºæ’
    insert() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "insert",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç›‘听
    monitor() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "monitor",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç›‘听转通话
    monitor_to_talk() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "monitor_to_talk",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç›‘听结束
    monitor_end() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "monitor_end",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€‰æ‹©é€šè¯
    choosecall() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "choosecall",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ä»£æŽ¥
    replacecall() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "replacecall",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ä¸‰æ–¹é€šè¯
    three() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "three",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢å¼€å§‹
    handoff_ready() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_ready",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢å‘¼å«
    handoff_call() {
      const { seatname, seatnum, other, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, other, uuid }, [
          "seatnum",
          "other",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_call",
        uuid: uuid,
        phone: other,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢æ”¶å›ž
    handoff_resume() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_resume",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢è½¬ç§»
    handoff_transfer() {
      const { seatname, seatnum, other, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, other, uuid }, [
          "seatnum",
          "other",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_transfer",
        uuid: uuid,
        phone: other,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢ä¸‰æ–¹
    handoff_three() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_three",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¼€å§‹é€šè¯å½•音
    record_start() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "record_start",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // åœæ­¢é€šè¯å½•音
    record_stop() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "record_stop",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开坐席状态
    openseatlist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "openseatlist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­åå¸­çŠ¶æ€
    closeseatlist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closeseatlist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开队列信息
    openqueues() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "openqueues",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­é˜Ÿåˆ—信息
    closequeues() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closequeues",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开通话信息
    opencalllist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "opencalllist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­é€šè¯ä¿¡æ¯
    closecalllist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closecalllist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开路由信息
    openroutelist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "openroutelist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­è·¯ç”±ä¿¡æ¯
    closeroutelist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closeroutelist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–åå¸­ä¿¡æ¯
    seatlist() {
      const { group } = this.config;
      if (!this.validateParams({ group }, ["group"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "seatlist",
        group: group,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–é˜Ÿåˆ—ä¿¡æ¯
    queues() {
      const protocol = {
        cmd: "status",
        action: "queues",
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–é€šè¯ä¿¡æ¯
    calllist() {
      const protocol = {
        cmd: "status",
        action: "calllist",
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–è·¯ç”±ä¿¡æ¯
    routelist() {
      const protocol = {
        cmd: "status",
        action: "routelist",
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–å¤–å‘¼å‚æ•°ä¿¡æ¯
    batch() {
      const { paramid } = this.config;
      if (!this.validateParams({ paramid }, ["paramid"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "batch",
        paramid: paramid,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¼€å§‹å¤–呼任务
    batch_start() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "batch_start",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // åœæ­¢å¤–呼任务
    batch_stop() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "batch_stop",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¿ƒè·³åŒ…
    keepalive(seatname, seatnum) {
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "keepalive",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ¸…除日志
    testclear() {
      this.logs = "";
      this.addLog("日志已清除");
    },
    // æ·»åŠ æ—¥å¿—
    addLog(message) {
      const timestamp = new Date().toLocaleTimeString();
      this.logs += `[${timestamp}] ${message}\n`;
      // é™åˆ¶æ—¥å¿—长度,防止内存溢出
      const logLines = this.logs.split("\n");
      if (logLines.length > 100) {
        this.logs = logLines.slice(-50).join("\n");
      }
    },
  },
};
</script>
<style scoped>
.websocket-demo {
  font-family: Arial, sans-serif;
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}
.config-area {
  margin-bottom: 20px;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: #f9f9f9;
}
.input-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.input-group label {
  font-weight: bold;
  min-width: 80px;
}
.input-group input {
  padding: 5px 10px;
  border: 1px solid #ccc;
  border-radius: 3px;
  width: 120px;
}
.button-area {
  margin-bottom: 20px;
}
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 10px;
}
.button-row button {
  padding: 8px 15px;
  border: 1px solid #ccc;
  border-radius: 3px;
  background-color: #f0f0f0;
  cursor: pointer;
  transition: background-color 0.3s;
  font-size: 12px;
}
.button-row button:hover {
  background-color: #e0e0e0;
}
.button-row button:active {
  background-color: #d0d0d0;
  transform: translateY(1px);
}
.log-area {
  height: 300px;
  overflow-y: auto;
  border: 1px solid #ccc;
  padding: 10px;
  background-color: #f5f5f5;
  white-space: pre-wrap;
  font-family: "Courier New", monospace;
  font-size: 12px;
  line-height: 1.4;
}
h3 {
  color: #333;
  border-bottom: 2px solid #eee;
  padding-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
h3 button {
  padding: 5px 10px;
  font-size: 12px;
  background-color: #f0f0f0;
  border: 1px solid #ccc;
  border-radius: 3px;
  cursor: pointer;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .websocket-demo {
    padding: 10px;
  }
  .input-group {
    flex-direction: column;
    align-items: flex-start;
  }
  .input-group input {
    width: 100%;
  }
  .button-row {
    flex-direction: column;
  }
  .button-row button {
    width: 100%;
    margin-bottom: 5px;
  }
}
</style>
src/views/followvisit/discharge/ClickCall.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1502 @@
<template>
  <div class="websocket-demo">
    <div>
      <h3>Websocket呼叫中心接口</h3>
      <div class="config-area">
        <div class="status-indicator">
          <span :class="['status-dot', connectionStatus]"></span>
          è¿žæŽ¥çŠ¶æ€: {{ connectionText }}
          <span :class="['status-dot', seatStatus]"></span>
          åº§å¸­çŠ¶æ€: {{ seatStatusText }}
        </div>
        <div class="input-group">
          <label>CTI_WS_URL</label>
          <input
            type="text"
            v-model="config.cti_ws_url"
            placeholder="wss://your-server.com"
          />
          <label>坐席工号</label>
          <input
            type="text"
            v-model="config.seatname"
            :placeholder="randomNum"
          />
          <label>坐席分机</label>
          <input
            type="text"
            v-model="config.seatnum"
            :placeholder="randomNum"
          />
          <label>密码</label>
          <input type="text" v-model="config.password" placeholder="123456" />
        </div>
        <div class="input-group">
          <label>外线号码</label>
          <input
            type="text"
            v-model="customerPhone"
            placeholder="请输入电话号码"
          />
          <label>技能组</label>
          <input type="text" v-model="config.group" placeholder="a3" />
        </div>
      </div>
      <!-- æ“ä½œæŒ‰é’®åŒºåŸŸ -->
      <div class="button-area">
        <div class="button-row">
          <button
            @click="handleSeatLogin"
            :disabled="!isConnected || isSeatLoggedIn"
          >
            ç­¾å…¥
          </button>
          <button @click="handleSeatLogout" :disabled="!isSeatLoggedIn">
            ç­¾å‡º
          </button>
          <button @click="callout" :disabled="!isSeatLoggedIn">外呼</button>
          <button @click="hangup" :disabled="!isSeatLoggedIn">挂机</button>
        </div>
        <div class="button-row">
          <button @click="afk" :disabled="!isSeatLoggedIn">示忙</button>
          <button @click="online" :disabled="!isSeatLoggedIn">示闲</button>
          <button @click="hold" :disabled="!isSeatLoggedIn">保持</button>
          <button @click="holdresume" :disabled="!isSeatLoggedIn">
            å–消保持
          </button>
        </div>
      </div>
      <!-- æ—¥å¿—显示区域 -->
      <h3>协议日志区 <button @click="testclear">清除</button></h3>
      <div class="log-area">{{ logs }}</div>
    </div>
  </div>
</template>
<script>
import { CallsetState, CallgetList } from "@/api/AiCentre/index";
export default {
  name: "WebsocketDemo",
  emits: ["status-change", "call-status", "error"],
  props: {
    customerPhone: {
      type: String,
      default: "",
    },
    autoLogin: {
      type: Boolean,
      default: true,
    },
  },
  data() {
    return {
      config: {
        cti_ws_url: "",
        seatname: "",
        seatnum: "",
        password: "123456",
        phone: "",
        uuid: "",
        other: "8001",
        group: "a3",
        paramid: "3",
      },
      randomNum: "",
      randomID: "",
      logs: "",
      ws: null,
      isConnected: false,
      isSeatLoggedIn: false,
      currentCallStatus: "idle", // idle, calling, connected
      seatResourceAcquired: false,
      reconnectAttempts: 0,
      maxReconnectAttempts: 5,
      heartbeatTimer: null,
    };
  },
  computed: {
    connectionStatus() {
      return this.isConnected ? "connected" : "disconnected";
    },
    connectionText() {
      return this.isConnected ? "已连接" : "未连接";
    },
    seatStatus() {
      return this.isSeatLoggedIn ? "logged-in" : "logged-out";
    },
    seatStatusText() {
      return this.isSeatLoggedIn ? "已签入" : "未签入";
    },
  },
  watch: {
    customerPhone(newVal) {
      this.config.phone = newVal;
    },
    isSeatLoggedIn(newVal) {
      this.$emit("status-change", {
        isLoggedIn: newVal,
        seatNumber: this.config.seatnum,
        status: newVal ? "ready" : "offline",
      });
    },
  },
  async mounted() {
    await this.initializeSeatResource();
    this.initializeWebSocket();
  },
  beforeUnmount() {
    this.cleanup();
  },
  methods: {
    // åˆå§‹åŒ–WebSocket连接
    initializeWebSocket() {
      try {
        const isHttps = window.location.protocol === "https:";
        this.config.cti_ws_url = isHttps
          ? "wss://9.208.2.190:8092/cal-api/"
          : "ws://40.78.0.169:6688";
        if (typeof window.WebSocket === "undefined") {
          this.addLog("错误: æµè§ˆå™¨ä¸æ”¯æŒWebSocket");
          return;
        }
        this.connectWebSocket();
      } catch (error) {
        this.addLog(`初始化WebSocket错误: ${error.message}`);
        setTimeout(() => this.connectWebSocket(), 2000);
      }
    },
    // åˆå§‹åŒ–座席号资源
    async initializeSeatResource() {
      try {
        const res = await CallgetList();
        if (res.data && res.data.length > 0) {
          // this.randomNum = res.data[0].tel;
          // this.randomID = res.data[0].id;
          this.randomNum = 8000;
          this.randomID = 8000;
          // è®¾ç½®é»˜è®¤åº§å¸­å·
          this.config.seatname = this.randomNum;
          this.config.seatnum = this.randomNum;
          // ç«‹å³å ç”¨åº§å¸­å·èµ„源
          await this.startCallsetState();
          this.seatResourceAcquired = true;
          this.addLog(`座席号资源获取成功: ${this.randomNum}`);
        }
      } catch (error) {
        console.error("获取座席号失败:", error);
        this.addLog("错误: èŽ·å–åº§å¸­å·èµ„æºå¤±è´¥");
        this.$emit("error", { type: "seat_acquisition_failed", error });
      }
    },
    // å ç”¨åº§å¸­å·
    async startCallsetState() {
      try {
        await CallsetState({ id: this.randomID, state: 1 });
        this.addLog("座席号状态更新为使用中");
      } catch (error) {
        console.error("更新座席号状态失败:", error);
        throw error;
      }
    },
    // é‡Šæ”¾åº§å¸­å·
    async releaseSeatResource() {
      if (this.seatResourceAcquired && this.randomID) {
        try {
          await CallsetState({ id: this.randomID, state: 0 });
          this.addLog("座席号资源已释放");
          this.seatResourceAcquired = false;
        } catch (error) {
          console.error("释放座席号失败:", error);
        }
      }
    },
    // è¿žæŽ¥WebSocket
    // è¿žæŽ¥WebSocket
    connectWebSocket() {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        return;
      }
      if (this.reconnectAttempts >= this.maxReconnectAttempts) {
        this.addLog("错误: è¾¾åˆ°æœ€å¤§é‡è¿žæ¬¡æ•°ï¼Œåœæ­¢é‡è¿ž");
        return;
      }
      try {
        let wsUrl = this.config.cti_ws_url;
        if (
          window.location.protocol === "https:" &&
          wsUrl.startsWith("ws://")
        ) {
          wsUrl = wsUrl.replace("ws://", "wss://");
        }
        this.ws = new WebSocket(wsUrl);
        this.ws.onopen = () => {
          this.isConnected = true;
          this.reconnectAttempts = 0;
          this.addLog("WebSocket连接成功");
          this.startHeartbeat();
          // è¿žæŽ¥æˆåŠŸåŽè‡ªåŠ¨ç­¾å…¥
          if (this.autoLogin && this.seatResourceAcquired) {
            setTimeout(() => this.handleSeatLogin(), 500);
          }
        };
        this.ws.onmessage = (event) => {
          this.handleWebSocketMessage(event);
        };
        this.ws.onclose = (event) => {
          this.isConnected = false;
          this.isSeatLoggedIn = false;
          this.stopHeartbeat();
          this.addLog(`WebSocket连接关闭: ${event.code} ${event.reason}`);
          // è‡ªåŠ¨é‡è¿ž
          if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++;
            setTimeout(() => this.connectWebSocket(), 3000);
          }
        };
        this.ws.onerror = (error) => {
          this.addLog(`WebSocket错误: ${error.message}`);
        };
      } catch (error) {
        this.addLog(`连接WebSocket失败: ${error.message}`);
        setTimeout(() => this.connectWebSocket(), 3000);
      }
    },
    // å¤„理WebSocket消息
    handleWebSocketMessage(event) {
      try {
        // æ£€æŸ¥æ•°æ®ç±»åž‹ï¼šå¯èƒ½æ˜¯å­—符串或Blob
        if (event.data instanceof Blob) {
          // å¤„理二进制数据(Blob)
          const reader = new FileReader();
          reader.onload = () => {
            try {
              const textData = reader.result;
              this.addLog(`收到Blob消息: ${textData}`);
              this.processWebSocketData(textData);
            } catch (error) {
              this.addLog(`Blob数据处理错误: ${error.message}`);
            }
          };
          reader.readAsText(event.data);
        } else if (typeof event.data === "string") {
          // ç›´æŽ¥å¤„理文本数据
          this.addLog(`收到文本消息: ${event.data}`);
          this.processWebSocketData(event.data);
        } else {
          this.addLog(`未知数据类型: ${typeof event.data}`);
        }
      } catch (error) {
        this.addLog(`消息处理错误: ${error.message}`);
      }
    },
    // ä¸“门处理解析后的WebSocket数据
    processWebSocketData(messageText) {
      console.log(messageText,'消息1');
      try {
        const obj = JSON.parse(messageText);
      console.log(obj,'消息2');
        // å¤„理心跳包
        if (obj.cmd === "system" && obj.action === "keepalive") {
          this.keepalive(obj.seatname, obj.seatnum);
        }
        // å¤„理挂断
         if (obj.action === "calloutend") {
          this.hangup();
        }
        // å¤„理签入响应
        if (obj.cmd === "system" && obj.action === "seatlogin") {
            this.isSeatLoggedIn = true;
            this.addLog("座席签入成功");
            this.$emit("status-change", {
              isLoggedIn: true,
              seatNumber: this.config.seatnum,
              status: "ready",
            });
        }
        // å¤„理签出响应
        if (obj.cmd === "system" && obj.action === "seatlogout") {
          this.isSeatLoggedIn = false;
          this.addLog("座席签出成功");
          this.$emit("status-change", {
            isLoggedIn: false,
            status: "offline",
          });
        }
        // è‡ªåŠ¨è®¾ç½®UUID(来电事件)
        if (obj.cmd === "control" && obj.action === "tp_callin") {
          this.config.uuid = obj.uuid;
          this.addLog(`自动设置UUID: ${obj.uuid}`);
          this.$emit("call-status", {
            status: "incoming",
            uuid: obj.uuid,
            phone: obj.phone || "未知号码",
          });
        }
        // å¤„理外呼响应
        if (obj.cmd === "control" && obj.action === "callout") {
            this.$emit("call-status", {
              status: obj.status || "calling",
              uuid: obj.uuid,
              phone: this.config.phone,
            });
        }
        // å¤„理挂机响应
        if (obj.cmd === "control" && obj.action === "hangup") {
          this.$emit("call-status", {
            status: "idle",
            uuid: obj.uuid,
          });
        }
        // å¤„理通话状态变化
        if (obj.cmd === "control" && obj.status) {
          this.handleCallStatusChange(obj);
        }
      } catch (error) {
        this.addLog(`JSON解析错误: ${error.message}, åŽŸå§‹æ•°æ®: ${messageText}`);
      }
    },
    // å¤„理呼叫状态变化
    handleCallStatusChange(obj) {
      const statusMap = {
        ringing: "振铃中",
        connected: "通话中",
        held: "已保持",
        ended: "通话结束",
      };
      this.addLog(`通话状态: ${statusMap[obj.status] || obj.status}`);
      this.$emit("call-status", {
        status: obj.status,
        uuid: obj.uuid,
        phone: obj.phone || this.config.phone,
      });
    },
    // å¼€å§‹å¿ƒè·³æ£€æµ‹
    startHeartbeat() {
      this.heartbeatTimer = setInterval(() => {
        if (this.isConnected && this.isSeatLoggedIn) {
          this.keepalive(this.config.seatname, this.config.seatnum);
        }
      }, 30000); // 30秒心跳
    },
    // åœæ­¢å¿ƒè·³æ£€æµ‹
    stopHeartbeat() {
      if (this.heartbeatTimer) {
        clearInterval(this.heartbeatTimer);
        this.heartbeatTimer = null;
      }
    },
    // åº§å¸­ç­¾å…¥
    async handleSeatLogin() {
      if (!this.seatResourceAcquired) {
        this.addLog("错误: æœªèŽ·å–åº§å¸­å·èµ„æºï¼Œæ— æ³•ç­¾å…¥");
        return;
      }
      const { seatname, seatnum, password } = this.config;
      if (!seatname || !seatnum) {
        this.addLog("错误: åº§å¸­å·¥å·å’Œåˆ†æœºå·ä¸èƒ½ä¸ºç©º");
        return;
      }
      const protocol = {
        cmd: "system",
        action: "seatlogin",
        seatname: seatname,
        seatnum: seatnum,
        password: password,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // åº§å¸­ç­¾å‡º
    async handleSeatLogout() {
      const { seatname, seatnum } = this.config;
      const protocol = {
        cmd: "system",
        action: "seatlogout",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      if (this.sendWebSocketMessage(protocol)) {
        this.isSeatLoggedIn = false;
        // å»¶è¿Ÿé‡Šæ”¾èµ„源,确保签出完成
        setTimeout(() => this.releaseSeatResource(), 1000);
      }
    },
    // æ–­å¼€WebSocket连接
    disconnectWebSocket() {
      if (this.ws) {
        this.ws.close();
        this.ws = null;
        this.isConnected = false;
        this.addLog("WebSocket已断开");
      }
    },
    // å‘送WebSocket消息
    sendWebSocketMessage(message) {
      if (!this.isConnected || !this.ws) {
        this.addLog("错误: WebSocket未连接");
        return false;
      }
      try {
        const messageStr =
          typeof message === "string" ? message : JSON.stringify(message);
        this.ws.send(messageStr);
        this.addLog(`发送消息: ${messageStr}`);
        return true;
      } catch (error) {
        this.addLog(`发送消息失败: ${error.message}`);
        return false;
      }
    },
    // éªŒè¯å‚æ•°
    validateParams(params, requiredFields) {
      for (const field of requiredFields) {
        if (!params[field] || params[field].toString().trim() === "") {
          this.addLog(`错误: ${field} ä¸èƒ½ä¸ºç©º`);
          return false;
        }
      }
      return true;
    },
    // ==================== WebSocket.js åŠŸèƒ½æ•´åˆ ====================
    // ç­¾å…¥
    seatlogin() {
      const { seatname, seatnum, password, cti_ws_url } = this.config;
      if (
        !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
      ) {
        return;
      }
      // é‡æ–°è¿žæŽ¥WebSocket(原js文件中的逻辑)
      this.connectWebSocket();
      setTimeout(() => {
        const protocol = {
          cmd: "system",
          action: "seatlogin",
          seatname: seatname,
          seatnum: seatnum,
          password: password,
          timestamp: Date.now(),
        };
        this.sendWebSocketMessage(protocol);
      }, 1000);
    },
    // ç­¾å‡º
    seatlogout() {
      const { seatname, seatnum } = this.config;
      if (
        !this.validateParams({ seatname, seatnum }, ["seatname", "seatnum"])
      ) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "seatlogout",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
      this.ws.close();
    },
    // ç¤ºå¿™
    afk() {
      const protocol = {
        cmd: "system",
        action: "afk",
        seatname: this.config.seatname,
        seatnum: this.config.seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç¤ºé—²
    online() {
      const protocol = {
        cmd: "system",
        action: "online",
        seatname: this.config.seatname,
        seatnum: this.config.seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ä»£ç­”
    pickup() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "pickup",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æŒ‚机
    hangup() {
      const protocol = {
        cmd: "control",
        action: "hangup",
        seatname: this.config.seatname,
        seatnum: this.config.seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¤–呼操作
    async callout(phoneNumber = null) {
      const phone = phoneNumber || this.customerPhone || this.config.phone;
      if (!phone) {
        this.addLog("错误: è¢«å«å·ç ä¸èƒ½ä¸ºç©º");
        this.$emit("error", { type: "phone_number_required" });
        return;
      }
      if (!this.isSeatLoggedIn) {
        this.addLog("错误: åº§å¸­æœªç­¾å…¥ï¼Œæ— æ³•外呼");
        return;
      }
      const protocol = {
        cmd: "control",
        action: "callout",
        phone: phone,
        seatname: this.config.seatname,
        seatnum: this.config.seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
      this.$emit("call-status", { status: "calling", phone });
    },
    // æ¸…理资源
    cleanup() {
      this.stopHeartbeat();
      if (this.ws) {
        this.ws.close();
        this.ws = null;
      }
      this.releaseSeatResource();
    },
    // é€šè¯è½¬ç§»
    transfer() {
      const { seatname, seatnum, phone, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, phone, uuid }, [
          "seatnum",
          "phone",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "transfer",
        uuid: uuid,
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯è½¬ç§»æ”¶å›ž
    transferresume() {
      const { seatname, seatnum, phone, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, phone, uuid }, [
          "seatnum",
          "phone",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "transferresume",
        uuid: uuid,
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯ä¿æŒ
    hold() {
      const protocol = {
        cmd: "control",
        action: "hold",
        uuid: this.config.uuid,
        seatname: this.config.seatname,
        seatnum: this.config.seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯ä¿æŒæ”¶å›ž
    holdresume() {
      const protocol = {
        cmd: "control",
        action: "holdresume",
        uuid: this.config.uuid,
        seatname: this.config.seatname,
        seatnum: this.config.seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯å¼ºæ‹†
    remove() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "remove",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€šè¯å¼ºæ’
    insert() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "insert",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç›‘听
    monitor() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "monitor",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç›‘听转通话
    monitor_to_talk() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "monitor_to_talk",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ç›‘听结束
    monitor_end() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "monitor_end",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // é€‰æ‹©é€šè¯
    choosecall() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "choosecall",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ä»£æŽ¥
    replacecall() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "replacecall",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // ä¸‰æ–¹é€šè¯
    three() {
      const { seatname, seatnum, phone } = this.config;
      if (!this.validateParams({ seatnum, phone }, ["seatnum", "phone"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "three",
        phone: phone,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢å¼€å§‹
    handoff_ready() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_ready",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢å‘¼å«
    handoff_call() {
      const { seatname, seatnum, other, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, other, uuid }, [
          "seatnum",
          "other",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_call",
        uuid: uuid,
        phone: other,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢æ”¶å›ž
    handoff_resume() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_resume",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢è½¬ç§»
    handoff_transfer() {
      const { seatname, seatnum, other, uuid } = this.config;
      if (
        !this.validateParams({ seatnum, other, uuid }, [
          "seatnum",
          "other",
          "uuid",
        ])
      ) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_transfer",
        uuid: uuid,
        phone: other,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å’¨è¯¢ä¸‰æ–¹
    handoff_three() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "handoff_three",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¼€å§‹é€šè¯å½•音
    record_start() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "record_start",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // åœæ­¢é€šè¯å½•音
    record_stop() {
      const { seatname, seatnum, uuid } = this.config;
      if (!this.validateParams({ seatnum, uuid }, ["seatnum", "uuid"])) {
        return;
      }
      const protocol = {
        cmd: "control",
        action: "record_stop",
        uuid: uuid,
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开坐席状态
    openseatlist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "openseatlist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­åå¸­çŠ¶æ€
    closeseatlist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closeseatlist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开队列信息
    openqueues() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "openqueues",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­é˜Ÿåˆ—信息
    closequeues() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closequeues",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开通话信息
    opencalllist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "opencalllist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­é€šè¯ä¿¡æ¯
    closecalllist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closecalllist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ‰“开路由信息
    openroutelist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "openroutelist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å…³é—­è·¯ç”±ä¿¡æ¯
    closeroutelist() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "closeroutelist",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–åå¸­ä¿¡æ¯
    seatlist() {
      const { group } = this.config;
      if (!this.validateParams({ group }, ["group"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "seatlist",
        group: group,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–é˜Ÿåˆ—ä¿¡æ¯
    queues() {
      const protocol = {
        cmd: "status",
        action: "queues",
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–é€šè¯ä¿¡æ¯
    calllist() {
      const protocol = {
        cmd: "status",
        action: "calllist",
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–è·¯ç”±ä¿¡æ¯
    routelist() {
      const protocol = {
        cmd: "status",
        action: "routelist",
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // èŽ·å–å¤–å‘¼å‚æ•°ä¿¡æ¯
    batch() {
      const { paramid } = this.config;
      if (!this.validateParams({ paramid }, ["paramid"])) {
        return;
      }
      const protocol = {
        cmd: "status",
        action: "batch",
        paramid: paramid,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // å¼€å§‹å¤–呼任务
    batch_start() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "batch_start",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // åœæ­¢å¤–呼任务
    batch_stop() {
      const { seatname, seatnum } = this.config;
      if (!this.validateParams({ seatnum }, ["seatnum"])) {
        return;
      }
      const protocol = {
        cmd: "system",
        action: "batch_stop",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    keepalive(seatname, seatnum) {
      const protocol = {
        cmd: "system",
        action: "keepalive",
        seatname: seatname,
        seatnum: seatnum,
        timestamp: Date.now(),
      };
      this.sendWebSocketMessage(protocol);
    },
    // æ¸…除日志
    testclear() {
      this.logs = "";
      this.addLog("日志已清除");
    },
    // æ·»åŠ æ—¥å¿—
    addLog(message) {
      const timestamp = new Date().toLocaleTimeString();
      this.logs += `[${timestamp}] ${message}\n`;
      // é™åˆ¶æ—¥å¿—长度,防止内存溢出
      const logLines = this.logs.split("\n");
      if (logLines.length > 100) {
        this.logs = logLines.slice(-50).join("\n");
      }
    },
  },
};
</script>
<style scoped>
.status-indicator {
  margin-bottom: 15px;
  padding: 10px;
  background: #f5f5f5;
  border-radius: 4px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.status-dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  display: inline-block;
}
.status-dot.connected {
  background-color: #52c41a;
}
.status-dot.disconnected {
  background-color: #f5222d;
}
.status-dot.logged-in {
  background-color: #1890ff;
}
.status-dot.logged-out {
  background-color: #d9d9d9;
}
.button-row button:disabled {
  opacity: 0.6;
  cursor: not-allowed;
}
.config-area {
  margin-bottom: 20px;
}
.input-group {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 10px;
}
.input-group label {
  font-weight: bold;
  min-width: 80px;
}
.input-group input {
  padding: 5px 10px;
  border: 1px solid #ccc;
  border-radius: 3px;
  width: 120px;
}
.button-area {
  margin-bottom: 20px;
}
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 10px;
}
.log-area {
  height: 200px;
  overflow-y: auto;
  border: 1px solid #ccc;
  padding: 10px;
  background: #f5f5f5;
  white-space: pre-wrap;
  font-family: monospace;
}
.websocket-demo {
  font-family: Arial, sans-serif;
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
}
.config-area {
  margin-bottom: 20px;
  padding: 15px;
  border: 1px solid #ddd;
  border-radius: 4px;
  background-color: #f9f9f9;
}
.input-group {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 10px;
}
.input-group label {
  font-weight: bold;
  min-width: 80px;
}
.input-group input {
  padding: 5px 10px;
  border: 1px solid #ccc;
  border-radius: 3px;
  width: 120px;
}
.button-area {
  margin-bottom: 20px;
}
.button-row {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  margin-bottom: 10px;
}
.button-row button {
  padding: 8px 15px;
  border: 1px solid #ccc;
  border-radius: 3px;
  background-color: #f0f0f0;
  cursor: pointer;
  transition: background-color 0.3s;
  font-size: 12px;
}
.button-row button:hover {
  background-color: #e0e0e0;
}
.button-row button:active {
  background-color: #d0d0d0;
  transform: translateY(1px);
}
.log-area {
  height: 300px;
  overflow-y: auto;
  border: 1px solid #ccc;
  padding: 10px;
  background-color: #f5f5f5;
  white-space: pre-wrap;
  font-family: "Courier New", monospace;
  font-size: 12px;
  line-height: 1.4;
}
h3 {
  color: #333;
  border-bottom: 2px solid #eee;
  padding-bottom: 10px;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
h3 button {
  padding: 5px 10px;
  font-size: 12px;
  background-color: #f0f0f0;
  border: 1px solid #ccc;
  border-radius: 3px;
  cursor: pointer;
}
/* å“åº”式设计 */
@media (max-width: 768px) {
  .websocket-demo {
    padding: 10px;
  }
  .input-group {
    flex-direction: column;
    align-items: flex-start;
  }
  .input-group input {
    width: 100%;
  }
  .button-row {
    flex-direction: column;
  }
  .button-row button {
    width: 100%;
    margin-bottom: 5px;
  }
}
</style>
src/views/followvisit/discharge/index.vue
@@ -120,7 +120,29 @@
            placeholder="请输入随访人员"
          ></el-input>
        </el-form-item>
        <el-form-item label="主治医生" prop="drname">
          <el-input
            v-model="topqueryParams.drname"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="经管医生" prop="managementDoctor">
          <el-input
            v-model="topqueryParams.managementDoctor"
            placeholder="请输入主治医生"
          ></el-input>
        </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="status">
          <el-cascader
            v-model="topqueryParams.scopetype"
@@ -169,7 +191,7 @@
      </el-form>
      <el-divider></el-divider>
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
        <!-- <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
@@ -182,11 +204,10 @@
              >
            </div>
          </div>
        </el-col>
        </el-col> -->
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
@@ -238,11 +259,7 @@
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="primary"
                plain
                size="medium"
                @click="affiliation()"
              <el-button type="primary" size="medium" @click="affiliation()"
                >本人所属服务</el-button
              >
            </div>
@@ -252,7 +269,7 @@
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >当日服务</el-button
                >今日服务</el-button
              >
            </div>
          </div>
@@ -391,6 +408,7 @@
          prop="drname"
        />
        <el-table-column
          v-if="orgname != '丽水市中医院'"
          label="随访人员"
          align="center"
          key="updateBy"
@@ -456,16 +474,6 @@
          key="leavehospitaldistrictname"
          prop="leavehospitaldistrictname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="诊断名称"
          align="center"
          key="leavediagname"
          prop="leavediagname"
          width="120"
          :show-overflow-tooltip="true"
        >
        </el-table-column>
@@ -828,7 +836,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
        {
@@ -876,6 +884,7 @@
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      },
      orgname: "",
      propss: { multiple: true },
      options: [],
@@ -924,6 +933,10 @@
          value: 1,
          label: "异常",
        },
         {
          value: 2,
          label: "警告",
        },
        {
          value: 0,
          label: "正常",
@@ -955,8 +968,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -977,6 +990,7 @@
    this.serviceState = store.getters.serviceState;
    this.checkboxlist = store.getters.checkboxlist;
    this.errtype = this.$route.query.errtype;
    this.orgname = localStorage.getItem("orgname");
    this.leavehospitaldistrictcode =
      this.$route.query.leavehospitaldistrictcode;
    this.sourcetype[0].children = store.getters.belongDepts.map((dept) => {
@@ -1011,6 +1025,21 @@
  methods: {
    /** æŸ¥è¯¢é—¨è¯Šéšè®¿æœåŠ¡åˆ—è¡¨ */
    getList(refresh) {
      // é»˜è®¤å…¨éƒ¨
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        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(
@@ -1062,6 +1091,22 @@
        });
        this.total = response.total;
      });
    },
    // æ—¶é—´
    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}`;
    },
    affiliation() {
      this.topqueryParams.managementDoctorCode = store.getters.hisUserId;
@@ -1233,11 +1278,64 @@
      };
      this.handleQuery(1);
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    handleSelectionChange(selection) {
      this.ids = selection.map((item) => item.userId);
      this.single = selection.length != 1;
      this.multiple = !selection.length;
    handleSelectionChange(rows) {
      this.selectedRows = rows.map((row) => {
        // åˆå§‹åŒ–评分字段
        return {
          ...row,
          authenticity: row.authenticity || 0,
          weekFinish: row.weekFinish || 0,
          standard: row.standard || 0,
          timeliness: row.timeliness || 0,
          library: row.library || 0,
          environment: row.environment || 0,
          doctorSatisfaction: row.doctorSatisfaction || 0,
          nurseSatisfaction: row.nurseSatisfaction || 0,
        };
      });
      if (this.selectedRows.length > 0) {
        this.multiple = false;
      } else {
        this.multiple = true;
      }
    },
    // è®¡ç®—总分
    calculateTotal(row) {
      return (
        (row.authenticity || 0) +
        (row.weekFinish || 0) +
        (row.standard || 0) +
        (row.timeliness || 0) +
        (row.library || 0) +
        (row.environment || 0) +
        (row.doctorSatisfaction || 0) +
        (row.nurseSatisfaction || 0)
      );
    },
    // ä¿å­˜è¯„分
    saveScores() {
      this.selectedRows.forEach((item) => {
        item.createBy = null;
        item.patName = item.sendname;
        item.hospitaldistrictname = item.leavehospitaldistrictname;
      });
      addsatisfaction(this.selectedRows).then((res) => {
        if (res.code == 200) {
          this.$message.success("评分保存成功");
          this.scoreDialogVisible = false;
          this.selectedRows = [];
          this.$refs.userform.clearSelection();
        } else {
          this.$modal.msgWarning("评分保存失败");
          this.scoreDialogVisible = false;
          this.selectedRows = [];
          this.$refs.userform.clearSelection();
        }
      });
      // è¿™é‡Œå¯ä»¥æ·»åŠ ä¿å­˜é€»è¾‘ï¼Œå¦‚è°ƒç”¨API保存评分
    },
    //删除选项
    handleClose(tag) {
@@ -1271,11 +1369,10 @@
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning("360功能暂未开通");
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1388,11 +1485,9 @@
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1430,6 +1525,8 @@
    tableRowClassName({ row, rowIndex }) {
      if (row.excep == 1) {
        return "warning-row";
      } else if (row.excep == 2) {
        return "remind-row";
      }
      return "";
    },
@@ -1450,6 +1547,9 @@
}
::v-deep.el-table .warning-row {
  background: #eec4c4;
}
::v-deep.el-table .remind-row {
  background: #fcf5aa;
}
.documentf {
@@ -1530,10 +1630,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #f2f8ff;
  color: #324a9b;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664d9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
@@ -1548,7 +1650,7 @@
  background: #d0fdd8;
}
::v-deep.ysfleftvlue .el-card__body:hover {
  background: #8df8a4;
  background: #0abc54;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-bb {
@@ -1573,7 +1675,7 @@
  color: #ffffff;
}
.button-zx {
  background: #4fabe9;
  background: #324a9b;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
@@ -1584,7 +1686,30 @@
    font-size: 24px;
  }
}
.purple-button {
  background-color: #7e22ce;
  border-color: #7e22ce;
  color: #fff;
}
.purple-button:hover,
.purple-button:focus {
  background-color: #9333ea;
  border-color: #9333ea;
}
.purple-button:active {
  background-color: #6b21a8;
  border-color: #6b21a8;
}
.button-textxga {
  color: #de7897;
}
.purple-button.is-disabled {
  background-color: #d8b4fe;
  border-color: #d8b4fe;
  opacity: 1; /* ä¿æŒç¦ç”¨çŠ¶æ€é€æ˜Žåº¦ */
}
// é€‰é¡¹å­—体放大
// ::v-deep.el-checkbox-group {
//   span {
src/views/followvisit/discharge/js/cti-manager.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,202 @@
// cti-manager.js - ç§»é™¤prototype依赖的版本
(function(root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof module === 'object' && module.exports) {
        module.exports = factory();
    } else {
        root.CTIManager = factory();
    }
}(this, function() {
    'use strict';
    // ç®€å•的对象扩展方法
    function extend(target, source) {
        if (!source) return target;
        for (var key in source) {
            if (source.hasOwnProperty(key)) {
                target[key] = source[key];
            }
        }
        return target;
    }
    function CTIManager(options) {
        var defaultOptions = {
            autoReconnect: true,
            maxReconnectAttempts: 5,
            debug: true
        };
        this.options = extend({}, defaultOptions);
        this.options = extend(this.options, options || {});
        this.websocket = null;
        this.isLoggedIn = false;
        this.currentCall = null;
        this.seatInfo = {};
        this.eventHandlers = {};
        this.init();
    }
    CTIManager.prototype = {
        init: function() {
            this.setupEventHandlers();
        },
        setupEventHandlers: function() {
            // é»˜è®¤äº‹ä»¶å¤„理器
            this.on('login_success', this.handleLoginSuccess.bind(this));
            this.on('login_failed', this.handleLoginFailed.bind(this));
            this.on('call_start', this.handleCallStart.bind(this));
            this.on('call_end', this.handleCallEnd.bind(this));
            this.on('seat_status_change', this.handleSeatStatusChange.bind(this));
        },
        connect: function(url) {
            try {
                // ç¡®ä¿CTIWebSocket已全局可用
                if (typeof CTIWebSocket === 'undefined') {
                    this.trigger('error', { type: 'dependency_error', message: 'CTIWebSocket未定义' });
                    return false;
                }
                this.websocket = new CTIWebSocket({
                    url: url,
                    debug: this.options.debug,
                    reconnectInterval: 3000,
                    maxReconnectAttempts: this.options.maxReconnectAttempts
                });
                this.bindWebSocketEvents();
                return true;
            } catch (error) {
                this.trigger('error', { type: 'connection_error', error: error });
                return false;
            }
        },
        bindWebSocketEvents: function() {
            var self = this;
            this.websocket.on('open', function() {
                self.trigger('connected');
            });
            this.websocket.on('close', function() {
                self.isLoggedIn = false;
                self.trigger('disconnected');
            });
            this.websocket.on('error', function(error) {
                self.trigger('error', error);
            });
            // ç›‘听特定的CTI消息
            this.websocket.on('cti:system:keepalive', function(data) {
                self.trigger('keepalive', data);
            });
            this.websocket.on('cti:control:tp_callin', function(data) {
                self.trigger('call_incoming', data);
                if (data.uuid) {
                    self.trigger('uuid_received', data.uuid);
                }
            });
        },
        // ç™»å½•相关方法
        login: function(seatname, seatnum, password) {
            if (!this.websocket || !this.websocket.isConnected) {
                this.trigger('error', { type: 'not_connected', message: 'WebSocket未连接' });
                return false;
            }
            return this.websocket.seatlogin(seatname, seatnum, password);
        },
        logout: function() {
            if (this.websocket && this.isLoggedIn) {
                var result = this.websocket.seatlogout(this.seatInfo.seatname, this.seatInfo.seatnum);
                if (result) {
                    this.isLoggedIn = false;
                    this.seatInfo = {};
                }
                return result;
            }
            return false;
        },
        // å‘¼å«æŽ§åˆ¶æ–¹æ³•
        makeCall: function(phoneNumber) {
            if (!this.isLoggedIn) {
                this.trigger('error', { type: 'not_logged_in', message: '坐席未登录' });
                return false;
            }
            return this.websocket.callout(this.seatInfo.seatname, this.seatInfo.seatnum, phoneNumber);
        },
        hangupCall: function(uuid) {
            if (!this.isLoggedIn) {
                return false;
            }
            var callUuid = uuid || (this.currentCall ? this.currentCall.uuid : '');
            return this.websocket.hangup(this.seatInfo.seatname, this.seatInfo.seatnum, callUuid);
        },
        // åå¸­çŠ¶æ€æŽ§åˆ¶
        setAfk: function() {
            if (this.isLoggedIn) {
                return this.websocket.afk(this.seatInfo.seatname, this.seatInfo.seatnum);
            }
            return false;
        },
        setOnline: function() {
            if (this.isLoggedIn) {
                return this.websocket.online(this.seatInfo.seatname, this.seatInfo.seatnum);
            }
            return false;
        },
        // äº‹ä»¶ç®¡ç†
        on: function(event, handler) {
            if (!this.eventHandlers[event]) {
                this.eventHandlers[event] = [];
            }
            this.eventHandlers[event].push(handler);
        },
        off: function(event, handler) {
            if (this.eventHandlers[event]) {
                var index = this.eventHandlers[event].indexOf(handler);
                if (index > -1) {
                    this.eventHandlers[event].splice(index, 1);
                }
            }
        },
        trigger: function(event, data) {
            if (this.eventHandlers[event]) {
                this.eventHandlers[event].forEach(function(handler) {
                    if (typeof handler === 'function') {
                        handler(data);
                    }
                });
            }
        },
        destroy: function() {
            if (this.websocket) {
                this.websocket.close();
                this.websocket = null;
            }
            this.eventHandlers = {};
        }
    };
    return CTIManager;
}));
src/views/followvisit/discharge/js/json2.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,506 @@
//  json2.js
//  2016-05-01
//  Public Domain.
//  NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
//  See http://www.JSON.org/js.html
//  This code should be minified before deployment.
//  See http://javascript.crockford.com/jsmin.html
//  USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
//  NOT CONTROL.
//  This file creates a global JSON object containing two methods: stringify
//  and parse. This file is provides the ES5 JSON capability to ES3 systems.
//  If a project might run on IE8 or earlier, then this file should be included.
//  This file does nothing on ES5 systems.
//      JSON.stringify(value, replacer, space)
//          value       any JavaScript value, usually an object or array.
//          replacer    an optional parameter that determines how object
//                      values are stringified for objects. It can be a
//                      function or an array of strings.
//          space       an optional parameter that specifies the indentation
//                      of nested structures. If it is omitted, the text will
//                      be packed without extra whitespace. If it is a number,
//                      it will specify the number of spaces to indent at each
//                      level. If it is a string (such as "\t" or "&nbsp;"),
//                      it contains the characters used to indent at each level.
//          This method produces a JSON text from a JavaScript value.
//          When an object value is found, if the object contains a toJSON
//          method, its toJSON method will be called and the result will be
//          stringified. A toJSON method does not serialize: it returns the
//          value represented by the name/value pair that should be serialized,
//          or undefined if nothing should be serialized. The toJSON method
//          will be passed the key associated with the value, and this will be
//          bound to the value.
//          For example, this would serialize Dates as ISO strings.
//              Date.prototype.toJSON = function (key) {
//                  function f(n) {
//                      // Format integers to have at least two digits.
//                      return (n < 10)
//                          ? "0" + n
//                          : n;
//                  }
//                  return this.getUTCFullYear()   + "-" +
//                       f(this.getUTCMonth() + 1) + "-" +
//                       f(this.getUTCDate())      + "T" +
//                       f(this.getUTCHours())     + ":" +
//                       f(this.getUTCMinutes())   + ":" +
//                       f(this.getUTCSeconds())   + "Z";
//              };
//          You can provide an optional replacer method. It will be passed the
//          key and value of each member, with this bound to the containing
//          object. The value that is returned from your method will be
//          serialized. If your method returns undefined, then the member will
//          be excluded from the serialization.
//          If the replacer parameter is an array of strings, then it will be
//          used to select the members to be serialized. It filters the results
//          such that only members with keys listed in the replacer array are
//          stringified.
//          Values that do not have JSON representations, such as undefined or
//          functions, will not be serialized. Such values in objects will be
//          dropped; in arrays they will be replaced with null. You can use
//          a replacer function to replace those with JSON values.
//          JSON.stringify(undefined) returns undefined.
//          The optional space parameter produces a stringification of the
//          value that is filled with line breaks and indentation to make it
//          easier to read.
//          If the space parameter is a non-empty string, then that string will
//          be used for indentation. If the space parameter is a number, then
//          the indentation will be that many spaces.
//          Example:
//          text = JSON.stringify(["e", {pluribus: "unum"}]);
//          // text is '["e",{"pluribus":"unum"}]'
//          text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t");
//          // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
//          text = JSON.stringify([new Date()], function (key, value) {
//              return this[key] instanceof Date
//                  ? "Date(" + this[key] + ")"
//                  : value;
//          });
//          // text is '["Date(---current time---)"]'
//      JSON.parse(text, reviver)
//          This method parses a JSON text to produce an object or array.
//          It can throw a SyntaxError exception.
//          The optional reviver parameter is a function that can filter and
//          transform the results. It receives each of the keys and values,
//          and its return value is used instead of the original value.
//          If it returns what it received, then the structure is not modified.
//          If it returns undefined then the member is deleted.
//          Example:
//          // Parse the text. Values that look like ISO date strings will
//          // be converted to Date objects.
//          myData = JSON.parse(text, function (key, value) {
//              var a;
//              if (typeof value === "string") {
//                  a =
//   /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
//                  if (a) {
//                      return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
//                          +a[5], +a[6]));
//                  }
//              }
//              return value;
//          });
//          myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
//              var d;
//              if (typeof value === "string" &&
//                      value.slice(0, 5) === "Date(" &&
//                      value.slice(-1) === ")") {
//                  d = new Date(value.slice(5, -1));
//                  if (d) {
//                      return d;
//                  }
//              }
//              return value;
//          });
//  This is a reference implementation. You are free to copy, modify, or
//  redistribute.
/*jslint
    eval, for, this
*/
/*property
    JSON, apply, call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
    getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
    lastIndex, length, parse, prototype, push, replace, slice, stringify,
    test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
if (typeof JSON !== "object") {
    JSON = {};
}
(function () {
    "use strict";
    var rx_one = /^[\],:{}\s]*$/;
    var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
    var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
    var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
    var rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
    function f(n) {
        // Format integers to have at least two digits.
        return n < 10
            ? "0" + n
            : n;
    }
    function this_value() {
        return this.valueOf();
    }
    if (typeof Date.prototype.toJSON !== "function") {
        Date.prototype.toJSON = function () {
            return isFinite(this.valueOf())
                ? this.getUTCFullYear() + "-" +
                        f(this.getUTCMonth() + 1) + "-" +
                        f(this.getUTCDate()) + "T" +
                        f(this.getUTCHours()) + ":" +
                        f(this.getUTCMinutes()) + ":" +
                        f(this.getUTCSeconds()) + "Z"
                : null;
        };
        Boolean.prototype.toJSON = this_value;
        Number.prototype.toJSON = this_value;
        String.prototype.toJSON = this_value;
    }
    var gap;
    var indent;
    var meta;
    var rep;
    function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
        rx_escapable.lastIndex = 0;
        return rx_escapable.test(string)
            ? "\"" + string.replace(rx_escapable, function (a) {
                var c = meta[a];
                return typeof c === "string"
                    ? c
                    : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
            }) + "\""
            : "\"" + string + "\"";
    }
    function str(key, holder) {
// Produce a string from holder[key].
        var i;          // The loop counter.
        var k;          // The member key.
        var v;          // The member value.
        var length;
        var mind = gap;
        var partial;
        var value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
        if (value && typeof value === "object" &&
                typeof value.toJSON === "function") {
            value = value.toJSON(key);
        }
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
        if (typeof rep === "function") {
            value = rep.call(holder, key, value);
        }
// What happens next depends on the value's type.
        switch (typeof value) {
        case "string":
            return quote(value);
        case "number":
// JSON numbers must be finite. Encode non-finite numbers as null.
            return isFinite(value)
                ? String(value)
                : "null";
        case "boolean":
        case "null":
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce "null". The case is included here in
// the remote chance that this gets fixed someday.
            return String(value);
// If the type is "object", we might be dealing with an object or an array or
// null.
        case "object":
// Due to a specification blunder in ECMAScript, typeof null is "object",
// so watch out for that case.
            if (!value) {
                return "null";
            }
// Make an array to hold the partial results of stringifying this object value.
            gap += indent;
            partial = [];
// Is the value an array?
            if (Object.prototype.toString.apply(value) === "[object Array]") {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
                length = value.length;
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value) || "null";
                }
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
                v = partial.length === 0
                    ? "[]"
                    : gap
                        ? "[\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "]"
                        : "[" + partial.join(",") + "]";
                gap = mind;
                return v;
            }
// If the replacer is an array, use it to select the members to be stringified.
            if (rep && typeof rep === "object") {
                length = rep.length;
                for (i = 0; i < length; i += 1) {
                    if (typeof rep[i] === "string") {
                        k = rep[i];
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (
                                gap
                                    ? ": "
                                    : ":"
                            ) + v);
                        }
                    }
                }
            } else {
// Otherwise, iterate through all of the keys in the object.
                for (k in value) {
                    if (Object.prototype.hasOwnProperty.call(value, k)) {
                        v = str(k, value);
                        if (v) {
                            partial.push(quote(k) + (
                                gap
                                    ? ": "
                                    : ":"
                            ) + v);
                        }
                    }
                }
            }
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
            v = partial.length === 0
                ? "{}"
                : gap
                    ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}"
                    : "{" + partial.join(",") + "}";
            gap = mind;
            return v;
        }
    }
// If the JSON object does not yet have a stringify method, give it one.
    if (typeof JSON.stringify !== "function") {
        meta = {    // table of character substitutions
            "\b": "\\b",
            "\t": "\\t",
            "\n": "\\n",
            "\f": "\\f",
            "\r": "\\r",
            "\"": "\\\"",
            "\\": "\\\\"
        };
        JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
            var i;
            gap = "";
            indent = "";
// If the space parameter is a number, make an indent string containing that
// many spaces.
            if (typeof space === "number") {
                for (i = 0; i < space; i += 1) {
                    indent += " ";
                }
// If the space parameter is a string, it will be used as the indent string.
            } else if (typeof space === "string") {
                indent = space;
            }
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
            rep = replacer;
            if (replacer && typeof replacer !== "function" &&
                    (typeof replacer !== "object" ||
                    typeof replacer.length !== "number")) {
                throw new Error("JSON.stringify");
            }
// Make a fake root object containing our value under the key of "".
// Return the result of stringifying the value.
            return str("", {"": value});
        };
    }
// If the JSON object does not yet have a parse method, give it one.
    if (typeof JSON.parse !== "function") {
        JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
            var j;
            function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
                var k;
                var v;
                var value = holder[key];
                if (value && typeof value === "object") {
                    for (k in value) {
                        if (Object.prototype.hasOwnProperty.call(value, k)) {
                            v = walk(value, k);
                            if (v !== undefined) {
                                value[k] = v;
                            } else {
                                delete value[k];
                            }
                        }
                    }
                }
                return reviver.call(holder, key, value);
            }
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
            text = String(text);
            rx_dangerous.lastIndex = 0;
            if (rx_dangerous.test(text)) {
                text = text.replace(rx_dangerous, function (a) {
                    return "\\u" +
                            ("0000" + a.charCodeAt(0).toString(16)).slice(-4);
                });
            }
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with "()" and "new"
// because they can cause invocation, and "=" because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
// replace all simple value tokens with "]" characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or "]" or
// "," or ":" or "{" or "}". If that is so, then the text is safe for eval.
            if (
                rx_one.test(
                    text
                        .replace(rx_two, "@")
                        .replace(rx_three, "]")
                        .replace(rx_four, "")
                )
            ) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The "{" operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
                j = eval("(" + text + ")");
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
                return (typeof reviver === "function")
                    ? walk({"": j}, "")
                    : j;
            }
// If the text is not JSON parseable, then a SyntaxError is thrown.
            throw new SyntaxError("JSON.parse");
        };
    }
}());
src/views/followvisit/discharge/js/prototype.js
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
src/views/followvisit/discharge/js/websocket.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,826 @@
var ws;
//--------------------------------��������websocket-----------------------------------------//
function initwebsocket(cti_ws_url) {
    if ('WebSocket' in window) {
        ws = new WebSocket(cti_ws_url);
    } else {
        alert("do not suports  websocket");
    }
    ws.onopen = function(evt) {
         alert("open websocket success");
    }
    ws.onclose = function(evt) {
        alert("websocket close");
    }
    //��������
    ws.onmessage = function(evt) {
            var reader = new FileReader();
            reader.onloadend = function(e) {
                        console.log(this.result);
                        var msgdiv = document.getElementById("msg");
                        var span = document.createElement("span");
                        span.innerHTML = "receive_msg:"+this.result + "<br />";
                        msgdiv.appendChild(span);
                        var obj = eval('(' + this.result + ')');
                        if(obj.cmd=="system"&&obj.action=="keepalive"){
                            keepalive(obj.seatname,obj.seatnum);
                            }
                            var uuid = document.getElementById("uuid");
                            if(obj.cmd=="control"&&obj.action=="tp_callin"){
                            uuid.value=obj.uuid;
                            }
                    };
            reader.readAsText(event.data);
    }
    ws.onerror = function(evt) {
        alert("websocket error");
    }
}
function sendmessage(protocolStr){
    console.log();
        var msgdiv = document.getElementById("msg");
                        var span = document.createElement("span");
                        span.innerHTML = "send_msg:"+protocolStr + "<br />";
                        msgdiv.appendChild(span);
    ws.send(protocolStr);
}
function testclear(){
        var msgdiv = document.getElementById("msg");
        msgdiv.innerHTML="";
}
//--------------------------------command send-----------------------------------------//
//��ϯǩ��
function seatlogin(seatname,seatnum,password,cti_ws_url) {
    if ('WebSocket' in window) {
        ws = new WebSocket(cti_ws_url);
    } else {
        alert("do not suports  websocket");
    }
    ws.onopen = function(evt) {
         alert("open websocket success");
             if(seatname=="")
        {
            alert("seatname is  null");
            return;
        }
        if(seatnum=="")
        {
            alert("seatnum is  null");
            return;
        }
        var protocol={"cmd":"system","action":"seatlogin","seatname":"<seatname>","seatnum":"<seatnum>","password":"<password>","timestamp":"<timestamp>"};
        protocol.seatname=seatname;
        protocol.seatnum=seatnum;
        protocol.password=password;
        var protocolStr=JSON.stringify(protocol);
        sendmessage(protocolStr);
    }
    ws.onclose = function(evt) {
        alert("websocket close");
    }
    //��������
    ws.onmessage = function(evt) {
            var reader = new FileReader();
            reader.onloadend = function(e) {
                        console.log(this.result);
                        var msgdiv = document.getElementById("msg");
                        var span = document.createElement("span");
                        span.innerHTML = "receive_msg:"+this.result + "<br />";
                        msgdiv.appendChild(span);
                        var obj = eval('(' + this.result + ')');
                        if(obj.cmd=="system"&&obj.action=="keepalive"){
                            keepalive(obj.seatname,obj.seatnum);
                            }
                            var uuid = document.getElementById("uuid");
                            if(obj.cmd=="control"&&obj.action=="tp_callin"){
                            uuid.value=obj.uuid;
                            }
                    };
            reader.readAsText(event.data);
    }
    ws.onerror = function(evt) {
        alert("websocket error");
    }
    //ws.send(protocolStr);
}
//��ϯǩ��
function seatlogout(seatname,seatnum) {
    if(seatname=="")
    {
        alert("seatname is  null");
        return;
    }
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"system","action":"seatlogout","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
    ws.close();
}
//����
function afk(seatname,seatnum) {
    if(seatname=="")
    {
        alert("seatname is  null");
        return;
    }
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"system","action":"afk","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����
function online(seatname,seatnum) {
    if(seatname=="")
    {
        alert("seatname is  null");
        return;
    }
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"system","action":"online","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//���
function callout(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"callout","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ת��
function transfer(seatname,seatnum,phone,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"transfer","uuid":"<uuid>","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    protocol.uuid=uuid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ת���ջ�
function transferresume(seatname,seatnum,phone,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"transferresume","uuid":"<uuid>","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    protocol.uuid=uuid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ͨ������
function hold(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"hold","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.uuid=uuid;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ͨ�������ջ�
function holdresume(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"holdresume","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.uuid=uuid;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����
function pickup(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"control","action":"pickup","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�Ҷ�
function hangup(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"control","action":"hangup","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ͨ��ǿ��
function remove(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"remove","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ͨ��ǿ��
function insert(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"insert","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����
function monitor(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"monitor","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����תͨ��
function monitor_to_talk(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"monitor_to_talk","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��������
function monitor_end(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"monitor_end","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ѡ��
function choosecall(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"choosecall","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.uuid=uuid;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����
function replacecall(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"replacecall","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����ͨ��
function three(seatname,seatnum,phone) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    var protocol={"cmd":"control","action":"three","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//������ϯ״̬
function openseatlist(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"openseatlist","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�ر���ϯ״̬
function closeseatlist(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"closeseatlist","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�����Ŷ���Ϣ
function openqueues(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"openqueues","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�ر��Ŷ���Ϣ
function closequeues(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"closequeues","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����ͨ����Ϣ
function opencalllist(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"opencalllist","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�ر�ͨ����Ϣ
function closecalllist(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"closecalllist","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�����м���Ϣ
function openroutelist(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"openroutelist","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�ر��м���Ϣ
function closeroutelist(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"status","action":"closeroutelist","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ȡ��ϯ��Ϣ
function seatlist(group) {
    if(group=="")
    {
        alert("group is  null");
        return;
    }
    var protocol={"cmd":"status","action":"seatlist","group":"<group>","timestamp":"<timestamp>"};
    protocol.group=group;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ȡ�Ŷ���Ϣ
function queues() {
    var protocol={"cmd":"status","action":"queues","timestamp":"<timestamp>"};
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ȡͨ����Ϣ
function calllist() {
    var protocol={"cmd":"status","action":"calllist","timestamp":"<timestamp>"};
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ȡ�м���Ϣ
function routelist() {
    var protocol={"cmd":"status","action":"routelist","timestamp":"<timestamp>"};
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ȡ�������������Ϣ
function batch(paramid) {
    if(paramid=="")
    {
        alert("paramid is  null");
        return;
    }
    var protocol={"cmd":"status","action":"batch","paramid":"<paramid>","timestamp":"<timestamp>"};
    protocol.paramid=paramid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ʼ�������
function batch_start(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"system","action":"batch_start","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
  sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//�ر��������
function batch_stop(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"system","action":"batch_stop","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
  sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ͨ����ѯ��ʼ
function handoff_ready(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"handoff_ready","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.uuid=uuid;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ѯ
function handoff_call(seatname,seatnum,phone,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"handoff_call","uuid":"<uuid>","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    protocol.uuid=uuid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ѯ�ջ�
function handoff_resume(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"handoff_resume","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    protocol.uuid=uuid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ѯת��
function handoff_transfer(seatname,seatnum,phone,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(phone=="")
    {
        alert("phone is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"handoff_transfer","uuid":"<uuid>","phone":"<phone>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.phone=phone;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    protocol.uuid=uuid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//��ѯ����
function handoff_three(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"handoff_three","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    protocol.uuid=uuid;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//����
function keepalive(seatname,seatnum) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    var protocol={"cmd":"system","action":"keepalive","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.seatnum=seatnum;
    protocol.seatname=seatname;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
}
//ͨ������¼��
function record_start(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"record_start","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.uuid=uuid;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//ͨ��ֹͣ¼��
function record_stop(seatname,seatnum,uuid) {
    if(seatnum=="")
    {
        alert("seatnum is  null");
        return;
    }
    if(uuid=="")
    {
        alert("uuid is  null");
        return;
    }
    var protocol={"cmd":"control","action":"record_stop","uuid":"<uuid>","seatname":"<seatname>","seatnum":"<seatnum>","timestamp":"<timestamp>"};
    protocol.uuid=uuid;
    protocol.seatname=seatname;
    protocol.seatnum=seatnum;
    var protocolStr=JSON.stringify(protocol);
    sendmessage(protocolStr);
    //ws.send(protocolStr);
}
//---------------------------------------------------------------------------------//
src/views/followvisit/discharge/outpatientService.vue
@@ -186,8 +186,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -252,7 +251,7 @@
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >当日服务</el-button
                >今日服务</el-button
              >
            </div>
          </div>
@@ -917,7 +916,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
        {
@@ -972,13 +971,21 @@
          value: 3,
          label: "发送时间(倒序)",
        },
         {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: null,
        sort: 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 3,
        searchscope: 3,visitCount: 1,
        scopetype: [],
@@ -1064,8 +1071,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -1399,11 +1406,10 @@
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1516,11 +1522,9 @@
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1745,10 +1749,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
@@ -1766,6 +1772,9 @@
  background: #8df8a4;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-textxga {
  color: #de7897;
}
.button-bb {
  font-weight: 500;
  background-color: #2ba05c;
src/views/followvisit/linem/index.vue
@@ -7,8 +7,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            v-hasPermi="['system:user:add']"
src/views/followvisit/mzsatisfaction/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1976 @@
<template>
  <div class="app-container">
    <div class="leftvlue" style="margin-bottom: 20px">
      <el-row :gutter="10">
        <el-col :span="2.5" v-for="(item, index) in cardlist" :key="index">
          <el-card
            shadow="hover"
            :body-style="item.router ? ' cursor: pointer' : 'cursor: default'"
          >
            <div style="padding: 8px" @click="$router.push(item.router)">
              <span>{{ item.name }}</span>
              <div
                style="
                  text-align: center;
                  font-size: 18px;
                  margin-top: 10px;
                  font-weight: 600;
                "
              >
                {{ item.value ? item.value : 0 }}
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="2.5">
          <div class="ysfleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>表单已发送</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ yfsvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
        <el-col :span="2.5">
          <div class="errleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>异常</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ ycvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
      </el-row>
    </div>
    <el-row :gutter="20">
      <!--用户数据-->
      <el-form
        :model="topqueryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        v-show="showSearch"
        label-width="98px"
      >
        <el-form-item label="任务名称">
          <el-input
            v-model="topqueryParams.taskName"
            placeholder="请选择任务名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="出院时间">
          <el-date-picker
            v-model="dateRange"
            style="width: 240px"
            value-format="yyyy-MM-dd"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="患者姓名" prop="sendname">
          <el-input
            v-model="topqueryParams.sendname"
            placeholder="请输入患者姓名"
          ></el-input>
        </el-form-item>
        <el-form-item label="诊断名称" prop="leavediagname">
          <el-input
            v-model="topqueryParams.leavediagname"
            placeholder="请输入诊断名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访人员" prop="updateBy">
          <el-input
            v-model="topqueryParams.updateBy"
            placeholder="请输入随访人员"
          ></el-input>
        </el-form-item>
        <el-form-item label="主治医生" prop="drname">
          <el-input
            v-model="topqueryParams.drname"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="经管医生" prop="managementDoctor">
          <el-input
            v-model="topqueryParams.managementDoctor"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="患者范围" prop="status">
          <el-cascader
            v-model="topqueryParams.scopetype"
            placeholder="默认全部"
            :options="sourcetype"
            :props="{ expandTrigger: 'hover' }"
            @change="handleChange"
          ></el-cascader>
        </el-form-item>
        <el-form-item label="任务状态" prop="status">
          <el-select v-model="topqueryParams.sendstate" placeholder="请选择">
            <el-option
              v-for="item in topicoptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="排序方式" prop="status">
          <el-select v-model="topqueryParams.sort" placeholder="请选择">
            <el-option
              v-for="item in topicoptionssort"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="medium"
            @click="handleQuery(1)"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
      <el-divider></el-divider>
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-upload2"
                size="medium"
                @click="handleExport"
                >导出</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="primary"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
          >
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-warning-outline"
                size="medium"
                @click="toleadExport(1)"
                >执行失败</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="danger"
                plain
                icon="el-icon-warning"
                size="medium"
                @click="toleadExport(2)"
                >结果异常</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="success"
                plain
                size="medium"
                @click="buidegetTasklist()"
                >待办服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="primary"
                plain
                size="medium"
                @click="affiliation()"
                >本人所属服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >今日服务</el-button
              >
            </div>
          </div>
        </el-col>
      </el-row>
      <el-table
        v-loading="loading"
        ref="userform"
        :data="userList"
        :row-class-name="tableRowClassName"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column
          label="任务名称"
          fixed
          width="150"
          show-overflow-tooltip
          align="center"
          key="taskName"
          prop="taskName"
        />
        <!-- <el-table-column label="序号" fixed align="center" key="id" prop="id" /> -->
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          key="sendname"
          prop="sendname"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                gettoken360(scope.row.sfzh, scope.row.drcode, scope.row.drname)
              "
              ><span class="button-textsc">{{
                scope.row.sendname
              }}</span></el-button
            >
          </template>
        </el-table-column>
        <el-table-column
          label="任务状态"
          align="center"
          key="sendstate"
          prop="sendstate"
          width="120"
        >
          <template slot-scope="scope">
            <el-tooltip
              class="item"
              effect="dark"
              :content="scope.row.remark"
              placement="top-start"
            >
              <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>
            </el-tooltip>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务异常说明"
          width="120"
          align="center"
          key="remark"
          prop="remark" -->
        />
        <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
          label="随访完成时间"
          sortable
          align="center"
          prop="finishtime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.finishtime) }}</span>
          </template>
        </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="drname"
          prop="drname"
        />
        <el-table-column
          label="经管医生"
          align="center"
          key="managementDoctor"
          prop="managementDoctor"
          width="120"
        />
        <el-table-column
          label="出院天数"
          width="120"
          align="center"
          key="endDay"
          prop="endDay"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.endDay ? scope.row.endDay + "天" : "" }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="身份证号码"
          width="200"
          align="center"
          key="sfzh"
          prop="sfzh"
        />
        <el-table-column
          label="联系电话"
          width="200"
          align="center"
          key="phone"
          prop="phone"
        />
        <el-table-column
          label="责任护士"
          width="120"
          align="center"
          key="nurseName"
          prop="nurseName"
        />
        <!-- <el-table-column
          label="病历号"
          align="center"
          sortable
          key="medicalRecordNo"
          prop="medicalRecordNo"
          width="120"
        /> -->
        <!-- <el-table-column label="年龄" align="center" key="age" prop="age" /> -->
        <!-- <el-table-column label="性别"width="100" align="center" key="sex" prop="sex" /> -->
        <!-- <el-table-column label="床号" align="center" key="badNo" prop="badNo" /> -->
        <el-table-column
          label="科室"
          align="center"
          key="deptname"
          prop="deptname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="病区"
          align="center"
          key="leavehospitaldistrictname"
          prop="leavehospitaldistrictname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="诊断名称"
          align="center"
          key="leavediagname"
          prop="leavediagname"
          width="120"
          :show-overflow-tooltip="true"
        >
        </el-table-column>
 <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="门诊满意度模板名称"
          align="center"
          key="templatename"
          prop="templatename"
          width="200"
        />
        <el-table-column
          label="任务执行方式"
          align="center"
          key="preachform"
          prop="preachform"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.preachform">{{ item }}、 </span>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务发送流程"
          align="center"
          key="serviceSubtaskRecordList"
          prop="serviceSubtaskRecordList"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.serviceSubtaskRecordList"
              >{{ item.remark }}、
            </span>
          </template>
        </el-table-column> -->
        <el-table-column
          label="任务结果说明"
          width="220"
          align="center"
          key="remark"
          prop="remark"
        >
          <template slot-scope="scope" v-if="scope.row.remark">
            <el-tooltip
              :content="scope.row.remark"
              placement="top"
              effect="dark"
            >
              <el-tag
                type="warning"
                v-if="scope.row.sendstate != 5 && scope.row.sendstate != 4"
                >{{ scope.row.remark }}</el-tag
              >
              <el-tag type="warning" v-else>{{ scope.row.remark }}</el-tag>
            </el-tooltip>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
          fixed="right"
          width="300"
          class-name="small-padding fixed-width"
        >
          <template slot-scope="scope">
            <el-button size="medium" type="text" @click="Seedetails(scope.row)"
              ><span class="button-zx"
                ><i class="el-icon-s-order"></i>查看详情</span
              ></el-button
            >
            <el-button
              size="medium"
              type="text"
              @click="handleUpdate(scope.row)"
              ><span class="button-textxga"
                ><i class="el-icon-edit"></i>患者过滤</span
              ></el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="topqueryParams.pageNum"
        :limit.sync="topqueryParams.pageSize"
        @pagination="getList"
      />
    </el-row>
    <!-- æ»¡æ„åº¦å¼¹æ¡† -->
    <el-dialog
      title="随访满意度评分"
      :visible.sync="scoreDialogVisible"
      width="80%"
      :close-on-click-modal="false"
    >
      <el-table :data="selectedRows" border style="width: 100%">
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          prop="sendname"
        />
        <el-table-column
          label="任务名称"
          width="180"
          align="center"
          prop="taskName"
        />
        <!-- æ–°å¢žè¯„分列 -->
        <el-table-column
          label="真实性(20)"
          align="center"
          key="authenticity"
          prop="authenticity"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.authenticity"
              :min="0"
              :max="20"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="一周内完成(20)"
          align="center"
          key="weekFinish"
          prop="weekFinish"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.weekFinish"
              :min="0"
              :max="20"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="规范性(10)"
          align="center"
          key="standard"
          prop="standard"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.standard"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="及时性(10)"
          align="center"
          key="timeliness"
          prop="timeliness"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.timeliness"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="宣教情况(10)"
          align="center"
          key="library"
          prop="library"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.library"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="环境满意度(10)"
          align="center"
          key="environment"
          prop="environment"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.environment"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="医生满意度(10)"
          align="center"
          key="doctorSatisfaction"
          prop="doctorSatisfaction"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.doctorSatisfaction"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="护士满意度(10)"
          align="center"
          key="nurseSatisfaction"
          prop="nurseSatisfaction"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.nurseSatisfaction"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="总分"
          align="center"
          key="total"
          prop="total"
          fixed="right"
        >
          <template slot-scope="scope">
            <span>{{ calculateTotal(scope.row) }}</span>
          </template>
        </el-table-column>
      </el-table>
      <div slot="footer" class="dialog-footer">
        <el-button @click="scoreDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveScores">保存</el-button>
      </div>
    </el-dialog>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹å½±åƒéšè®¿å¯¹è¯æ¡† -->
    <el-dialog
      :title="amendtag ? '修改患者信息' : '新增患者'"
      :visible.sync="Labelchange"
      width="900px"
    >
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="8">
            <el-form-item label="姓名" width="100" prop="name">
              <el-input
                v-model="form.name"
                placeholder="请输入姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="性别" width="100" prop="sex">
              <el-select v-model="form.sex" placeholder="请选择性别">
                <el-option
                  v-for="dict in sextype"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="年龄" prop="age">
              <el-input
                v-model="form.age"
                placeholder="请输入年龄"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="8">
            <el-form-item label="过滤医生" width="100" prop="filterDrname">
              <el-input
                v-model="form.filterDrname"
                placeholder="请输入医生姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="过滤原因">
              <el-input
                v-model="form.notrequiredreason"
                type="textarea"
                placeholder="请输入过滤原因"
              ></el-input>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
        <el-button @click="cancel">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
    <!-- ä¿®æ”¹å‘送时间对话框 -->
    <el-dialog
      title="发送时间设置"
      :visible.sync="modificationVisible"
      width="45%"
    >
      <div style="margin-bottom: 20px; color: red">
        ç»Ÿä¸€ä¿®æ”¹å½“天未发送的任务时间
      </div>
      <el-form
        :model="ruleForm"
        :rules="rules"
        ref="ruleForm"
        label-width="120px"
        class="demo-ruleForm"
      >
        <el-form-item label="发送日期">
          <el-date-picker
            v-model="ruleForm.value1"
            type="date"
            placeholder="选择日期"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="时间段" prop="type">
          <el-checkbox-group v-model="ruleForm.type">
            <el-checkbox label="上午" name="type"></el-checkbox>
            <el-checkbox label="下午" name="type"></el-checkbox>
            <el-checkbox label="晚上" name="type"></el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="上午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value2"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="下午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value3"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="晚上时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value4"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="modificationVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="modificationVisible = false"
          >ç¡® å®š</el-button
        >
      </span>
    </el-dialog>
    <!-- å†æ¬¡éšè®¿ -->
    <el-dialog title="患者再次随访" :visible.sync="dialogFormVisible">
      <el-form ref="zcform" :rules="zcrules" :model="zcform" label-width="80px">
        <el-form-item label="任务名称">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.taskName"
          ></el-input>
        </el-form-item>
        <el-form-item label="患者名称">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.sendname"
          ></el-input>
        </el-form-item>
        <el-form-item label="年龄">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.age"
          ></el-input>
        </el-form-item>
        <el-form-item label="科室">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.deptname"
          ></el-input>
        </el-form-item>
        <el-form-item label="病区">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.leavehospitaldistrictname"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访方式" prop="resource">
          <el-radio-group v-model="zcform.resource">
            <el-radio label="1">本病区随访</el-radio>
            <el-radio label="2">随访中心随访</el-radio>
          </el-radio-group>
        </el-form-item>
        <!-- <el-form-item label="即刻发送">
          <el-switch v-model="zcform.delivery"></el-switch>
        </el-form-item> -->
        <el-form-item label="出院时间">
          <el-input
            style="width: 400px"
            disabled
            v-model="zcform.endtime"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访完成时间" prop="date1">
          <el-date-picker
            type="date"
            placeholder="选择日期"
            v-model="zcform.date1"
            style="width: 100%"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="随访记录">
          <el-input type="textarea" v-model="zcform.remark"></el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import {
  delUser,
  addUser,
  updateUser,
  resetUserPwd,
  changeUserStatus,
} from "@/api/system/user";
import {
  getTaskservelist,
  buidegetTasklist,
  addserviceSubtask,
  query360PatInfo,
  addsatisfaction,
} from "@/api/AiCentre/index";
import { alterpatient, particularpatient } from "@/api/patient/homepage";
import Treeselect from "@riophae/vue-treeselect";
import store from "@/store";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
  name: "Discharge",
  dicts: ["sys_normal_disable", "sys_user_sex", "sys_yujing", "sys_suggest"],
  components: { Treeselect },
  data() {
    return {
      // é®ç½©å±‚
      loading: true,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      dialogFormVisible: false,
      // æ€»æ¡æ•°
      total: 0,
      // ç”¨æˆ·è¡¨æ ¼æ•°æ®
      userList: null,
      // å¼¹å‡ºå±‚标题
      title: "新增影像随访",
      // æ˜¯å¦æ˜¾ç¤ºä¿®æ”¹ã€æ·»åŠ å¼¹å‡ºå±‚
      addalteropen: false,
      // ä¿®æ”¹å‘送时间对话框
      modificationVisible: false,
      // éƒ¨é—¨åç§°
      deptName: undefined,
      // é»˜è®¤å¯†ç 
      initPassword: undefined,
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      dateRangefs: [],
      // å²—位选项
      postOptions: [],
      ruleForm: {
        type: [],
      },
      zcform: {},
      dynamicTags: ["选项一", "选项二", "选项三"], //选项
      inputVisible: false,
      Labelchange: false,
      ycvalue: "",
      yfsvalue: "",
      inputValue: "",
      preachform: "",
      previewVisible: false, //影像随访预览弹框
      radio: "",
      radios: [],
      previewtype: 2, //预览影像随访类型
      total: 0, // æ€»æ¡æ•°
      // æ»¡æ„åº¦è°ƒæŸ¥æ•°æ®
      scoreDialogVisible: false,
      selectedRows: [],
      value: [],
      list: [],
      sourcetype: [
        {
          value: 1,
          label: "科室",
          children: [],
        },
        {
          value: 2,
          label: "病区",
          children: [],
        },
        {
          value: 3,
          label: "全部",
        },
      ],
      loading: false,
      cardlist: [
        {
          name: "服务总量",
          value: 0,
        },
        // {
        //   name: "患者过滤",
        //   value: 0,
        // },
        {
          name: "需随访",
          value: 0,
        },
        {
          name: "发送失败",
          value: 0,
        },
        {
          name: "待随访",
          value: 0,
        },
        // {
        //   name: "已发送",
        //   value: 0,
        // },
        // {
        //   name: "表单已发送",
        //   value: 0,
        // },
      ],
      zcrules: {
        date1: [
          { required: true, message: "请选择随访方式", trigger: "change" },
        ],
        resource: [
          { required: true, message: "请选择随访时间", trigger: "blur" },
        ],
      },
      // è¡¨å•参数
      form: {
        phonenumber: "",
        totagid: "",
        types: "",
        nickName: "",
        qystatus: "",
        btstatus: "",
      },
      topicoptionssort: [
        {
          value: 0,
          label: "出院时间(正序)",
        },
        {
          value: 1,
          label: "出院时间(倒序)",
        },
        {
          value: 2,
          label: "发送时间(正序)",
        },
        {
          value: 3,
          label: "发送时间(倒序)",
        },
        {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: 6,
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 14,
        searchscope: 3,
        visitCount: 1,
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      },
      propss: { multiple: true },
      options: [],
      topicoptions: [
        {
          value: null,
          label: "全部",
        },
        {
          value: 1,
          label: "表单已领取",
        },
        {
          value: 2,
          label: "待随访",
        },
        {
          value: 3,
          label: "表单已发送",
        },
        {
          value: 4,
          label: "不执行",
        },
        {
          value: 5,
          label: "发送失败",
        },
        {
          value: 6,
          label: "已完成",
        },
      ],
      sextype: [
        {
          value: 1,
          label: "男",
        },
        {
          value: 2,
          label: "女",
        },
      ],
      topicoptionsyj: [
        {
          value: 1,
          label: "异常",
        },
        {
          value: 0,
          label: "正常",
        },
      ],
      url: "http://9.208.2.190:8090/smartor/serviceExternal/query360PatInfo",
      postData: {
        XiaoXiTou: {
          FaSongFCSJC: "ZJHES",
          FaSongJGID: localStorage.getItem("orgid"),
          FaSongJGMC: localStorage.getItem("orgname"),
          FaSongSJ: "2025-01-09 17:29:36",
          FaSongXTJC: "SUIFANGXT",
          FaSongXTMC: "随访系统",
          XiaoXiID: "5FA92AFB-9833-4608-87C7-F56A654AC171",
          XiaoXiLX: "SC_LC_360STCX",
          XiaoXiMC: "360 视图查询",
          ZuHuID: localStorage.getItem("ZuHuID"),
          ZuHuMC: localStorage.getItem("orgname"),
        },
        YeWuXX: {
          BingRenXX: {
            ZhengJianHM: "",
            ZhengJianLXDM: "01",
            ZhengJianLXMC: "居民身份证",
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
          },
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
          },
        },
      },
      amendtag: false,
      errtype: "",
      leavehospitaldistrictcode: "",
      serviceState: [],
      checkboxlist: [],
      // è¡¨å•校验
      rules: {},
    };
  },
  watch: {},
  created() {
    this.serviceState = store.getters.serviceState;
    this.checkboxlist = store.getters.checkboxlist;
    this.errtype = this.$route.query.errtype;
    this.leavehospitaldistrictcode =
      this.$route.query.leavehospitaldistrictcode;
    this.sourcetype[0].children = store.getters.belongDepts.map((dept) => {
      return {
        label: dept.deptName,
        value: dept.deptCode,
      };
    });
    this.sourcetype[1].children = store.getters.belongWards.map((dept) => {
      return {
        label: dept.districtName,
        value: dept.districtCode,
      };
    });
    if (this.errtype) {
      this.toleadExport(2);
    } else {
      this.getList(1);
    }
    this.getConfigKey("sys.user.initPassword").then((response) => {
      this.initPassword = response.msg;
    });
  },
  activated() {
    this.getList(1);
  },
  methods: {
    /** æŸ¥è¯¢éšè®¿æœåŠ¡åˆ—è¡¨ */
    getList(refresh) {
      // é»˜è®¤å…¨éƒ¨
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
        console.log(this.topqueryParams.leavehospitaldistrictcodes, "11");
      }
      this.loading = true;
      if (
        this.topqueryParams.leavehospitaldistrictcodes[0] &&
        this.topqueryParams.leaveldeptcodes[0]
      ) {
        this.topqueryParams.deptOrDistrict = 2;
      } else {
        this.topqueryParams.deptOrDistrict = 1;
      }
      getTaskservelist(this.topqueryParams).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          // this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[1].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[2].value = response.rows[0].fssb;
          this.cardlist[3].value = response.rows[0].dsf;
          // this.cardlist[4].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    affiliation() {
           this.topqueryParams.managementDoctorCode= store.getters.hisUserId;
      this.getList(1);
    },
    onthatday() {
      this.topqueryParams.startSendDateTime = this.getCurrentDate();
      this.topqueryParams.endSendDateTime = this.getCurrentDate();
      this.getList(1);
    },
    getCurrentDate() {
      const now = new Date();
      return now.toISOString().slice(0, 10); // æˆªå–前10个字符,即 YYYY-MM-DD
    },
    buidegetTasklist(type) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
      }
      let obj = {
        pageNum: 1,
        pageSize: 10,
        leavehospitaldistrictcodes:
          this.topqueryParams.leavehospitaldistrictcodes,
        sendstates: [2, 3],
        leaveldeptcodes: this.topqueryParams.leaveldeptcodes,
      };
      buidegetTasklist(obj).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[2].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[3].value = response.rows[0].fssb;
          this.cardlist[4].value = response.rows[0].dsf;
          // this.cardlist[5].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    // æŸ¥çœ‹é—¨è¯Šéšè®¿è¯¦æƒ…
    Referencequestion(row) {
      this.previewVisible = true;
    },
    // æ·»åŠ å¼¹æ¡†æœç´¢
    remoteMethod(query) {
      if (query !== "") {
        this.loading = true;
        setTimeout(() => {
          this.loading = false;
          this.options = this.list.filter((item) => {
            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
          });
        }, 200);
      } else {
        this.options = [];
      }
    },
    // å½±åƒéšè®¿çŠ¶æ€ä¿®æ”¹
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
      this.$modal
        .confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
        .then(function () {
          return changeUserStatus(row.userId, row.status);
        })
        .then(() => {
          this.$modal.msgSuccess(text + "成功");
        })
        .catch(function () {
          row.status = row.status === "0" ? "1" : "0";
        });
    },
    // è¡¨å•重置
    reset() {
      this.form = {
        userId: undefined,
        deptId: undefined,
        userName: undefined,
        nickName: undefined,
        password: undefined,
        phonenumber: undefined,
        email: undefined,
        sex: undefined,
        status: "0",
        remark: undefined,
        postIds: [],
        roleIds: [],
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery(refresh) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      this.topqueryParams.pageNum = 1;
      this.topqueryParams.startOutHospTime = this.dateRange[0];
      this.topqueryParams.endOutHospTime = this.dateRange[1];
      this.topqueryParams.startSendDateTime = this.dateRangefs[0];
      this.topqueryParams.endSendDateTime = this.dateRangefs[1];
      this.getList(refresh);
    },
    // æ‚£è€…范围处理
    handleChange(value) {
      let type = value[0];
      let code = value.slice(-1)[0];
      this.topqueryParams.leavehospitaldistrictcodes = [];
      this.topqueryParams.leaveldeptcodes = [];
      if (type == 1) {
        this.topqueryParams.leaveldeptcodes.push(code);
        this.topqueryParams.leavehospitaldistrictcodes = [];
        this.topqueryParams.searchscope = 1;
      } else if (type == 2) {
        this.topqueryParams.leavehospitaldistrictcodes.push(code);
        this.topqueryParams.leaveldeptcodes = [];
        this.topqueryParams.searchscope = 2;
      } else {
        this.topqueryParams.searchscope = 3;
      }
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.dateRange = [];
      this.dateRangefs = [];
      this.topqueryParams = {
        pageNum: 1,
        pageSize: 10,
        sendstate: 6,
        sort: 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        serviceType: 14,
        searchscope: 3,
        visitCount: 1,
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      };
      this.handleQuery(1);
    },
    handleSelectionChange(rows) {
      this.selectedRows = rows.map((row) => {
        // åˆå§‹åŒ–评分字段
        return {
          ...row,
          authenticity: row.authenticity || 0,
          weekFinish: row.weekFinish || 0,
          standard: row.standard || 0,
          timeliness: row.timeliness || 0,
          library: row.library || 0,
          environment: row.environment || 0,
          doctorSatisfaction: row.doctorSatisfaction || 0,
          nurseSatisfaction: row.nurseSatisfaction || 0,
        };
      });
      if (this.selectedRows.length > 0) {
        this.multiple = false;
      } else {
        this.multiple = true;
      }
    },
    // è®¡ç®—总分
    calculateTotal(row) {
      return (
        (row.authenticity || 0) +
        (row.weekFinish || 0) +
        (row.standard || 0) +
        (row.timeliness || 0) +
        (row.library || 0) +
        (row.environment || 0) +
        (row.doctorSatisfaction || 0) +
        (row.nurseSatisfaction || 0)
      );
    },
    // ä¿å­˜è¯„分
    saveScores() {
      this.selectedRows.forEach((item) => {
        item.createBy = null;
        item.patName = item.sendname;
        item.hospitaldistrictname = item.leavehospitaldistrictname;
      });
      addsatisfaction(this.selectedRows).then((res) => {
        if (res.code == 200) {
          this.$message.success("评分保存成功");
          this.scoreDialogVisible = false;
          this.selectedRows = [];
          this.$refs.userform.clearSelection();
        } else {
          this.$modal.msgWarning("评分保存失败");
          this.scoreDialogVisible = false;
          this.selectedRows = [];
          this.$refs.userform.clearSelection();
        }
      });
      // è¿™é‡Œå¯ä»¥æ·»åŠ ä¿å­˜é€»è¾‘ï¼Œå¦‚è°ƒç”¨API保存评分
    },
    //删除选项
    handleClose(tag) {
      this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1);
    },
    //触发新增输入
    showInput() {
      this.inputVisible = true;
      this.$nextTick((_) => {
        this.$refs.saveTagInput.$refs.input.focus();
      });
    },
    //获取失去焦点触发
    handleInputConfirm() {
      let inputValue = this.inputValue;
      if (inputValue) {
        this.dynamicTags.push(inputValue);
      }
      this.inputVisible = false;
      this.inputValue = "";
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
      this.$router.push({
        path: "/followvisit/QuestionnaireTask",
        query: {
          type: 2,
          serviceType: 14,
        },
      });
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning("360功能暂未开通");
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
          // this.linkUrl = res.data.url;
        } else {
          this.$modal.msgWarning("360查询无结果");
        }
      });
    },
    /** é‡ç½®å¯†ç æŒ‰é’®æ“ä½œ */
    handleResetPwd(row) {
      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        closeOnClickModal: false,
        inputPattern: /^.{5,20}$/,
        inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
      })
        .then(({ value }) => {
          resetUserPwd(row.userId, value).then((response) => {
            this.$modal.msgSuccess("修改成功,新密码是:" + value);
          });
        })
        .catch(() => {});
    },
    // å–消按钮
    cancel() {
      this.Labelchange = false;
      this.reset();
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function () {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          this.form.isoperation = 2;
          this.form.notrequiredFlag = 1;
          alterpatient(this.form)
            .then((response) => {
              console.log(response);
            })
            .then(() => {
              this.getList(1);
              this.$modal.msgSuccess("患者过滤成功");
            });
          this.reset();
          this.Labelchange = false;
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const userIds = row.userId || this.ids;
      this.$modal
        .confirm('是否确认删除用户编号为"' + userIds + '"的数据项?')
        .then(function () {
          return delUser(userIds);
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨åœæ­¢
    AllStop() {
      this.$modal
        .confirm("是否停止全部任务?")
        .then(function () {
          return console.log("停止成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgWarning("停止成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨å¼€å§‹
    AllStarted() {
      this.$modal
        .confirm("是否开启全部任务?")
        .then(function () {
          return console.log("开启成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("开启成功");
        })
        .catch(() => {});
    },
    // ä»»åŠ¡é‡ç½®
    TaskReset() {
      this.$modal
        .confirm("是否重置选中的任务项?")
        .then(function () {
          return console.log("选中成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("重置成功");
        })
        .catch(() => {});
    },
    // è®¾ç½®å‘送时间
    Sendtimesetting() {
      this.modificationVisible = true;
    },
    // è·³è½¬è¯¦æƒ…页
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
        if (row.type == 1) {
          type = 1;
        }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
          taskid: row.taskid,
          patid: row.patid,
          id: row.id,
          Voicetype: type,
          visitCount: this.topqueryParams.visitCount,
        },
      });
    },
    onSubmit() {},
    // æ‚£è€…过滤触发
    handleUpdate(row) {
      particularpatient(row.patid).then((response) => {
        this.form = response.data;
        this.form.filterDrname = store.getters.nickName;
      });
      this.amendtag = true;
      this.Labelchange = true;
    },
    // ä¾¿æ·æŒ‰é’®
    toleadExport(too) {
      if (too == 1) {
        this.topqueryParams.sendstate = 4;
        this.topqueryParams.excep = null;
      } else if (too == 2) {
        this.topqueryParams.excep = 1;
      }
      this.handleQuery();
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      this.topqueryParams.pageNum = null;
      this.topqueryParams.pageSize = null;
      this.download(
        "smartor/serviceSubtask/patItemExport",
        {
          ...this.topqueryParams,
        },
        `user_${new Date().getTime()}.xlsx`
      );
    },
    // å¼‚常列渲染
    tableRowClassName({ row, rowIndex }) {
      if (row.excep == 1) {
        return "warning-row";
      }
      return "";
    },
    getCurrentTime() {
      const now = new Date();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, "0");
      const day = String(now.getDate()).padStart(2, "0");
      const hours = String(now.getHours()).padStart(2, "0");
      const minutes = String(now.getMinutes()).padStart(2, "0");
      const seconds = String(now.getSeconds()).padStart(2, "0");
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
  },
};
</script>
<style lang="scss" scoped>
.el-button--primary.is-plain {
  color: #ffffff;
  background: #409eff;
  border-color: #4fabe9;
}
.document {
  // width: 100px;
  height: 50px;
}
::v-deep.el-table .warning-row {
  background: #eec4c4;
}
.documentf {
  display: flex;
  justify-content: flex-end;
}
.download {
  text-align: center;
  .el-upload__tip {
    font-size: 23px;
  }
  .el-upload__text {
    font-size: 23px;
  }
}
.uploading {
  margin-top: 20px;
  margin: 20px;
  padding: 30px;
  background: #ffffff;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
}
.el-tag + .el-tag {
  margin-left: 10px;
}
.button-new-tag {
  margin-left: 10px;
  height: 32px;
  line-height: 30px;
  padding-top: 0;
  padding-bottom: 0;
}
.input-new-tag {
  width: 90px;
  margin-left: 10px;
  vertical-align: bottom;
}
.drexamine {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  background: #daeaf5;
  img {
    width: 100px;
    height: 100px;
  }
}
.button-textxga {
  color: #de7897;
}
.qrcode-dialo {
  // text-align: center;
  //   display: flex;
  margin: 20px;
  padding: 30px;
  background: #edf1f7;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
  .topic-dev {
    margin-bottom: 25px;
    font-size: 20px !important;
    .dev-text {
      margin-bottom: 10px;
    }
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
  background: #fdd0d7;
}
::v-deep.errleftvlue .el-card__body:hover {
  background: #f88d96;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.ysfleftvlue .el-card__body {
  background: #d0fdd8;
}
::v-deep.ysfleftvlue .el-card__body:hover {
  background: #8df8a4;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-bb {
  font-weight: 500;
  background-color: #2ba05c;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-xq {
  font-weight: 500;
  background-color: #409eff;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-sc {
  font-weight: 500;
  background-color: #b3a21f;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-zx {
  background: #4fabe9;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
::v-deep.el-radio-group {
  span {
    font-size: 24px;
  }
}
.purple-button {
  background-color: #7e22ce;
  border-color: #7e22ce;
  color: #fff;
}
.purple-button:hover,
.purple-button:focus {
  background-color: #9333ea;
  border-color: #9333ea;
}
.purple-button:active {
  background-color: #6b21a8;
  border-color: #6b21a8;
}
.purple-button.is-disabled {
  background-color: #d8b4fe;
  border-color: #d8b4fe;
  opacity: 1; /* ä¿æŒç¦ç”¨çŠ¶æ€é€æ˜Žåº¦ */
}
// é€‰é¡¹å­—体放大
// ::v-deep.el-checkbox-group {
//   span {
//     font-size: 24px;
//   }
// }
</style>
src/views/followvisit/outpatient/index.vue
@@ -138,8 +138,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -739,7 +738,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
@@ -1131,12 +1130,9 @@
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
          console.log(type, "rwo");
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1273,10 +1269,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
src/views/followvisit/record/detailpage/index.vue
@@ -18,7 +18,7 @@
                >只展示本次服务信息</el-button
              >
            </div>
            <div style="margin-left: 20px; color: #59a0f0">
            <!-- <div style="margin-left: 20px; color: #59a0f0">
              <el-link
                href="https://9.208.2.207:6060/search-homepage"
                target="_blank"
@@ -26,7 +26,7 @@
              >
                å‰å¾€CDSS查询
              </el-link>
            </div>
            </div> -->
            <div class="merge-controls" v-if="Whetherall">
              <el-button
                type="primary"
@@ -52,6 +52,7 @@
        <el-table
          :data="logsheetlist"
          :row-class-name="tableRowClassName"
          :max-height="350"
          style="width: 100%"
           height="350"
          @selection-change="handleSelectionChange"
@@ -255,6 +256,7 @@
      />
    </el-dialog>
    <div class="action-container">
      <!-- éšè®¿å†…容 -->
      <div class="call-action">
        <div class="call-container">
          <!-- <div class="call-header">
@@ -295,29 +297,28 @@
                          }}</span>
                        </div>
                        <div class="dev-xx">
                          <el-radio-group
                            v-model="item.scriptResult"
                            @change="
                              handleOptionChange(
                                $event,
                                index,
                                item.svyLibTemplateTargetoptions,
                                item
                              )
                            "
                          >
                          <el-radio-group v-model="item.scriptResult">
                            <el-radio
                              v-for="(
                                items, indexs
                              ) in item.svyLibTemplateTargetoptions"
                              ) in item.svyTaskTemplateTargetoptions"
                              :class="items.isabnormal ? 'red-star' : ''"
                              :key="indexs"
                              :label="items.optioncontent"
                              @click.native.prevent="
                                handleRadioToggle(
                                  item,
                                  index,
                                  item.svyTaskTemplateTargetoptions,
                                  items.optioncontent
                                )
                              "
                              >{{ items.optioncontent }}</el-radio
                            >
                          </el-radio-group>
                        </div>
                        <div
                          v-if="item.showAppendInput || item.answerps"
                          v-if="item.showAppendInput || item.answerps"
                          class="append-input-container"
                        >
@@ -359,7 +360,7 @@
                              @change="$forceUpdate()"
                              v-for="(
                                items, indexs
                              ) in item.svyLibTemplateTargetoptions"
                              ) in item.svyTaskTemplateTargetoptions"
                              :key="indexs"
                              :label="items.optioncontent"
                            >
@@ -382,8 +383,18 @@
                          {{ index + 1 }}、[问答]<span>{{
                            item.scriptContent
                          }}</span>
                          <span v-if="item.valueType == 3">(只能输入数字)</span>
                        </div>
                        <div class="dev-xx">
                        <div class="dev-xx" v-if="item.valueType == 3">
                          <el-input
                            type="text"
                            v-numeric-only
                            placeholder="请输入答案"
                            v-model="item.scriptResult"
                          >
                          </el-input>
                        </div>
                        <div class="dev-xx" v-else>
                          <el-input
                            type="textarea"
                            :rows="2"
@@ -423,8 +434,9 @@
                          >
                            <el-radio
                              v-for="(items, index) in item.scriptResult"
                              :key="items"
                              :key="index"
                              :label="items"
                              :class="items.isabnormal ? 'red-star' : ''"
                              >{{ items }}</el-radio
                            >
                          </el-radio-group>
@@ -438,15 +450,25 @@
                      <div class="scriptTopic-dev" :key="index" v-else>
                        <div class="dev-text">
                          {{ index + 1 }}、[问答]<span>{{
                            item.questiontext
                            item.scriptContent
                          }}</span>
                          <span v-if="item.valueType == 3">(只能输入数字)</span>
                        </div>
                        <div class="dev-xx">
                        <div class="dev-xx" v-if="item.valueType == 3">
                          <el-input
                            type="text"
                            v-numeric-only
                            placeholder="请输入答案"
                            v-model="item.scriptResult"
                          >
                          </el-input>
                        </div>
                        <div class="dev-xx" v-else>
                          <el-input
                            type="textarea"
                            :rows="2"
                            placeholder="请输入答案"
                            v-model="item.matchedtext"
                            v-model="item.scriptResult"
                            clearable
                          >
                          </el-input>
@@ -530,6 +552,7 @@
          </div>
        </div>
      </div>
      <!-- äººå·¥å¤„理 -->
      <div class="manual-action">
        <div class="Followuserinfos">
          <div>
@@ -557,7 +580,47 @@
                    @click="sendAgain()"
                    >再次随访</el-button
                  >
                </div> -->
                </div>
                <div class="tag-selector-container">
                  <el-select
                    v-model="selectedTag"
                    placeholder="请选择异常状态"
                    clearable
                    style="width: 150px; margin-right: 10px"
                  >
                    <el-option
                      v-for="item in tagOptions"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                      <span style="display: flex; align-items: center">
                        <span
                          class="color-indicator"
                          :style="{ backgroundColor: item.color }"
                        ></span>
                        <span>{{ item.label }}</span>
                      </span>
                    </el-option>
                  </el-select>
                  <!-- å½“前选择的颜色指示器 -->
                  <div
                    v-if="selectedTag"
                    class="color-indicator selected-indicator"
                    :style="{ backgroundColor: getSelectedTagColor() }"
                  ></div>
                  <!-- æ ‡è®°è¯´æ˜Žæç¤º -->
                  <el-tooltip
                    v-if="selectedTag"
                    effect="light"
                    :content="getSelectedDescription()"
                    placement="top"
                  >
                    <i class="el-icon-info tag-info-icon"></i>
                  </el-tooltip>
                </div>
              </div>
              <el-row>
                <el-col :span="14"
@@ -626,10 +689,24 @@
                  />
                </div>
              </div>
              <el-form-item label="随访记录">
              <el-form-item label="随访内容" v-if="orgname == '丽水市中医院'">
                <el-input type="textarea" v-model="form.remark"></el-input>
              </el-form-item>
              <el-form-item label="随访记录" v-else>
                <el-input type="textarea" v-model="form.remark"></el-input>
              </el-form-item>
              <el-form-item label="随访情况" v-if="orgname == '丽水市中医院'">
                <el-radio-group v-model="form.taskSituation">
                  <el-radio
                    v-for="city in cities"
                    :label="city.value"
                    :value="city.value"
                    :key="city.value"
                    >{{ city.label }}</el-radio
                  >
                </el-radio-group>
              </el-form-item>
              <el-form-item label="处理意见">
                <div>
                  <el-button
@@ -653,6 +730,9 @@
                  <el-button plain type="info" @click="Editsingletaskson('5')"
                    >中心随访</el-button
                  >
                  <el-button type="primary" round @click="sendAgainmsg"
                    >短信发送</el-button
                  >
                </div>
              </el-form-item>
            </el-form>
@@ -672,6 +752,24 @@
                </el-row>
                <el-row :gutter="20">
                  <el-col :span="12"
                    ><el-form-item label="性别" prop="telcode">
                      <el-select v-model="userform.sex" placeholder="请选择">
                        <el-option label="男" :value="1"> </el-option>
                        <el-option label="女" :value="2"> </el-option>
                      </el-select> </el-form-item
                  ></el-col>
                  <el-col :span="12">
                    <el-form-item label="年龄" prop="name">
                      <el-input
                        v-model="userform.age"
                        placeholder="请输入姓名"
                        maxlength="20"
                      ></el-input> </el-form-item
                  ></el-col>
                </el-row>
                <el-row :gutter="20">
                  <el-col :span="12"
                    ><el-form-item label="联系方式" prop="telcode">
                      <el-input
                        v-model="userform.telcode"
@@ -685,6 +783,16 @@
                        v-model="userform.relativetelcode"
                        placeholder="请输入姓名"
                        maxlength="20"
                      ></el-input> </el-form-item
                  ></el-col>
                </el-row>
                <el-row :gutter="20">
                  <el-col :span="24">
                    <el-form-item label="诊断名称" prop="name">
                      <el-input
                        v-model="form.leavediagname"
                        placeholder="请输入诊断"
                        maxlength="50"
                      ></el-input> </el-form-item
                  ></el-col>
                </el-row>
@@ -714,7 +822,53 @@
        </div>
      </div>
    </div>
    <!-- çŸ­ä¿¡å‘送对话框 -->
    <el-dialog title="短信发送" :visible.sync="smsDialogVisible">
      <!-- æ³¨æ„è¿™é‡Œä½¿ç”¨äº† smsDialogVisible ä»¥åŒºåˆ†å·²æœ‰çš„ dialogFormVisible -->
      <el-form ref="smsForm" :model="form" label-width="80px">
        <el-form-item label="患者名称">
          <el-input
            style="width: 400px"
            disabled
            v-model="form.sendname"
          ></el-input>
        </el-form-item>
        <el-form-item label="年龄">
          <el-input style="width: 400px" disabled v-model="form.age"></el-input>
        </el-form-item>
        <el-form-item label="电话">
          <el-input
            style="width: 400px"
            disabled
            v-model="userform.telcode"
          ></el-input>
          <!-- æ³¨æ„è¿™é‡Œå¯èƒ½ä½¿ç”¨ userform.telcode -->
        </el-form-item>
        <el-form-item label="科室">
          <el-input
            style="width: 400px"
            disabled
            v-model="form.deptname"
          ></el-input>
        </el-form-item>
        <el-form-item label="病区">
          <el-input
            style="width: 400px"
            disabled
            v-model="form.leavehospitaldistrictname"
          ></el-input>
        </el-form-item>
        <el-form-item label="短信内容">
          <el-input type="textarea" v-model="smsContent"></el-input>
          <!-- å»ºè®®ä½¿ç”¨ç‹¬ç«‹çš„ smsContent å˜é‡ -->
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="smsDialogVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="sendSms">确认发送</el-button>
        <!-- æ³¨æ„æ–¹æ³•名改为 sendSms -->
      </div>
    </el-dialog>
    <el-dialog
      title="患者再次随访"
      v-dialogDrags
@@ -810,14 +964,14 @@
        <el-divider></el-divider>
        <el-row>
          <el-col :span="12">
            <el-form-item label="随访方式" prop="date1">
            <el-form-item label="随访方式" prop="visitType">
              <el-select
                v-model="form.visitType"
                filterable
                allow-create
                default-first-option
                @change="visitChange"
                placeholder="请选择随访方式(依出院时间技计算)"
                placeholder="请选择随访方式(依出院时间计算)"
              >
                <el-option
                  v-for="item in options"
@@ -838,6 +992,8 @@
                align="right"
                v-model="form.date1"
                class="custom-disabled"
                value-format="yyyy-MM-dd"
                @change="checkFollowupDate"
              ></el-date-picker>
            </el-form-item>
          </el-col>
@@ -860,6 +1016,18 @@
        <el-button type="primary" @click="setupsubtask">确认创建服务</el-button>
      </div>
    </el-dialog>
    <div class="main-content" v-if="orgname == '景宁畲族自治县人民医院'">
      <!-- <el-button @click="CaldialogVisible = true">打开弹框</el-button> -->
      <!-- å¼¹æ¡†è°ƒç”¨ -->
      <el-dialog
        title="呼叫功能框"
        :visible.sync="CaldialogVisible"
        width="60%"
      >
        <CallCenterLs ref="CallCenterLs" :initial-phone="currentPhoneNumber" />
      </el-dialog>
    </div>
  </div>
</template>
@@ -876,6 +1044,7 @@
  updatePersonVoices,
  addPersonVoices,
  query360PatInfo,
  sendMsg,
} from "@/api/AiCentre/index";
import {
  messagelistpatient,
@@ -884,12 +1053,74 @@
} from "@/api/patient/homepage";
import CallButton from "@/components/CallButton";
import MergeAndModify from "./MergeAndModify.vue";
import CallCenterLs from "@/components/CallCenterLs";
export default {
  components: {
    CallButton,
    MergeAndModify,
    CallCenterLs,
  },
  directives: {
    numericOnly: {
      bind(el, binding, vnode) {
        // å°è¯•获取实际的input元素
        const input = el.tagName === "INPUT" ? el : el.querySelector("input");
        if (!input) {
          console.warn("v-numeric-only: æœªæ‰¾åˆ°input元素");
          return;
        }
        const handleInput = function (event) {
          const oldValue = input.value;
          const newValue = oldValue.replace(/[^\d]/g, "");
          if (newValue !== oldValue) {
            input.value = newValue;
            // è§¦å‘input事件,通知v-model更新
            input.dispatchEvent(new Event("input", { bubbles: true })); // æ³¨æ„bubbles
          }
        };
        const handlePaste = function (event) {
          event.preventDefault();
          const clipboardData = event.clipboardData || window.clipboardData;
          const pastedData = clipboardData.getData("text");
          const numericValue = pastedData.replace(/[^\d]/g, "");
          // æ¨¡æ‹Ÿåœ¨å…‰æ ‡ä½ç½®æ’入纯数字文本
          const start = input.selectionStart;
          const end = input.selectionEnd;
          input.value =
            input.value.substring(0, start) +
            numericValue +
            input.value.substring(end);
          // è°ƒæ•´å…‰æ ‡ä½ç½®
          const newCursorPos = start + numericValue.length;
          input.setSelectionRange(newCursorPos, newCursorPos);
          // è§¦å‘input事件
          input.dispatchEvent(new Event("input", { bubbles: true }));
        };
        input.addEventListener("input", handleInput);
        input.addEventListener("paste", handlePaste);
        // å­˜å‚¨å¼•用以便解绑
        el._numericOnly = {
          inputHandle: handleInput,
          pasteHandle: handlePaste,
          inputEl: input,
        };
      },
      unbind(el) {
        if (el._numericOnly) {
          const { inputHandle, pasteHandle, inputEl } = el._numericOnly;
          inputEl.removeEventListener("input", inputHandle);
          inputEl.removeEventListener("paste", pasteHandle);
          delete el._numericOnly;
        }
      },
    },
  },
  dicts: ["sys_normal_disable", "sys_user_sex", "sys_yujing", "sys_suggest"],
  data() {
    const validatePhone = (rule, value, callback) => {
@@ -911,7 +1142,11 @@
      // å·²æœ‰æ•°æ®...
      callStatus: "idle", // idle, calling, connected, ended, failed
      isEndingCall: false,
      CaldialogVisible: false,
      currentCall: null, // å½“前通话对象
      // è·¯ç”±ç›‘听相关
      routeWatcher: null,
      lastRoutePath: this.$route.path,
      input: "今天身体还不错",
      radio: "2",
      taskname: "",
@@ -919,19 +1154,86 @@
      voice: "",
      templateid: "",
      again: "",
      orgname: "",
      zcform: {},
      form: {},
      cities: [
        {
          label: "正常语音",
          value: "1",
        },
        {
          label: "患者拒接或拒访",
          value: "2",
        },
        {
          label: "面访或者接诊",
          value: "3",
        },
        {
          label: "微信随访",
          value: "4",
        },
        {
          label: "随访电话不正确",
          value: "5",
        },
        {
          label: "其他情况不宜随访",
          value: "6",
        },
      ],
      tableDatatop: [], //题目表
      voiceDatatop: [], //题目表
      dynamicTags: [],
      isMergeMode: false,
      mergeDialogVisible: false,
      selectedServices: [], // é€‰ä¸­çš„æœåŠ¡åˆ—è¡¨
      selectedTag: "",
      tagOptions: [
        {
          value: "0",
          label: "正常",
          type: "normal",
          color: "#7ff5e1",
          description: "患者情况正常,无需特别关注",
        },
        {
          value: "1",
          label: "异常",
          type: "abnormal",
          color: "#f75c5c",
          description: "患者存在异常情况,需要重点关注",
        },
        {
          value: "2",
          label: "警告",
          type: "warning",
          color: "#fbfb4a",
          description: "患者情况需要警告注意,可能存在风险",
        },
      ],
      zcrules: {
        resource: [
          { required: true, message: "请选择随访方式", trigger: "change" },
        ],
        date1: [{ required: true, message: "请选择随访时间", trigger: "blur" }],
        date1: [
          { required: true, message: "请选择随访时间", trigger: "blur" },
          {
            validator: (rule, value, callback) => {
              if (!value) {
                return callback(new Error("请选择随访时间"));
              }
              const selectedDate = new Date(value);
              const now = new Date();
              if (selectedDate < now) {
                return callback(new Error("随访时间不能早于当前日期"));
              }
              callback();
            },
            trigger: "change",
          },
        ],
      },
      userrules: {
        telcode: [{ validator: validatePhone, trigger: "blur" }],
@@ -963,8 +1265,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -976,56 +1278,56 @@
          // ç¦ç”¨ä»Šå¤©åŠä¹‹å‰çš„æ—¥æœŸ
          return time.getTime() < Date.now() - 24 * 60 * 60 * 1000;
        },
        shortcuts: [
          {
            text: "七天后",
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() + 3600 * 1000 * 24 * 7);
              picker.$emit("pick", date);
            },
          },
          {
            text: "15天后",
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() + 3600 * 1000 * 24 * 15);
              picker.$emit("pick", date);
            },
          },
          {
            text: "一个月后",
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() + 3600 * 1000 * 24 * 30);
              picker.$emit("pick", date);
            },
          },
          {
            text: "三个月后",
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() + 3600 * 1000 * 24 * 90);
              picker.$emit("pick", date);
            },
          },
          {
            text: "六个月后",
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() + 3600 * 1000 * 24 * 180);
              picker.$emit("pick", date);
            },
          },
          {
            text: "一年后",
            onClick(picker) {
              const date = new Date();
              date.setTime(date.getTime() + 3600 * 1000 * 24 * 365);
              picker.$emit("pick", date);
            },
          },
        ],
        // shortcuts: [
        //   {
        //     text: "七天后",
        //     onClick(picker) {
        //       const date = new Date();
        //       date.setTime(date.getTime() + 3600 * 1000 * 24 * 7);
        //       picker.$emit("pick", date);
        //     },
        //   },
        //   {
        //     text: "15天后",
        //     onClick(picker) {
        //       const date = new Date();
        //       date.setTime(date.getTime() + 3600 * 1000 * 24 * 15);
        //       picker.$emit("pick", date);
        //     },
        //   },
        //   {
        //     text: "一个月后",
        //     onClick(picker) {
        //       const date = new Date();
        //       date.setTime(date.getTime() + 3600 * 1000 * 24 * 30);
        //       picker.$emit("pick", date);
        //     },
        //   },
        //   {
        //     text: "三个月后",
        //     onClick(picker) {
        //       const date = new Date();
        //       date.setTime(date.getTime() + 3600 * 1000 * 24 * 90);
        //       picker.$emit("pick", date);
        //     },
        //   },
        //   {
        //     text: "六个月后",
        //     onClick(picker) {
        //       const date = new Date();
        //       date.setTime(date.getTime() + 3600 * 1000 * 24 * 180);
        //       picker.$emit("pick", date);
        //     },
        //   },
        //   {
        //     text: "一年后",
        //     onClick(picker) {
        //       const date = new Date();
        //       date.setTime(date.getTime() + 3600 * 1000 * 24 * 365);
        //       picker.$emit("pick", date);
        //     },
        //   },
        // ],
      },
      options: [
        {
@@ -1054,6 +1356,8 @@
        },
      ],
      userform: {},
      smsDialogVisible: false, // æŽ§åˆ¶çŸ­ä¿¡å¯¹è¯æ¡†æ˜¾ç¤º
      smsContent: "", // å­˜å‚¨çŸ­ä¿¡å†…容
      Whetherall: true, //是否全部记录展示
      dialogFormVisible: false,
      Voicetype: 0, //是否为语音服务
@@ -1067,6 +1371,7 @@
      patid: null,
    };
  },
  computed: {
    callStatusText() {
      const statusMap = {
@@ -1098,6 +1403,7 @@
    this.Voicetype = this.$route.query.Voicetype;
    this.visitCount = this.$route.query.visitCount;
    this.serviceType = this.$route.query.serviceType;
    this.orgname = localStorage.getItem("orgname");
    this.getTaskservelist();
  },
@@ -1142,11 +1448,10 @@
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning("360功能暂未开通");
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1160,7 +1465,7 @@
    getuserinfo() {
      const queryParams = {
        pid: Number(this.patid),
        allhosp: "0",
        allhosp: "0", //1住院2门诊3体检4出院
      };
      // æ‚£è€…基础信息
      messagelistpatient(queryParams).then((response) => {
@@ -1179,35 +1484,64 @@
    },
    // å†æ¬¡éšè®¿æ—¶é—´é€‰å–
    visitChange(value) {
      // æ ¹æ®é€‰æ‹©çš„随访方式设置时间
      if (!this.form.endtime) {
        this.$message.warning("请先确认出院时间");
        this.form.visitType = "";
        this.$refs.zcform.clearValidate(["visitType"]);
        return;
      }
      const dischargeDate = new Date(this.form.endtime);
      const now = new Date();
      let followupDate = new Date(dischargeDate);
      // æ ¹æ®é€‰æ‹©çš„随访方式计算随访日期
      if (value.includes("七天后")) {
        this.form.date1 = new Date(
          Date.parse(this.form.endtime) + 3600 * 1000 * 24 * 7
        );
        followupDate.setDate(dischargeDate.getDate() + 7);
      } else if (value.includes("15天后")) {
        this.form.date1 = new Date(
          Date.parse(this.form.endtime) + 3600 * 1000 * 24 * 15
        );
        followupDate.setDate(dischargeDate.getDate() + 15);
      } else if (value.includes("一个月后")) {
        this.form.date1 = new Date(
          Date.parse(this.form.endtime) + 3600 * 1000 * 24 * 30
        );
        followupDate.setMonth(dischargeDate.getMonth() + 1);
      } else if (value.includes("三个月后")) {
        this.form.date1 = new Date(
          Date.parse(this.form.endtime) + 3600 * 1000 * 24 * 90
        );
        followupDate.setMonth(dischargeDate.getMonth() + 3);
      } else if (value.includes("六个月后")) {
        this.form.date1 = new Date(
          Date.parse(this.form.endtime) + 3600 * 1000 * 24 * 180
        );
        followupDate.setMonth(dischargeDate.getMonth() + 6);
      } else if (value.includes("一年后")) {
        this.form.date1 = new Date(
          Date.parse(this.form.endtime) + 3600 * 1000 * 24 * 365
        followupDate.setFullYear(dischargeDate.getFullYear() + 1);
      }
      if (followupDate < now) {
        this.$message.warning(
          `计算出的随访时间 ${this.formatTime(followupDate)} å·²è¿‡æœŸ`
        );
        this.form.visitType = "";
        this.$refs.zcform.clearValidate(["visitType", "date1"]);
        return;
      }
      this.form.date1 = this.formatTime(followupDate);
      this.$refs.zcform.clearValidate(["date1"]);
    },
    // æ£€æŸ¥éšè®¿æ—¥æœŸæ˜¯å¦æœ‰æ•ˆ
    checkFollowupDate(date) {
      console.log(date);
      if (!date) {
        this.form.date1 = "";
        this.$refs.zcform.clearValidate(["date1"]);
        return;
      }
      const selectedDate = new Date(date);
      const now = new Date();
      if (selectedDate < now) {
        this.$message.warning("随访时间不能早于当前日期");
        this.form.date1 = "";
        this.$refs.zcform.validateField("date1"); // è§¦å‘验证
      }
    },
    // èŽ·å–è¯­éŸ³æ•°æ®
    getPersonVoices(id) {
      let obj = {
@@ -1267,14 +1601,25 @@
      let excep = "";
      const promises = [];
      this.tableDatatop.forEach((item) => {
        var objs = item.svyLibTemplateTargetoptions.find(
          (items) => items.optioncontent == item.scriptResult
        );
        if (obj) {
          if (objs.isabnormal) {
            excep = 1;
        if (item.valueType == 3 && item.scriptResult) {
          // éªŒè¯æ˜¯å¦ä¸ºæœ‰æ•ˆæ•°å­—
          if (!/^\d+$/.test(item.scriptResult)) {
            this.$message.error(`问题 "${item.scriptContent}" å¿…须输入数字`);
            return;
          }
        }
        var objs = item.svyTaskTemplateTargetoptions.find(
          (items) => items.optioncontent == item.scriptResult
        );
        if (objs) {
          if (excep != 1 && objs.isabnormal) {
            excep = objs.isabnormal;
            this.selectedTag = objs.isabnormal;
          }
        }
        console.log(excep, "excep");
        let obj = {
          asrtext: null,
          patid: this.patid,
@@ -1297,6 +1642,7 @@
          promises.push(serviceSubtaskDetailadd(obj));
        }
      });
      // ä½¿ç”¨ Promise.all ç­‰å¾…所有异步操作完成
      Promise.all(promises)
        .then((results) => {
@@ -1308,49 +1654,49 @@
          });
          this.Editsingletasksonyic(6);
          // this.$modal
          //   .confirm(
          //     '任务保存成功是否针对患者:"' +
          //       this.logsheetlist[0].sendname +
          //       '"再次随访?',
          //     "确认",
          //     {
          //       confirmButtonText: "确定",
          //       cancelButtonText: "取消",
          //       showCancelButton: true,
          //       dangerouslyUseHTMLString: true,
          //       confirmButtonClass: "custom-confirm-button", // è‡ªå®šä¹‰ç¡®è®¤æŒ‰é’®çš„类名
          //       cancelButtonClass: "custom-cancel-button", // è‡ªå®šä¹‰å–消按钮的类名
          //     }
          //   )
          //   .then(() => {
          //     document.querySelector("#app").scrollTo(0, 0);
          //     this.formtidy();
          //     this.dialogFormVisible = true;
          //   })
          //   .catch(() => {
          //     if (this.form.serviceType == 13) {
          //       if (this.visitCount != 1) {
          //         this.$router.push({
          //           path: "/logisticsservice/zbAgain",
          //         });
          //       } else {
          //         this.$router.push({
          //           path: "/logisticsservice/record",
          //         });
          //       }
          //     } else if (this.form.serviceType == 2) {
          //       if (this.visitCount != 1) {
          //         this.$router.push({
          //           path: "/logisticsservice/again",
          //         });
          //       } else {
          //         this.$router.push({
          //           path: "/followvisit/discharge",
          //         });
          //       }
          //     }
          //   });
          this.$modal
            .confirm(
              '任务保存成功是否针对患者:"' +
                this.userform.name +
                '"再次随访?',
              "确认",
              {
                confirmButtonText: "确定",
                cancelButtonText: "取消",
                showCancelButton: true,
                dangerouslyUseHTMLString: true,
                confirmButtonClass: "custom-confirm-button", // è‡ªå®šä¹‰ç¡®è®¤æŒ‰é’®çš„类名
                cancelButtonClass: "custom-cancel-button", // è‡ªå®šä¹‰å–消按钮的类名
              }
            )
            .then(() => {
              document.querySelector("#app").scrollTo(0, 0);
              this.formtidy();
              this.dialogFormVisible = true;
            })
            .catch(() => {
              if (this.form.serviceType == 13) {
                if (this.visitCount != 1) {
                  this.$router.push({
                    path: "/logisticsservice/zbAgain",
                  });
                } else {
                  this.$router.push({
                    path: "/logisticsservice/record",
                  });
                }
              } else if (this.form.serviceType == 2) {
                if (this.visitCount != 1) {
                  this.$router.push({
                    path: "/logisticsservice/again",
                  });
                } else {
                  this.$router.push({
                    path: "/followvisit/discharge",
                  });
                }
              }
            });
        })
        .catch((error) => {
          // å¦‚果有任何一个异步操作失败,会进入这里
@@ -1398,8 +1744,13 @@
        this.$message.error("请输入正确的手机号码");
        return;
      }
      this.currentPhoneNumber = phone;
      // å‘¼å«åˆ¤æ–­
      if (this.orgname == "景宁畲族自治县人民医院") {
        this.CaldialogVisible = true;
        return;
      }
      this.callType = type;
      this.callStatus = "calling";
@@ -1446,7 +1797,11 @@
      }, 3000);
    },
    yuyingetdetail() {
      this.tableDatatop.forEach((item, index) => {
      const dataToSubmit = JSON.parse(JSON.stringify(this.tableDatatop));
      console.log(dataToSubmit, "dataToSubmit");
      return;
      dataToSubmit.forEach((item, index) => {
        // å¯¹æ‹·è´çš„æ•°æ®è¿›è¡Œæ“ä½œï¼Œä¸å½±å“åŽŸå§‹çš„ scriptResult æ•°ç»„
        item.scriptResult = item.scriptResult.join("&");
        item.templatequestionnum = index + 1;
        item.subId = this.id;
@@ -1458,8 +1813,9 @@
        item.patid = this.patid;
        item.templateid = item.templateID;
      });
      let obj = {
        serviceSubtaskDetailList: this.tableDatatop,
        serviceSubtaskDetailList: dataToSubmit, // æäº¤å¤„理后的副本
        param1: this.taskid,
        param2: this.patid,
        subId: this.id,
@@ -1518,7 +1874,16 @@
    formtidy() {
      this.form.visitType2 = this.form.visitType;
      this.form.date2 = this.form.longSendTime;
      // this.form.date1 = this.setCurrentDate();
      this.form.remark2 = this.form.remark;
    },
    setCurrentDate() {
      // èŽ·å–å½“å‰æ—¥æœŸå¹¶æ ¼å¼åŒ–ä¸º yyyy-MM-dd
      const today = new Date();
      const year = today.getFullYear();
      const month = String(today.getMonth() + 1).padStart(2, "0");
      const day = String(today.getDate()).padStart(2, "0");
      return `${year}-${month}-${day}`;
    },
    // èŽ·å–æ‚£è€…è®°å½•
    getTaskservelist(id) {
@@ -1531,7 +1896,7 @@
      getTaskservelist({
        patid: this.patid,
        subId: id,
        pageSize:100,
        pageSize: 100,
      }).then((res) => {
        if (res.code == 200) {
          console.log(11);
@@ -1539,12 +1904,11 @@
          this.form = res.rows[0].serviceSubtaskList.find(
            (item) => item.id == this.id
          );
          console.log(this.form);
          console.log(this.form.serviceType, "serviceType");
          console.log(this.form, "serviceType");
          this.logsheetlist = res.rows[0].serviceSubtaskList;
          this.templateid = this.logsheetlist[0].templateid;
          this.templateid = this.form.templateid;
          this.selectedTag = this.form.excep;
          const targetDate = new Date(this.form.longSendTime); // ç›®æ ‡æ—¥æœŸ
          const now = new Date(); // å½“前时间
          if (now < targetDate && this.form.sendstate == 2) {
@@ -1564,6 +1928,34 @@
          this.getsearchrResults();
        }
      });
    },
    // è°ƒèµ·çŸ­ä¿¡å‘送对话框
    sendAgainmsg() {
      this.smsDialogVisible = true;
      // å¯ä»¥åœ¨è¿™é‡Œåˆå§‹åŒ– smsContent,例如 this.smsContent = '';
    },
    // å‘送短信的方法
    sendSms() {
      // è¿™é‡Œè°ƒç”¨ä½ çš„短信发送 API
      // å‡è®¾ API ä¸º sendMsg,参数可能需要根据实际情况调整
      sendMsg({
        phone: this.userform.telcode, // ç¡®ä¿ç”µè¯å·ç å­—段正确
        content: this.smsContent,
      })
        .then((res) => {
          if (res.code == 200) {
            this.$modal.msgSuccess("发送成功");
            this.smsDialogVisible = false; // å…³é—­å¯¹è¯æ¡†
            this.smsContent = ""; // æ¸…空内容
          } else {
            this.$modal.msgError("发送失败");
          }
        })
        .catch((error) => {
          console.error("发送短信失败:", error);
          this.$modal.msgError("发送失败");
        });
    },
    Editsingletaskson(son) {
      let objson = {};
@@ -1594,6 +1986,8 @@
            (item) => item.id == this.id
          );
          objson.remark = this.form.remark;
          objson.taskSituation = this.form.taskSituation;
          objson.excep = this.selectedTag;
          if (sendstate) objson.sendstate = sendstate;
          Editsingletaskson(objson).then((res) => {
            if (res.code) {
@@ -1618,6 +2012,30 @@
      }
      return "";
    },
    getSelectedTagType() {
      if (!this.selectedTag) return "";
      const tag = this.tagOptions.find(
        (item) => item.value === this.selectedTag
      );
      return tag ? tag.type : "";
    },
    getSelectedTagColor() {
      if (!this.selectedTag) return "";
      const tag = this.tagOptions.find(
        (item) => item.value === this.selectedTag
      );
      return tag ? tag.color : "";
    },
    getSelectedDescription() {
      if (!this.selectedTag) return "";
      const tag = this.tagOptions.find(
        (item) => item.value === this.selectedTag
      );
      return tag ? tag.description : "";
    },
    // è°ƒèµ·å†æ¬¡å‘送
    sendAgain() {
      document.querySelector("#app").scrollTo(0, 0);
@@ -1630,10 +2048,10 @@
      this.$modal
        .confirm('是否查看任务为"' + row.taskName + '"的服务详情数据?')
        .then(() => {
          if (row.preachformson) {
            if (row.preachformson.includes("3")) {
              this.Voicetype = 1;
            }
          let type = "";
          console.log(row, "rwo");
          if (row.type == 1) {
            type = 1;
          }
          this.taskid = row.taskid;
          this.id = row.id;
@@ -1661,7 +2079,7 @@
          (item) => item.optioncontent == a
        );
      } else {
        var obj = this.tableDatatop[b].svyLibTemplateTargetoptions.find(
        var obj = this.tableDatatop[b].svyTaskTemplateTargetoptions.find(
          (item) => item.optioncontent == a
        );
      }
@@ -1672,13 +2090,47 @@
      }
      this.$forceUpdate();
    },
    handleRadioToggles(questionItem, optionValue) {
      if (!questionItem.matchedtext) {
        questionItem.matchedtext == "";
      }
      // å¦‚果点击的是当前已选中的选项,则取消选中
      if (questionItem.matchedtext == optionValue) {
        this.$set(questionItem, "matchedtext", "");
        // åŒæ—¶é‡ç½®ä¸Žé€‰é¡¹ç›¸å…³çš„状态
        questionItem.isabnormal = false;
        questionItem.showAppendInput = false;
        // æ³¨æ„ï¼šå–消选中时,我们通常不希望触发题目跳转逻辑,所以直接返回
        // å¦‚果需要,可以在这里添加取消选中后的特定逻辑,例如重置题目序列
      } else {
        // å¦‚果点击的是未选中的选项,则通过更改绑定值来触发原始的 handleOptionChange æ–¹æ³•
        // è¿™é‡Œåªéœ€è¦æ”¹å˜ v-model ç»‘定的值,change事件会自动触发
        this.$set(questionItem, "matchedtext", optionValue); // åŽç»­çš„跳转等复杂逻辑会在 handleOptionChange ä¸­æ­£å¸¸æ‰§è¡Œ
      }
    },
    // æ–°å¢žçš„切换选中/取消选中方法
    handleRadioToggle(questionItem, index, options, optionValue) {
      // å¦‚果点击的是当前已选中的选项,则取消选中
      if (questionItem.scriptResult === optionValue) {
        questionItem.scriptResult = ""; // æ¸…空选中值
        // åŒæ—¶é‡ç½®ä¸Žé€‰é¡¹ç›¸å…³çš„状态
        questionItem.isabnormal = false;
        questionItem.showAppendInput = false;
        // æ³¨æ„ï¼šå–消选中时,我们通常不希望触发题目跳转逻辑,所以直接返回
        // å¦‚果需要,可以在这里添加取消选中后的特定逻辑,例如重置题目序列
      } else {
        // å¦‚果点击的是未选中的选项,则通过更改绑定值来触发原始的 handleOptionChange æ–¹æ³•
        // è¿™é‡Œåªéœ€è¦æ”¹å˜ v-model ç»‘定的值,change事件会自动触发
        questionItem.scriptResult = optionValue;
        this.handleOptionChange(optionValue, index, options, questionItem);
        // åŽç»­çš„跳转等复杂逻辑会在 handleOptionChange ä¸­æ­£å¸¸æ‰§è¡Œ
      }
    },
    // åœ¨methods部分,修改handleOptionChange方法:
    handleOptionChange(selectedOption, questionIndex, options, a) {
      // åˆ¤æ–­æ˜¯å¦è¯­éŸ³
      if (this.Voicetype) {
        this.aahandleOptionChange(selectedOption, questionIndex, options);
        return;
      }
      console.log(selectedOption, questionIndex, options, a, "888");
      if (document.activeElement) {
        document.activeElement.blur();
      }
@@ -1771,7 +2223,7 @@
    },
    overdata() {
      this.tableDatatop.forEach((item, index) => {
        var obj = item.svyLibTemplateTargetoptions.find(
        var obj = item.svyTaskTemplateTargetoptions.find(
          (items) => items.optioncontent == item.scriptResult
        );
        if (obj) {
@@ -1901,6 +2353,21 @@
      this.getTaskservelist();
    },
  },
  // deactivated() {
  //   console.log(11);
  // },
  beforeRouteLeave(to, from, next) {
    this.$refs.callButton.cleanupResources();
    if (this.$refs.CallCenterLs) {
      console.log(1);
      this.$refs.CallCenterLs.handleSeatLogout();
    }
    next(); // ç¡®ä¿è°ƒç”¨ nex
  },
  // beforeRouteUpdate() {
  //   console.log(33);
  // },
};
</script>
@@ -1929,7 +2396,22 @@
    height: 100%; /* ç¡®ä¿é«˜åº¦ç»§æ‰¿ */
  }
}
.numeric-input {
  position: relative;
}
.numeric-input::after {
  content: "只能输入数字";
  position: absolute;
  right: 8px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 12px;
  color: #999;
  background: #f5f5f5;
  padding: 2px 6px;
  border-radius: 4px;
}
.call-container {
  padding: 20px;
  background: #fff;
@@ -2075,7 +2557,7 @@
.CONTENT {
  padding: 10px;
  height: 100%;
  min-height: 660px; /* è®¾ç½®æœ€å°é«˜åº¦ */
  min-height: 738px; /* è®¾ç½®æœ€å°é«˜åº¦ */
  .title {
    font-size: 22px;
@@ -2090,7 +2572,7 @@
  padding: 30px;
  border: 1px solid #dcdfe6;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12), 0 0 6px 0 rgba(0, 0, 0, 0.04);
  max-height: 580px; /* è®¾ç½®æœ€å¤§é«˜åº¦ */
  max-height: 618px; /* è®¾ç½®æœ€å¤§é«˜åº¦ */
  overflow-y: auto; /* å†…容超过高度时显示滚动条 */
  .topic-dev {
@@ -2172,7 +2654,54 @@
    top: 0;
  }
}
.tag-selector-container {
  display: flex;
  align-items: center;
  margin: 0 30px;
}
.color-indicator {
  width: 16px;
  height: 16px;
  border-radius: 3px;
  margin-right: 8px;
  display: inline-block;
}
.selected-indicator {
  margin-left: 10px;
  width: 20px;
  height: 20px;
}
.tag-info-icon {
  margin-left: 10px;
  color: #909399;
  cursor: pointer;
  font-size: 16px;
}
/* ç¡®ä¿é€‰æ‹©å™¨é€‰é¡¹ä¸­ä¹Ÿæ˜¾ç¤ºé¢œè‰²å— */
.el-select-dropdown__item {
  display: flex;
  align-items: center;
}
.tag-normal {
  background-color: #7ff5e1;
}
.tag-abnormal {
  background-color: #f75c5c;
}
.tag-warning {
  background-color: #fbfb4a;
}
.tag-info {
  margin-left: 10px;
  color: #909399;
  cursor: pointer;
}
::v-deep.offside-value .el-radio__label {
  color: #fff;
}
@@ -2222,4 +2751,193 @@
  color: #080808 !important;
  cursor: not-allowed;
}
/* åŽŸæœ‰çš„æ ·å¼ä¿æŒä¸å˜ï¼Œæ·»åŠ ä»¥ä¸‹å“åº”å¼ä»£ç  */
.Followupdetailspage {
  margin: 10px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.action-container {
  display: flex;
  flex-direction: row; /* é»˜è®¤æ¨ªå‘排列 */
  gap: 20px;
  margin: 0 10px 20px 10px;
  /* å½“缩放比例大于100%或屏幕宽度较小时改为上下排列 */
  @media screen and (min-resolution: 1.5dppx) {
    flex-direction: column;
    .call-action,
    .manual-action {
      width: 100% !important;
    }
  }
}
.call-action {
  width: 65%;
  min-width: 0;
}
.manual-action {
  flex: 1;
  min-width: 0;
}
/* è°ƒæ•´å†…部元素的响应式布局 */
.Followuserinfos {
  .el-form {
    /* è¡¨å•响应式调整 */
    .el-row {
      margin: 0 -10px;
    }
    .el-col {
      padding: 0 10px;
    }
    @media screen and (max-width: 768px) {
      .el-col {
        width: 100%;
        margin-bottom: 15px;
        &:last-child {
          margin-bottom: 0;
        }
      }
    }
  }
}
/* è°ƒæ•´è¡¨æ ¼çš„响应式表现 */
.el-table {
  ::v-deep .el-table__body-wrapper {
    overflow-x: auto;
  }
  /* åœ¨å°å±å¹•上调整表格列宽 */
  @media screen and (max-width: 992px) {
    .el-table-column {
      min-width: 120px;
    }
  }
}
/* è°ƒæ•´æ ‡ç­¾é€‰æ‹©å™¨çš„响应式布局 */
.tag-selector-container {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  @media screen and (max-width: 576px) {
    flex-direction: column;
    align-items: flex-start;
    .el-select {
      width: 100%;
      margin-right: 0 !important;
    }
  }
}
/* è°ƒæ•´æŒ‰é’®ç»„的响应式布局 */
.el-form-item.label-processing-opinion {
  .el-button-group {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    .el-button {
      flex: 1;
      min-width: 120px;
    }
  }
}
/* è°ƒæ•´é€‰é¡¹å¡çš„响应式表现 */
.el-tabs {
  ::v-deep .el-tabs__nav-wrap {
    overflow-x: auto;
    white-space: nowrap;
    &::after {
      display: none;
    }
  }
}
/* è°ƒæ•´é¢„览区域的响应式表现 */
.preview-left {
  @media screen and (max-width: 768px) {
    margin: 10px;
    padding: 15px;
    .topic-dev,
    .scriptTopic-dev {
      margin-bottom: 15px;
    }
  }
}
/* è°ƒæ•´å¯¹è¯æ¡†çš„响应式表现 */
.el-dialog {
  @media screen and (max-width: 992px) {
    width: 90% !important;
    margin-top: 5vh !important;
    .el-dialog__body {
      padding: 15px;
    }
  }
  @media screen and (max-width: 576px) {
    width: 95% !important;
    .el-form-item {
      margin-bottom: 15px;
    }
  }
}
/* ç¡®ä¿å†…容在缩放时保持可读性 */
.headline {
  font-size: clamp(18px, 2vw, 24px); /* ä½¿ç”¨clamp函数确保字体大小在合理范围内 */
}
/* ä¸ºç§»åŠ¨è®¾å¤‡ä¼˜åŒ–æ»šåŠ¨ä½“éªŒ */
@media screen and (max-width: 768px) {
  .Followuserinfo,
  .Followuserinfos {
    padding: 15px;
    margin: 5px;
  }
  .CONTENT {
    min-height: auto;
    padding: 5px;
  }
}
/* ç¼©æ”¾æ£€æµ‹æ ·å¼ */
@media screen and (min-resolution: 1.5dppx),
  screen and (-webkit-min-device-pixel-ratio: 1.5) {
  .action-container {
    flex-direction: column;
  }
  .call-action,
  .manual-action {
    width: 100%;
  }
  /* è°ƒæ•´å†…部元素间距 */
  .call-container,
  .Followuserinfos {
    margin-bottom: 20px;
  }
}
</style>
src/views/followvisit/record/index.vue
@@ -149,8 +149,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -215,7 +214,7 @@
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >当日服务</el-button
                >今日服务</el-button
              >
            </div>
          </div>
@@ -808,8 +807,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -863,7 +862,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
@@ -906,7 +905,7 @@
        serviceType: 13,
        searchscope: 3,
        sendstate: 2,
        sort: 2,
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
@@ -970,6 +969,14 @@
        {
          value: 3,
          label: "发送时间(倒序)",
        },
        {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      errtype: "",
@@ -1080,11 +1087,10 @@
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1406,12 +1412,9 @@
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
          console.log(type, "rwo");
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1585,10 +1588,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
src/views/followvisit/record/physical/index.vue
@@ -18,7 +18,7 @@
                >只展示本次服务信息</el-button
              >
            </div>
            <div style="margin-left: 20px; color: #59a0f0">
            <!-- <div style="margin-left: 20px; color: #59a0f0">
              <el-link
                href="https://9.208.2.207:6060/search-homepage"
                target="_blank"
@@ -26,7 +26,7 @@
              >
                å‰å¾€CDSS查询
              </el-link>
            </div>
            </div> -->
          </div>
          <!-- <el-button type="success">随访后短信</el-button> -->
        </div>
@@ -573,8 +573,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -692,11 +692,10 @@
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1106,11 +1105,11 @@
      this.$modal
        .confirm('是否查看任务为"' + row.taskName + '"的服务详情数据?')
        .then(() => {
          if (row.preachformson) {
            if (row.preachformson.includes("3")) {
              this.Voicetype = 1;
            }
          }
          let type = "";
      console.log(row, "rwo");
        if (row.type == 1) {
          type = 1;
        }
          this.taskid = row.taskid;
          this.id = row.id;
          this.patid = row.patid;
src/views/followvisit/tasklist/index.vue
@@ -250,8 +250,15 @@
              size="medium"
              type="text"
              @click="handleUpdate(scope.row, 1)"
              ><span class="button-xj"
                ><i class="el-icon-circle-plus-outline"></i>依照新增</span
              ><span class="button-xj">依照新增</span></el-button
            >
            <el-button
              v-if="scope.row.sendState != 5"
              size="medium"
              type="text"
              @click="handleAddpatient(scope.row.taskid, scope.row.type)"
              ><span class="button-hz"
                ><i class="el-icon-circle-plus-outline"></i>新增患者</span
              ></el-button
            >
            <el-button
@@ -259,9 +266,7 @@
              size="medium"
              type="text"
              @click="stop(scope.row)"
              ><span class="button-zt"
                ><i class="el-icon-circle-plus-outline"></i>暂停</span
              ></el-button
              ><span class="button-zt">暂停</span></el-button
            >
          </template>
        </el-table-column>
@@ -373,6 +378,13 @@
        >
      </div>
    </el-dialog>
    <!-- é€‰æ‹©æ‚£è€…弹框 -->
    <Patient-Selection
      ref="Patient"
      :dialogVisiblepatient="dialogVisiblepatient"
      @addoption="addoption"
      @kkoption="dialogVisiblepatient = true"
    />
  </div>
</template>
@@ -387,23 +399,19 @@
} from "@/api/system/user";
import {
  getTasklist,
  getTaskInfo,
  Editsingletask,
  delTaskInfo,
  Questionnairetasklist,
  Questionnairetaskget,
  Questionnairetasksponsor,
  TaskTemplateSendExecution,
} from "@/api/AiCentre/index";
import store from "@/store";
import PatientSelection from "@/components/PatientSelection"; //正则组件
import SFtable from "@/components/SFtable"; //表格组件
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
  name: "Tasklist",
  dicts: ["sys_normal_disable", "sys_user_sex", "task_status"],
  components: { Treeselect },
  components: { Treeselect, PatientSelection },
  data() {
    return {
      // é®ç½©å±‚
@@ -441,6 +449,8 @@
        showTimeNight: [],
        showTimeNoon: [],
      },
      dialogVisiblepatient: false,
      Patientlist: [],
      taskformVisible: false,
      dynamicTags: ["选项一", "选项二", "选项三"], //选项
      inputVisible: false,
@@ -474,61 +484,8 @@
          label: "消息通知",
        },
      ],
      taskoptions: [
        {
          value: "1",
          label: "监测评估",
        },
        {
          value: "2",
          label: "出院随访",
        },
        {
          value: "3",
          label: "门诊随访",
        },
        {
          value: "4",
          label: "宣教关怀",
        },
        {
          value: "5",
          label: "复诊管理",
        },
        // {
        //   value: "5",
        //   label: "满意度调查",
        // },
        {
          value: "7",
          label: "患者报告",
        },
        // {
        //   value: "8",
        //   label: "其他通知",
        // },
        {
          value: "9",
          label: "体检随访",
        },
        // {
        //   value: "10",
        //   label: "医技随访",
        // },
        {
          value: "11",
          label: "影像随访",
        },
        {
          value: "12",
          label: "心电随访",
        },
        {
          value: "13",
          label: "专病随访",
        },
      ],
      tasktopic: "2", //新增类型
      taskoptions: store.getters.tasktypes,
      tasktopic: 2, //新增类型
      activname: "",
      value: [],
      list: [],
@@ -693,6 +650,7 @@
    this.tasktopic = this.$route.query.tasktopic
      ? this.$route.query.tasktopic
      : this.tasktopic;
    this.tasktopic = Number(this.tasktopic);
    this.getList();
    this.getConfigKey("sys.user.initPassword").then((response) => {
      this.initPassword = response.msg;
@@ -719,6 +677,7 @@
        this.tasktopic == 3 ||
        this.tasktopic == 1 ||
        this.tasktopic == 7 ||
        this.tasktopic == 5 ||
        this.tasktopic == 6
      ) {
        if (!this.topqueryParams.type) this.topqueryParams.type = 1;
@@ -732,6 +691,7 @@
            label: "问卷随访",
          },
        ];
        // this.topqueryParams.type = 2;
        // this.topqueryParams.type = 2;
      } else if (this.tasktopic == 4 || this.tasktopic == 8) {
        if (!this.topqueryParams.type) this.topqueryParams.type = "3";
@@ -753,7 +713,12 @@
            value: 2,
            label: "问卷随访",
          },
          {
            value: 3,
            label: "宣教关怀",
          },
        ];
        // this.topqueryParams.type = 2;
      }
      this.topqueryParams.beginTime = this.dateRange[0];
      this.topqueryParams.endTime = this.dateRange[1];
@@ -1090,6 +1055,14 @@
      const item = data.find((item) => item.value === value);
      return item ? item.label : null;
    },
    handleAddpatient(taskid) {
      this.$refs.Patient.handleAddpatient(taskid);
      this.dialogVisiblepatient = true; // æ‰‹åŠ¨æŽ§åˆ¶å¼¹çª—æ˜¾ç¤º
    },
    addoption() {
      this.dialogVisiblepatient = false; // æ‰‹åŠ¨æŽ§åˆ¶å¼¹çª—æ˜¾ç¤º
      this.handleQuery();
    },
  },
};
</script>
@@ -1219,6 +1192,12 @@
  border-radius: 1px;
  color: #ffffff;
}
.button-hz {
  background: #63d37b;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
::v-deep.el-radio-group {
  span {
src/views/followvisit/technology/index.vue
@@ -138,8 +138,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -807,7 +806,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
@@ -858,8 +857,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -1092,11 +1091,9 @@
    },
    //患者360跳转
    gettoken360(sfzh,drcode,drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC=='丽水市中医院') {
        this.postData.YeWuXX.YongHuXX.YongHuID = '1400398571877961728';
        this.postData.YeWuXX.YongHuXX.YongHuXM = 'LSZYY';
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
           window.open(res.data.url, '_blank');
@@ -1344,12 +1341,9 @@
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
          console.log(type, "rwo");
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1521,10 +1515,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
src/views/followvisit/zbAgain/index.vue
@@ -180,8 +180,7 @@
        <el-col :span="1.5">
          <el-button
            type="primary"
            plain
            icon="el-icon-plus"
                        icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
@@ -246,7 +245,7 @@
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >当日服务</el-button
                >今日服务</el-button
              >
            </div>
          </div>
@@ -397,13 +396,7 @@
          key="drname"
          prop="drname"
        />
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="出院天数"
          width="120"
@@ -483,7 +476,13 @@
          :show-overflow-tooltip="true"
        >
        </el-table-column>
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="出院随访模板名称"
          align="center"
@@ -918,7 +917,7 @@
        //   value: 0,
        // },
        {
          name: "应随访",
          name: "需随访",
          value: 0,
        },
        {
@@ -973,13 +972,21 @@
          value: 3,
          label: "发送时间(倒序)",
        },
        {
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: 2,
        sort: 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 13,
        searchscope: 3,
        visitCount: 2,
@@ -1067,8 +1074,8 @@
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: "1400466972205912064",
            YongHuXM: "JNRMYY",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
@@ -1416,11 +1423,10 @@
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      if (this.postData.XiaoXiTou.ZuHuMC == "丽水市中医院") {
        this.postData.YeWuXX.YongHuXX.YongHuID = "1400398571877961728";
        this.postData.YeWuXX.YongHuXX.YongHuXM = "LSZYY";
      }
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
@@ -1533,11 +1539,9 @@
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
      if (row.preachformson) {
        if (row.preachformson.includes("3")) {
        if (row.type == 1) {
          type = 1;
        }
      }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
@@ -1762,10 +1766,12 @@
  }
}
::v-deep.leftvlue .el-card__body {
  background: #d0e9fd;
  background: #F2F8FF;
  color: #324A9B;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #8dc8f8;
  background: #3664D9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.errleftvlue .el-card__body {
@@ -1810,7 +1816,9 @@
  border-radius: 1px;
  color: #ffffff;
}
.button-textxga {
  color: #de7897;
}
::v-deep.el-radio-group {
  span {
    font-size: 24px;
src/views/followvisit/zysatisfaction/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1967 @@
<template>
  <div class="app-container">
    <div class="leftvlue" style="margin-bottom: 20px">
      <el-row :gutter="10">
        <el-col :span="2.5" v-for="(item, index) in cardlist" :key="index">
          <el-card
            shadow="hover"
            :body-style="item.router ? ' cursor: pointer' : 'cursor: default'"
          >
            <div style="padding: 8px" @click="$router.push(item.router)">
              <span>{{ item.name }}</span>
              <div
                style="
                  text-align: center;
                  font-size: 18px;
                  margin-top: 10px;
                  font-weight: 600;
                "
              >
                {{ item.value ? item.value : 0 }}
              </div>
            </div>
          </el-card>
        </el-col>
        <el-col :span="2.5">
          <div class="ysfleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>表单已发送</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ yfsvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
        <el-col :span="2.5">
          <div class="errleftvlue">
            <el-card shadow="hover">
              <div style="padding: 8px">
                <span>异常</span>
                <div
                  style="
                    text-align: center;
                    font-size: 18px;
                    margin-top: 10px;
                    font-weight: 600;
                  "
                >
                  {{ ycvalue }}
                </div>
              </div>
            </el-card>
          </div>
        </el-col>
      </el-row>
    </div>
    <el-row :gutter="20">
      <!--用户数据-->
      <el-form
        :model="topqueryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        v-show="showSearch"
        label-width="98px"
      >
        <el-form-item label="任务名称">
          <el-input
            v-model="topqueryParams.taskName"
            placeholder="请选择任务名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="出院时间">
          <el-date-picker
            v-model="dateRange"
            style="width: 240px"
            value-format="yyyy-MM-dd"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="患者姓名" prop="sendname">
          <el-input
            v-model="topqueryParams.sendname"
            placeholder="请输入患者姓名"
          ></el-input>
        </el-form-item>
        <el-form-item label="诊断名称" prop="leavediagname">
          <el-input
            v-model="topqueryParams.leavediagname"
            placeholder="请输入诊断名称"
          ></el-input>
        </el-form-item>
        <el-form-item label="随访人员" prop="updateBy">
          <el-input
            v-model="topqueryParams.updateBy"
            placeholder="请输入随访人员"
          ></el-input>
        </el-form-item>
        <el-form-item label="主治医生" prop="drname">
          <el-input
            v-model="topqueryParams.drname"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="经管医生" prop="managementDoctor">
          <el-input
            v-model="topqueryParams.managementDoctor"
            placeholder="请输入主治医生"
          ></el-input>
        </el-form-item>
        <el-form-item label="患者范围" prop="status">
          <el-cascader
            v-model="topqueryParams.scopetype"
            placeholder="默认全部"
            :options="sourcetype"
            :props="{ expandTrigger: 'hover' }"
            @change="handleChange"
          ></el-cascader>
        </el-form-item>
        <el-form-item label="任务状态" prop="status">
          <el-select v-model="topqueryParams.sendstate" placeholder="请选择">
            <el-option
              v-for="item in topicoptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="排序方式" prop="status">
          <el-select v-model="topqueryParams.sort" placeholder="请选择">
            <el-option
              v-for="item in topicoptionssort"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="medium"
            @click="handleQuery(1)"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
      <el-divider></el-divider>
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-upload2"
                size="medium"
                @click="handleExport"
                >导出</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <el-button
            type="primary"
            icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新增</el-button
          >
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="warning"
                plain
                icon="el-icon-warning-outline"
                size="medium"
                @click="toleadExport(1)"
                >执行失败</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="danger"
                plain
                icon="el-icon-warning"
                size="medium"
                @click="toleadExport(2)"
                >结果异常</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="success"
                plain
                size="medium"
                @click="buidegetTasklist()"
                >待办服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button
                type="primary"
                plain
                size="medium"
                @click="affiliation()"
                >本人所属服务</el-button
              >
            </div>
          </div>
        </el-col>
        <el-col :span="1.5">
          <div class="documentf">
            <div class="document">
              <el-button type="success" size="medium" @click="onthatday()"
                >今日服务</el-button
              >
            </div>
          </div>
        </el-col>
      </el-row>
      <el-table
        v-loading="loading"
        ref="userform"
        :data="userList"
        :row-class-name="tableRowClassName"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="50" align="center" />
        <el-table-column
          label="任务名称"
          fixed
          width="150"
          show-overflow-tooltip
          align="center"
          key="taskName"
          prop="taskName"
        />
        <!-- <el-table-column label="序号" fixed align="center" key="id" prop="id" /> -->
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          key="sendname"
          prop="sendname"
        >
          <template slot-scope="scope">
            <el-button
              size="medium"
              type="text"
              @click="
                gettoken360(scope.row.sfzh, scope.row.drcode, scope.row.drname)
              "
              ><span class="button-textsc">{{
                scope.row.sendname
              }}</span></el-button
            >
          </template>
        </el-table-column>
        <el-table-column
          label="任务状态"
          align="center"
          key="sendstate"
          prop="sendstate"
          width="120"
        >
          <template slot-scope="scope">
            <el-tooltip
              class="item"
              effect="dark"
              :content="scope.row.remark"
              placement="top-start"
            >
              <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>
            </el-tooltip>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务异常说明"
          width="120"
          align="center"
          key="remark"
          prop="remark" -->
        />
        <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
          label="随访完成时间"
          sortable
          align="center"
          prop="finishtime"
          width="160"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.finishtime) }}</span>
          </template>
        </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="drname"
          prop="drname"
        />
        <el-table-column
          label="经管医生"
          align="center"
          key="managementDoctor"
          prop="managementDoctor"
          width="120"
        />
        <el-table-column
          label="出院天数"
          width="120"
          align="center"
          key="endDay"
          prop="endDay"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.endDay ? scope.row.endDay + "天" : "" }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="身份证号码"
          width="200"
          align="center"
          key="sfzh"
          prop="sfzh"
        />
        <el-table-column
          label="联系电话"
          width="200"
          align="center"
          key="phone"
          prop="phone"
        />
        <el-table-column
          label="责任护士"
          width="120"
          align="center"
          key="nurseName"
          prop="nurseName"
        />
        <!-- <el-table-column
          label="病历号"
          align="center"
          sortable
          key="medicalRecordNo"
          prop="medicalRecordNo"
          width="120"
        /> -->
        <!-- <el-table-column label="年龄" align="center" key="age" prop="age" /> -->
        <!-- <el-table-column label="性别"width="100" align="center" key="sex" prop="sex" /> -->
        <!-- <el-table-column label="床号" align="center" key="badNo" prop="badNo" /> -->
        <el-table-column
          label="科室"
          align="center"
          key="deptname"
          prop="deptname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="病区"
          align="center"
          key="leavehospitaldistrictname"
          prop="leavehospitaldistrictname"
          width="120"
        >
        </el-table-column>
        <el-table-column
          label="诊断名称"
          align="center"
          key="leavediagname"
          prop="leavediagname"
          width="120"
          :show-overflow-tooltip="true"
        >
        </el-table-column>
        <el-table-column
          label="随访人员"
          align="center"
          key="updateBy"
          prop="updateBy"
          width="120"
        />
        <el-table-column
          label="住院满意度模板名称"
          align="center"
          key="templatename"
          prop="templatename"
          width="200"
        />
        <el-table-column
          label="任务执行方式"
          align="center"
          key="preachform"
          prop="preachform"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.preachform">{{ item }}、 </span>
          </template>
        </el-table-column>
        <!-- <el-table-column
          label="任务发送流程"
          align="center"
          key="serviceSubtaskRecordList"
          prop="serviceSubtaskRecordList"
          width="160"
          :show-overflow-tooltip="true"
        >
          <template slot-scope="scope">
            <span v-for="item in scope.row.serviceSubtaskRecordList"
              >{{ item.remark }}、
            </span>
          </template>
        </el-table-column> -->
        <el-table-column
          label="任务结果说明"
          width="220"
          align="center"
          key="remark"
          prop="remark"
        >
          <template slot-scope="scope" v-if="scope.row.remark">
            <el-tooltip
              :content="scope.row.remark"
              placement="top"
              effect="dark"
            >
              <el-tag
                type="warning"
                v-if="scope.row.sendstate != 5 && scope.row.sendstate != 4"
                >{{ scope.row.remark }}</el-tag
              >
              <el-tag type="warning" v-else>{{ scope.row.remark }}</el-tag>
            </el-tooltip>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
          fixed="right"
          width="300"
          class-name="small-padding fixed-width"
        >
          <template slot-scope="scope">
            <!-- <el-tooltip
              class="item"
              effect="dark"
              content="再次随访"
              placement="top"
            >
              <el-button
                size="medium"
                type="text"
                v-if="scope.row.isVisitAgain!=2"
                @click="followupvisit(scope.row)"
                ><span class="button-bb"
                  ><i class="el-icon-s-promotion"></i>再次随访</span
                ></el-button
              >
            </el-tooltip>
            <el-tooltip
              v-if="scope.row.sendstate == 1 || scope.row.sendstate == 2"
              class="item"
              effect="dark"
              content="暂停服务"
              placement="top"
            >
              <el-button
                size="medium"
                type="text"
                @click="handlestop(scope.row)"
                v-hasPermi="['system:user:edit']"
                ><span class="button-sc"
                  ><i class="el-icon-remove-outline"></i>暂停服务</span
                ></el-button
              >
            </el-tooltip> -->
            <el-button size="medium" type="text" @click="Seedetails(scope.row)"
              ><span class="button-zx"
                ><i class="el-icon-s-order"></i>查看详情</span
              ></el-button
            >
            <el-button
              size="medium"
              type="text"
              @click="handleUpdate(scope.row)"
              ><span class="button-textxga"
                ><i class="el-icon-edit"></i>患者过滤</span
              ></el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="topqueryParams.pageNum"
        :limit.sync="topqueryParams.pageSize"
        @pagination="getList"
      />
    </el-row>
    <!-- æ»¡æ„åº¦å¼¹æ¡† -->
    <el-dialog
      title="随访满意度评分"
      :visible.sync="scoreDialogVisible"
      width="80%"
      :close-on-click-modal="false"
    >
      <el-table :data="selectedRows" border style="width: 100%">
        <el-table-column
          label="姓名"
          width="100"
          align="center"
          prop="sendname"
        />
        <el-table-column
          label="任务名称"
          width="180"
          align="center"
          prop="taskName"
        />
        <!-- æ–°å¢žè¯„分列 -->
        <el-table-column
          label="真实性(20)"
          align="center"
          key="authenticity"
          prop="authenticity"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.authenticity"
              :min="0"
              :max="20"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="一周内完成(20)"
          align="center"
          key="weekFinish"
          prop="weekFinish"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.weekFinish"
              :min="0"
              :max="20"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="规范性(10)"
          align="center"
          key="standard"
          prop="standard"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.standard"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="及时性(10)"
          align="center"
          key="timeliness"
          prop="timeliness"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.timeliness"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="宣教情况(10)"
          align="center"
          key="library"
          prop="library"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.library"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="环境满意度(10)"
          align="center"
          key="environment"
          prop="environment"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.environment"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="医生满意度(10)"
          align="center"
          key="doctorSatisfaction"
          prop="doctorSatisfaction"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.doctorSatisfaction"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="护士满意度(10)"
          align="center"
          key="nurseSatisfaction"
          prop="nurseSatisfaction"
          width="150"
        >
          <template slot-scope="scope">
            <el-input-number
              v-model="scope.row.nurseSatisfaction"
              :min="0"
              :max="10"
              :step="1"
              size="small"
            />
          </template>
        </el-table-column>
        <el-table-column
          label="总分"
          align="center"
          key="total"
          prop="total"
          fixed="right"
        >
          <template slot-scope="scope">
            <span>{{ calculateTotal(scope.row) }}</span>
          </template>
        </el-table-column>
      </el-table>
      <div slot="footer" class="dialog-footer">
        <el-button @click="scoreDialogVisible = false">取消</el-button>
        <el-button type="primary" @click="saveScores">保存</el-button>
      </div>
    </el-dialog>
    <!-- æ·»åŠ æˆ–ä¿®æ”¹å½±åƒéšè®¿å¯¹è¯æ¡† -->
    <el-dialog
      :title="amendtag ? '修改患者信息' : '新增患者'"
      :visible.sync="Labelchange"
      width="900px"
    >
      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
        <el-row>
          <el-col :span="8">
            <el-form-item label="姓名" width="100" prop="name">
              <el-input
                v-model="form.name"
                placeholder="请输入姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="性别" width="100" prop="sex">
              <el-select v-model="form.sex" placeholder="请选择性别">
                <el-option
                  v-for="dict in sextype"
                  :key="dict.value"
                  :label="dict.label"
                  :value="dict.value"
                ></el-option>
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="年龄" prop="age">
              <el-input
                v-model="form.age"
                placeholder="请输入年龄"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="8">
            <el-form-item label="过滤医生" width="100" prop="filterDrname">
              <el-input
                v-model="form.filterDrname"
                placeholder="请输入医生姓名"
                maxlength="30"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row>
          <el-col :span="24">
            <el-form-item label="过滤原因">
              <el-input
                v-model="form.notrequiredreason"
                type="textarea"
                placeholder="请输入过滤原因"
              ></el-input>
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">ç¡® å®š</el-button>
        <el-button @click="cancel">取 æ¶ˆ</el-button>
      </div>
    </el-dialog>
    <!-- ä¿®æ”¹å‘送时间对话框 -->
    <el-dialog
      title="发送时间设置"
      :visible.sync="modificationVisible"
      width="45%"
    >
      <div style="margin-bottom: 20px; color: red">
        ç»Ÿä¸€ä¿®æ”¹å½“天未发送的任务时间
      </div>
      <el-form
        :model="ruleForm"
        :rules="rules"
        ref="ruleForm"
        label-width="120px"
        class="demo-ruleForm"
      >
        <el-form-item label="发送日期">
          <el-date-picker
            v-model="ruleForm.value1"
            type="date"
            placeholder="选择日期"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="时间段" prop="type">
          <el-checkbox-group v-model="ruleForm.type">
            <el-checkbox label="上午" name="type"></el-checkbox>
            <el-checkbox label="下午" name="type"></el-checkbox>
            <el-checkbox label="晚上" name="type"></el-checkbox>
          </el-checkbox-group>
        </el-form-item>
        <el-form-item label="上午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value2"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="下午时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value3"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
        <el-form-item label="晚上时间区间" required>
          <el-time-picker
            is-range
            v-model="ruleForm.value4"
            range-separator="至"
            start-placeholder="开始时间"
            end-placeholder="结束时间"
            placeholder="选择时间范围"
          >
          </el-time-picker>
        </el-form-item>
      </el-form>
      <span slot="footer" class="dialog-footer">
        <el-button @click="modificationVisible = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="modificationVisible = false"
          >ç¡® å®š</el-button
        >
      </span>
    </el-dialog>
    <!-- å†æ¬¡éšè®¿ -->
  </div>
</template>
<script>
import {
  delUser,
  addUser,
  updateUser,
  resetUserPwd,
  changeUserStatus,
} from "@/api/system/user";
import {
  getTaskservelist,
  buidegetTasklist,
  addserviceSubtask,
  query360PatInfo,
  addsatisfaction,
} from "@/api/AiCentre/index";
import { alterpatient, particularpatient } from "@/api/patient/homepage";
import Treeselect from "@riophae/vue-treeselect";
import store from "@/store";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
  name: "Discharge",
  dicts: ["sys_normal_disable", "sys_user_sex", "sys_yujing", "sys_suggest"],
  components: { Treeselect },
  data() {
    return {
      // é®ç½©å±‚
      loading: true,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // æ€»æ¡æ•°
      total: 0,
      // ç”¨æˆ·è¡¨æ ¼æ•°æ®
      userList: null,
      // å¼¹å‡ºå±‚标题
      title: "新增影像随访",
      // æ˜¯å¦æ˜¾ç¤ºä¿®æ”¹ã€æ·»åŠ å¼¹å‡ºå±‚
      addalteropen: false,
      // ä¿®æ”¹å‘送时间对话框
      modificationVisible: false,
      // éƒ¨é—¨åç§°
      deptName: undefined,
      // é»˜è®¤å¯†ç 
      initPassword: undefined,
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      dateRangefs: [],
      // å²—位选项
      postOptions: [],
      ruleForm: {
        type: [],
      },
      zcform: {},
      dynamicTags: ["选项一", "选项二", "选项三"], //选项
      inputVisible: false,
      Labelchange: false,
      ycvalue: "",
      yfsvalue: "",
      inputValue: "",
      preachform: "",
      previewVisible: false, //影像随访预览弹框
      radio: "",
      radios: [],
      previewtype: 2, //预览影像随访类型
      total: 0, // æ€»æ¡æ•°
      // æ»¡æ„åº¦è°ƒæŸ¥æ•°æ®
      scoreDialogVisible: false,
      selectedRows: [],
      value: [],
      list: [],
      sourcetype: [
        {
          value: 1,
          label: "科室",
          children: [],
        },
        {
          value: 2,
          label: "病区",
          children: [],
        },
        {
          value: 3,
          label: "全部",
        },
      ],
      loading: false,
      cardlist: [
        {
          name: "服务总量",
          value: 0,
        },
        // {
        //   name: "患者过滤",
        //   value: 0,
        // },
        {
          name: "需随访",
          value: 0,
        },
        {
          name: "发送失败",
          value: 0,
        },
        {
          name: "待随访",
          value: 0,
        },
        // {
        //   name: "已发送",
        //   value: 0,
        // },
        // {
        //   name: "表单已发送",
        //   value: 0,
        // },
      ],
      zcrules: {
        date1: [
          { required: true, message: "请选择随访方式", trigger: "change" },
        ],
        resource: [
          { required: true, message: "请选择随访时间", trigger: "blur" },
        ],
      },
      // è¡¨å•参数
      form: {
        phonenumber: "",
        totagid: "",
        types: "",
        nickName: "",
        qystatus: "",
        btstatus: "",
      },
      topicoptionssort: [
        {
          value: 0,
          label: "出院时间(正序)",
        },
        {
          value: 1,
          label: "出院时间(倒序)",
        },
        {
          value: 2,
          label: "发送时间(正序)",
        },
        {
          value: 3,
          label: "发送时间(倒序)",
        },{
          value: 7,
          label: "应随访日期(正序)",
        },
        {
          value: 8,
          label: "应随访日期(倒序)",
        },
      ],
      // æŸ¥è¯¢å‚æ•°
      topqueryParams: {
        pageNum: 1,
        pageSize: 10,
        sendstate: 6,
        sort: localStorage.getItem("orgname") == "丽水市中医院" ? 8 : 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)  7应随访日期(倒序) åº”随访日期(正序)
        serviceType: 6,
        searchscope: 3,
        visitCount: 1,
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      },
      propss: { multiple: true },
      options: [],
      topicoptions: [
        {
          value: null,
          label: "全部",
        },
        {
          value: 1,
          label: "表单已领取",
        },
        {
          value: 2,
          label: "待随访",
        },
        {
          value: 3,
          label: "表单已发送",
        },
        {
          value: 4,
          label: "不执行",
        },
        {
          value: 5,
          label: "发送失败",
        },
        {
          value: 6,
          label: "已完成",
        },
      ],
      sextype: [
        {
          value: 1,
          label: "男",
        },
        {
          value: 2,
          label: "女",
        },
      ],
      topicoptionsyj: [
        {
          value: 1,
          label: "异常",
        },
        {
          value: 0,
          label: "正常",
        },
      ],
      url: "http://9.208.2.190:8090/smartor/serviceExternal/query360PatInfo",
      postData: {
        XiaoXiTou: {
          FaSongFCSJC: "ZJHES",
          FaSongJGID: localStorage.getItem("orgid"),
          FaSongJGMC: localStorage.getItem("orgname"),
          FaSongSJ: "2025-01-09 17:29:36",
          FaSongXTJC: "SUIFANGXT",
          FaSongXTMC: "随访系统",
          XiaoXiID: "5FA92AFB-9833-4608-87C7-F56A654AC171",
          XiaoXiLX: "SC_LC_360STCX",
          XiaoXiMC: "360 视图查询",
          ZuHuID: localStorage.getItem("ZuHuID"),
          ZuHuMC: localStorage.getItem("orgname"),
        },
        YeWuXX: {
          BingRenXX: {
            ZhengJianHM: "",
            ZhengJianLXDM: "01",
            ZhengJianLXMC: "居民身份证",
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
          },
          YongHuXX: {
            XiTongID: "SUIFANGXT",
            XiTongMC: "随访系统",
            YongHuID: localStorage.getItem("YongHuID"),
            YongHuXM: localStorage.getItem("YongHuXM"),
            ZuZhiJGID: localStorage.getItem("orgid"),
            ZuZhiJGMC: localStorage.getItem("orgname"),
            idp: "lyra",
          },
        },
      },
      amendtag: false,
      errtype: "",
      leavehospitaldistrictcode: "",
      serviceState: [],
      checkboxlist: [],
      // è¡¨å•校验
      rules: {},
    };
  },
  watch: {},
  created() {
    this.serviceState = store.getters.serviceState;
    this.checkboxlist = store.getters.checkboxlist;
    this.errtype = this.$route.query.errtype;
    this.leavehospitaldistrictcode =
      this.$route.query.leavehospitaldistrictcode;
    this.sourcetype[0].children = store.getters.belongDepts.map((dept) => {
      return {
        label: dept.deptName,
        value: dept.deptCode,
      };
    });
    this.sourcetype[1].children = store.getters.belongWards.map((dept) => {
      return {
        label: dept.districtName,
        value: dept.districtCode,
      };
    });
    if (this.errtype) {
      this.toleadExport(2);
    } else {
      this.getList(1);
    }
    this.getConfigKey("sys.user.initPassword").then((response) => {
      this.initPassword = response.msg;
    });
  },
  activated() {
    this.getList(1);
  },
  methods: {
    /** æŸ¥è¯¢éšè®¿æœåŠ¡åˆ—è¡¨ */
    getList(refresh) {
      // é»˜è®¤å…¨éƒ¨
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
        console.log(this.topqueryParams.leavehospitaldistrictcodes, "11");
      }
      this.loading = true;
      if (
        this.topqueryParams.leavehospitaldistrictcodes[0] &&
        this.topqueryParams.leaveldeptcodes[0]
      ) {
        this.topqueryParams.deptOrDistrict = 2;
      } else {
        this.topqueryParams.deptOrDistrict = 1;
      }
      getTaskservelist(this.topqueryParams).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          // this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[1].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[2].value = response.rows[0].fssb;
          this.cardlist[3].value = response.rows[0].dsf;
          // this.cardlist[4].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    affiliation() {
      this.topqueryParams.managementDoctorCode = store.getters.hisUserId;
      this.getList(1);
    },
    onthatday() {
      this.topqueryParams.startSendDateTime = this.getCurrentDate();
      this.topqueryParams.endSendDateTime = this.getCurrentDate();
      this.getList(1);
    },
    getCurrentDate() {
      const now = new Date();
      return now.toISOString().slice(0, 10); // æˆªå–前10个字符,即 YYYY-MM-DD
    },
    buidegetTasklist(type) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      // æŽ¥å—异常跳转
      if (this.errtype) {
        this.topqueryParams.leavehospitaldistrictcodes.push(
          this.leavehospitaldistrictcode
        );
      }
      let obj = {
        pageNum: 1,
        pageSize: 10,
        leavehospitaldistrictcodes:
          this.topqueryParams.leavehospitaldistrictcodes,
        sendstates: [2, 3],
        leaveldeptcodes: this.topqueryParams.leaveldeptcodes,
      };
      buidegetTasklist(obj).then((response) => {
        this.userList = response.rows[0].serviceSubtaskList;
        this.total = response.total;
        if (refresh) {
          this.cardlist[0].value =
            Number(response.rows[0].wzx) + Number(response.rows[0].ysf);
          this.cardlist[1].value = response.rows[0].wzx;
          this.cardlist[2].value = response.rows[0].ysf;
          this.ycvalue = response.rows[0].yc;
          this.cardlist[3].value = response.rows[0].fssb;
          this.cardlist[4].value = response.rows[0].dsf;
          // this.cardlist[5].value = response.rows[0].yfs2;
          this.yfsvalue = response.rows[0].yfs;
        }
        this.loading = false;
        this.userList.forEach((item) => {
          let idArray = null;
          if (item.endtime) {
            item.endDay = this.daysBetween(item.endtime);
          }
          if (item.preachform) {
            if (item.endtime) {
              item.preachformson = item.preachform;
              idArray = item.preachform.split(",");
            }
            item.preachform = idArray.map((value) => {
              // æŸ¥æ‰¾id对应的对象
              const item = this.checkboxlist.find(
                (item) => item.value == value
              );
              // å¦‚果找到对应的id,返回label值,否则返回null
              return item ? item.label : null;
            });
          }
        });
        this.total = response.total;
      });
    },
    // æŸ¥çœ‹é—¨è¯Šéšè®¿è¯¦æƒ…
    Referencequestion(row) {
      this.previewVisible = true;
    },
    // æ·»åŠ å¼¹æ¡†æœç´¢
    remoteMethod(query) {
      if (query !== "") {
        this.loading = true;
        setTimeout(() => {
          this.loading = false;
          this.options = this.list.filter((item) => {
            return item.label.toLowerCase().indexOf(query.toLowerCase()) > -1;
          });
        }, 200);
      } else {
        this.options = [];
      }
    },
    // å½±åƒéšè®¿çŠ¶æ€ä¿®æ”¹
    handleStatusChange(row) {
      let text = row.status === "0" ? "启用" : "停用";
      this.$modal
        .confirm('确认要"' + text + '""' + row.userName + '"用户吗?')
        .then(function () {
          return changeUserStatus(row.userId, row.status);
        })
        .then(() => {
          this.$modal.msgSuccess(text + "成功");
        })
        .catch(function () {
          row.status = row.status === "0" ? "1" : "0";
        });
    },
    // è¡¨å•重置
    reset() {
      this.form = {
        userId: undefined,
        deptId: undefined,
        userName: undefined,
        nickName: undefined,
        password: undefined,
        phonenumber: undefined,
        email: undefined,
        sex: undefined,
        status: "0",
        remark: undefined,
        postIds: [],
        roleIds: [],
      };
      this.resetForm("form");
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery(refresh) {
      if (this.topqueryParams.searchscope == 3) {
        this.topqueryParams.leaveldeptcodes = store.getters.belongDepts.map(
          (obj) => obj.deptCode
        );
        this.topqueryParams.leavehospitaldistrictcodes =
          store.getters.belongWards.map((obj) => obj.districtCode);
      }
      this.topqueryParams.pageNum = 1;
      this.topqueryParams.startOutHospTime = this.dateRange[0];
      this.topqueryParams.endOutHospTime = this.dateRange[1];
      this.topqueryParams.startSendDateTime = this.dateRangefs[0];
      this.topqueryParams.endSendDateTime = this.dateRangefs[1];
      this.getList(refresh);
    },
    // æ‚£è€…范围处理
    handleChange(value) {
      let type = value[0];
      let code = value.slice(-1)[0];
      this.topqueryParams.leavehospitaldistrictcodes = [];
      this.topqueryParams.leaveldeptcodes = [];
      if (type == 1) {
        this.topqueryParams.leaveldeptcodes.push(code);
        this.topqueryParams.leavehospitaldistrictcodes = [];
        this.topqueryParams.searchscope = 1;
      } else if (type == 2) {
        this.topqueryParams.leavehospitaldistrictcodes.push(code);
        this.topqueryParams.leaveldeptcodes = [];
        this.topqueryParams.searchscope = 2;
      } else {
        this.topqueryParams.searchscope = 3;
      }
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.dateRange = [];
      this.dateRangefs = [];
      this.topqueryParams = {
        pageNum: 1,
        pageSize: 10,
        sendstate: 6,
        sort: 2, //0 å‡ºé™¢æ—¶é—´(正序)    1 å‡ºé™¢æ—¶é—´(倒序)   2 å‘送时间(正序)    3 å‘送时间(倒序)
        serviceType: 6,
        searchscope: 3,
        visitCount: 1,
        scopetype: [],
        leaveldeptcodes: [],
        leavehospitaldistrictcodes: [],
      };
      this.handleQuery(1);
    },
    handleSelectionChange(rows) {
      this.selectedRows = rows.map((row) => {
        // åˆå§‹åŒ–评分字段
        return {
          ...row,
          authenticity: row.authenticity || 0,
          weekFinish: row.weekFinish || 0,
          standard: row.standard || 0,
          timeliness: row.timeliness || 0,
          library: row.library || 0,
          environment: row.environment || 0,
          doctorSatisfaction: row.doctorSatisfaction || 0,
          nurseSatisfaction: row.nurseSatisfaction || 0,
        };
      });
      if (this.selectedRows.length > 0) {
        this.multiple = false;
      } else {
        this.multiple = true;
      }
    },
    // è®¡ç®—总分
    calculateTotal(row) {
      return (
        (row.authenticity || 0) +
        (row.weekFinish || 0) +
        (row.standard || 0) +
        (row.timeliness || 0) +
        (row.library || 0) +
        (row.environment || 0) +
        (row.doctorSatisfaction || 0) +
        (row.nurseSatisfaction || 0)
      );
    },
    // ä¿å­˜è¯„分
    saveScores() {
      this.selectedRows.forEach((item) => {
        item.createBy = null;
        item.patName = item.sendname;
        item.hospitaldistrictname = item.leavehospitaldistrictname;
      });
      addsatisfaction(this.selectedRows).then((res) => {
        if (res.code == 200) {
          this.$message.success("评分保存成功");
          this.scoreDialogVisible = false;
          this.selectedRows = [];
          this.$refs.userform.clearSelection();
        } else {
          this.$modal.msgWarning("评分保存失败");
          this.scoreDialogVisible = false;
          this.selectedRows = [];
          this.$refs.userform.clearSelection();
        }
      });
      // è¿™é‡Œå¯ä»¥æ·»åŠ ä¿å­˜é€»è¾‘ï¼Œå¦‚è°ƒç”¨API保存评分
    },
    //删除选项
    handleClose(tag) {
      this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1);
    },
    //触发新增输入
    showInput() {
      this.inputVisible = true;
      this.$nextTick((_) => {
        this.$refs.saveTagInput.$refs.input.focus();
      });
    },
    //获取失去焦点触发
    handleInputConfirm() {
      let inputValue = this.inputValue;
      if (inputValue) {
        this.dynamicTags.push(inputValue);
      }
      this.inputVisible = false;
      this.inputValue = "";
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
      this.$router.push({
        path: "/followvisit/QuestionnaireTask",
        query: {
          type: 2,
          serviceType: 6,
        },
      });
    },
    //患者360跳转
    gettoken360(sfzh, drcode, drname) {
      // this.$modal.msgWarning('360功能暂未开通');
      this.postData.YeWuXX.BingRenXX.ZhengJianHM = sfzh;
      query360PatInfo(this.postData).then((res) => {
        if (res.data.url) {
          window.open(res.data.url, "_blank");
          // this.linkUrl = res.data.url;
        } else {
          this.$modal.msgWarning("360查询无结果");
        }
      });
    },
    /** é‡ç½®å¯†ç æŒ‰é’®æ“ä½œ */
    handleResetPwd(row) {
      this.$prompt('请输入"' + row.userName + '"的新密码', "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        closeOnClickModal: false,
        inputPattern: /^.{5,20}$/,
        inputErrorMessage: "用户密码长度必须介于 5 å’Œ 20 ä¹‹é—´",
      })
        .then(({ value }) => {
          resetUserPwd(row.userId, value).then((response) => {
            this.$modal.msgSuccess("修改成功,新密码是:" + value);
          });
        })
        .catch(() => {});
    },
    // å–消按钮
    cancel() {
      this.Labelchange = false;
      this.reset();
    },
    /** æäº¤æŒ‰é’® */
    submitForm: function () {
      this.$refs["form"].validate((valid) => {
        if (valid) {
          this.form.isoperation = 2;
          this.form.notrequiredFlag = 1;
          alterpatient(this.form)
            .then((response) => {
              console.log(response);
            })
            .then(() => {
              this.getList(1);
              this.$modal.msgSuccess("患者过滤成功");
            });
          this.reset();
          this.Labelchange = false;
        }
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const userIds = row.userId || this.ids;
      this.$modal
        .confirm('是否确认删除用户编号为"' + userIds + '"的数据项?')
        .then(function () {
          return delUser(userIds);
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨åœæ­¢
    AllStop() {
      this.$modal
        .confirm("是否停止全部任务?")
        .then(function () {
          return console.log("停止成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgWarning("停止成功");
        })
        .catch(() => {});
    },
    // å…¨éƒ¨å¼€å§‹
    AllStarted() {
      this.$modal
        .confirm("是否开启全部任务?")
        .then(function () {
          return console.log("开启成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("开启成功");
        })
        .catch(() => {});
    },
    // ä»»åŠ¡é‡ç½®
    TaskReset() {
      this.$modal
        .confirm("是否重置选中的任务项?")
        .then(function () {
          return console.log("选中成功");
        })
        .then(() => {
          this.getList(1);
          this.$modal.msgSuccess("重置成功");
        })
        .catch(() => {});
    },
    // è®¾ç½®å‘送时间
    Sendtimesetting() {
      this.modificationVisible = true;
    },
    // è·³è½¬è¯¦æƒ…页
    Seedetails(row) {
      let type = "";
      console.log(row, "rwo");
        if (row.type == 1) {
          type = 1;
        }
      this.$router.push({
        path: "/followvisit/record/detailpage/",
        query: {
          taskid: row.taskid,
          patid: row.patid,
          id: row.id,
          Voicetype: type,
          visitCount: this.topqueryParams.visitCount,
        },
      });
    },
    onSubmit() {},
    // æš‚停服务
    handlestop(row) {
      let objson = row;
      this.$modal
        .confirm(
          '是否确认暂停任务名称为"' +
            row.taskName +
            '患者名称为"' +
            row.sendname +
            '"的数据项?'
        )
        .then(() => {
          getTaskservelist({
            patid: row.patid,
            taskid: row.taskid,
          }).then((res) => {
            if (res.code == 200) {
              objson.sendstate = 4;
              objson.remark = "服务暂停";
              Editsingletaskson(objson).then((res) => {
                if (res.code) {
                  this.$modal.msgSuccess("记录成功");
                  this.getList(1);
                }
              });
            }
          });
        })
        .catch(() => {});
    },
    // æ‚£è€…过滤触发
    handleUpdate(row) {
      particularpatient(row.patid).then((response) => {
        this.form = response.data;
        this.form.filterDrname = store.getters.nickName;
      });
      this.amendtag = true;
      this.Labelchange = true;
    },
    // ä¾¿æ·æŒ‰é’®
    toleadExport(too) {
      if (too == 1) {
        this.topqueryParams.sendstate = 4;
        this.topqueryParams.excep = null;
      } else if (too == 2) {
        this.topqueryParams.excep = 1;
      }
      this.handleQuery();
    },
    /** å¯¼å‡ºæŒ‰é’®æ“ä½œ */
    handleExport() {
      this.topqueryParams.pageNum = null;
      this.topqueryParams.pageSize = null;
      this.download(
        "smartor/serviceSubtask/patItemExport",
        {
          ...this.topqueryParams,
        },
        `user_${new Date().getTime()}.xlsx`
      );
    },
    // å¼‚常列渲染
    tableRowClassName({ row, rowIndex }) {
      if (row.excep == 1) {
        return "warning-row";
      }
      return "";
    },
    getCurrentTime() {
      const now = new Date();
      const year = now.getFullYear();
      const month = String(now.getMonth() + 1).padStart(2, "0");
      const day = String(now.getDate()).padStart(2, "0");
      const hours = String(now.getHours()).padStart(2, "0");
      const minutes = String(now.getMinutes()).padStart(2, "0");
      const seconds = String(now.getSeconds()).padStart(2, "0");
      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
    },
  },
};
</script>
<style lang="scss" scoped>
.el-button--primary.is-plain {
  color: #ffffff;
  background: #409eff;
  border-color: #4fabe9;
}
.document {
  // width: 100px;
  height: 50px;
}
::v-deep.el-table .warning-row {
  background: #eec4c4;
}
.documentf {
  display: flex;
  justify-content: flex-end;
}
.download {
  text-align: center;
  .el-upload__tip {
    font-size: 23px;
  }
  .el-upload__text {
    font-size: 23px;
  }
}
.uploading {
  margin-top: 20px;
  margin: 20px;
  padding: 30px;
  background: #ffffff;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
}
.el-tag + .el-tag {
  margin-left: 10px;
}
.button-new-tag {
  margin-left: 10px;
  height: 32px;
  line-height: 30px;
  padding-top: 0;
  padding-bottom: 0;
}
.input-new-tag {
  width: 90px;
  margin-left: 10px;
  vertical-align: bottom;
}
.drexamine {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 30px;
  background: #daeaf5;
  img {
    width: 100px;
    height: 100px;
  }
}
.qrcode-dialo {
  // text-align: center;
  //   display: flex;
  margin: 20px;
  padding: 30px;
  background: #edf1f7;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
  .topic-dev {
    margin-bottom: 25px;
    font-size: 20px !important;
    .dev-text {
      margin-bottom: 10px;
    }
  }
}
::v-deep.leftvlue .el-card__body {
  background: #f2f8ff;
  color: #324a9b;
}
::v-deep.leftvlue .el-card__body:hover {
  background: #3664d9;
  color: #fff;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-textxga {
  color: #de7897;
}
::v-deep.errleftvlue .el-card__body {
  background: #fdd0d7;
}
::v-deep.errleftvlue .el-card__body:hover {
  background: #f88d96;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
::v-deep.ysfleftvlue .el-card__body {
  background: #d0fdd8;
}
::v-deep.ysfleftvlue .el-card__body:hover {
  background: #8df8a4;
  cursor: pointer; /* é¼ æ ‡æ‚¬æµ®æ—¶å˜ä¸ºæ‰‹å½¢ */
}
.button-bb {
  font-weight: 500;
  background-color: #2ba05c;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-xq {
  font-weight: 500;
  background-color: #409eff;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-sc {
  font-weight: 500;
  background-color: #b3a21f;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
.button-zx {
  background: #4fabe9;
  padding: 5px;
  border-radius: 1px;
  color: #ffffff;
}
::v-deep.el-radio-group {
  span {
    font-size: 24px;
  }
}
.purple-button {
  background-color: #7e22ce;
  border-color: #7e22ce;
  color: #fff;
}
.purple-button:hover,
.purple-button:focus {
  background-color: #9333ea;
  border-color: #9333ea;
}
.purple-button:active {
  background-color: #6b21a8;
  border-color: #6b21a8;
}
.purple-button.is-disabled {
  background-color: #d8b4fe;
  border-color: #d8b4fe;
  opacity: 1; /* ä¿æŒç¦ç”¨çŠ¶æ€é€æ˜Žåº¦ */
}
// é€‰é¡¹å­—体放大
// ::v-deep.el-checkbox-group {
//   span {
//     font-size: 24px;
//   }
// }
</style>
src/views/groupManagement/PatientGroup/index.vue
src/views/groupManagement/PatientGroup/particulars.vue
src/views/groupManagement/PersonnelGroup/index.vue
src/views/groupManagement/PersonnelGroup/particulars.vue
src/views/groupManagement/serviceGroup/index.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,355 @@
<template>
  <div class="app-container">
    <el-row :gutter="20">
      <!-- æœç´¢åŒºåŸŸ -->
      <el-form
        :model="queryParams"
        ref="queryForm"
        size="small"
        :inline="true"
        v-show="showSearch"
        label-width="98px"
      >
        <el-form-item label="服务组名称">
          <el-input
            v-model="queryParams.groupName"
            placeholder="请输入服务组名称"
            @keyup.enter.native="handleQuery"
          ></el-input>
        </el-form-item>
        <el-form-item label="创建人">
          <el-input
            v-model="queryParams.createBy"
            placeholder="请输入创建人"
            @keyup.enter.native="handleQuery"
          ></el-input>
        </el-form-item>
        <el-form-item label="创建时间">
          <el-date-picker
            v-model="dateRange"
            style="width: 240px"
            value-format="yyyy-MM-dd"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
          ></el-date-picker>
        </el-form-item>
        <el-form-item label="状态">
          <el-select v-model="queryParams.status" placeholder="请选择状态">
            <el-option
              v-for="item in statusOptions"
              :key="item.value"
              :label="item.label"
              :value="item.value"
            ></el-option>
          </el-select>
        </el-form-item>
        <el-form-item>
          <el-button
            type="primary"
            icon="el-icon-search"
            size="medium"
            @click="handleQuery"
            >搜索</el-button
          >
          <el-button icon="el-icon-refresh" size="medium" @click="resetQuery"
            >重置</el-button
          >
        </el-form-item>
      </el-form>
      <el-divider></el-divider>
      <!-- æ“ä½œæŒ‰é’®åŒºåŸŸ -->
      <el-row :gutter="10" class="mb8">
        <el-col :span="1.5">
          <el-button
            type="primary"
            icon="el-icon-plus"
            size="medium"
            @click="handleAdd"
            >新建服务组</el-button
          >
        </el-col>
        <right-toolbar
          :showSearch.sync="showSearch"
          @queryTable="getList"
        ></right-toolbar>
      </el-row>
      <!-- æœåŠ¡ç»„åˆ—è¡¨ -->
      <el-table
        v-loading="loading"
        :data="groupList"
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="55" align="center" />
        <el-table-column
          label="服务组ID"
          align="center"
          key="groupId"
          prop="groupId"
          width="100"
        />
        <el-table-column
          label="服务组名称"
          align="center"
          key="groupName"
          prop="groupName"
          :show-overflow-tooltip="true"
        />
        <el-table-column
          label="描述"
          align="center"
          key="groupDesc"
          prop="groupDesc"
          :show-overflow-tooltip="true"
        />
        <el-table-column
          label="关联任务数"
          align="center"
          key="taskCount"
          prop="taskCount"
          width="100"
        />
        <el-table-column
          label="关联患者数"
          align="center"
          key="patientCount"
          prop="patientCount"
          width="100"
        />
        <el-table-column
          label="状态"
          align="center"
          key="status"
          prop="status"
          width="100"
        >
          <template slot-scope="scope">
            <el-tag :type="scope.row.status === 1 ? 'success' : 'info'">
              {{ scope.row.status === 1 ? "启用" : "停用" }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          label="创建人"
          align="center"
          key="createBy"
          prop="createBy"
        />
        <el-table-column
          label="创建时间"
          align="center"
          prop="createTime"
          width="180"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.createTime) }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          align="center"
          width="300"
          class-name="small-padding fixed-width"
        >
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              icon="el-icon-view"
              @click="handleView(scope.row)"
              >详情</el-button
            >
            <el-button
              size="mini"
              type="text"
              icon="el-icon-delete"
              class="delete-btn"
              @click="handleDelete(scope.row)"
              >删除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <!-- åˆ†é¡µ -->
      <pagination
        v-show="total > 0"
        :total="total"
        :page.sync="queryParams.pageNum"
        :limit.sync="queryParams.pageSize"
        @pagination="getList"
      />
    </el-row>
  </div>
</template>
<script>
import { listServiceGroup, delServiceGroup } from "@/api/system/serviceGroup";
export default {
  name: "ServiceGroupList",
  dicts: ["sys_normal_disable"],
  data() {
    return {
      // é®ç½©å±‚
      loading: false,
      // é€‰ä¸­æ•°ç»„
      ids: [],
      // éžå•个禁用
      single: true,
      // éžå¤šä¸ªç¦ç”¨
      multiple: true,
      // æ˜¾ç¤ºæœç´¢æ¡ä»¶
      showSearch: true,
      // æ€»æ¡æ•°
      total: 5,
      // æœåŠ¡ç»„è¡¨æ ¼æ•°æ®
      groupList: [
        {
          groupId: "SG2025001",
          groupName: "糖尿病随访管理组",
          groupDesc: "专门负责糖尿病患者的定期随访和健康管理服务",
          taskCount: 5,
          patientCount: 150,
          status: 1,
          createBy: "张医生",
          createTime: "2025-10-15 14:30:00",
        },
        {
          groupId: "SG2025002",
          groupName: "高血压健康管理组",
          groupDesc: "高血压患者的用药指导和血压监测管理",
          taskCount: 3,
          patientCount: 89,
          status: 1,
          createBy: "李医生",
          createTime: "2025-10-10 09:15:00",
        },
        {
          groupId: "SG2025003",
          groupName: "术后康复随访组",
          groupDesc: "外科手术后患者的康复指导和随访管理",
          taskCount: 2,
          patientCount: 45,
          status: 0,
          createBy: "王医生",
          createTime: "2025-09-28 16:20:00",
        },
        {
          groupId: "SG2025004",
          groupName: "孕产期健康管理组",
          groupDesc: "孕期和产后妇女的健康监测和指导服务",
          taskCount: 4,
          patientCount: 67,
          status: 1,
          createBy: "赵医生",
          createTime: "2025-10-05 11:30:00",
        },
        {
          groupId: "SG2025005",
          groupName: "慢性病综合管理组",
          groupDesc: "多种慢性病患者的综合健康管理服务",
          taskCount: 6,
          patientCount: 203,
          status: 1,
          createBy: "张医生",
          createTime: "2025-09-15 08:45:00",
        },
      ],
      // æ—¥æœŸèŒƒå›´
      dateRange: [],
      // çŠ¶æ€é€‰é¡¹
      statusOptions: [
        { value: 1, label: "启用" },
        { value: 0, label: "停用" },
      ],
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
        pageNum: 1,
        pageSize: 10,
        groupName: undefined,
        createBy: undefined,
        status: undefined,
      },
    };
  },
  created() {
    this.getList();
  },
  methods: {
    /** æŸ¥è¯¢æœåŠ¡ç»„åˆ—è¡¨ */
    getList() {
      // this.loading = true;
      // listServiceGroup(
      //   this.addDateRange(this.queryParams, this.dateRange)
      // ).then((response) => {
      //   this.groupList = response.rows;
      //   this.total = response.total;
      //   this.loading = false;
      // });
    },
    /** æœç´¢æŒ‰é’®æ“ä½œ */
    handleQuery() {
      this.queryParams.pageNum = 1;
      this.getList();
    },
    /** é‡ç½®æŒ‰é’®æ“ä½œ */
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.handleQuery();
    },
    // å¤šé€‰æ¡†é€‰ä¸­æ•°æ®
    handleSelectionChange(selection) {
      this.ids = selection.map((item) => item.groupId);
      this.single = selection.length !== 1;
      this.multiple = !selection.length;
    },
    /** æ–°å¢žæŒ‰é’®æ“ä½œ */
    handleAdd() {
      this.$router.push("/group/serviceGroupPar");
    },
    /** è¯¦æƒ…按钮操作 */
    handleView(row, type) {
      this.$router.push({
        path: "/group/serviceGroupPar",
        query: {
          type: this.topqueryParams.type,
        },
      });
    },
    /** åˆ é™¤æŒ‰é’®æ“ä½œ */
    handleDelete(row) {
      const groupIds = row.groupId || this.ids;
      this.$modal
        .confirm('是否确认删除服务组编号为"' + groupIds + '"的数据项?')
        .then(() => {
          return delServiceGroup(groupIds);
        })
        .then(() => {
          this.getList();
          this.$modal.msgSuccess("删除成功");
        })
        .catch(() => {});
    },
  },
};
</script>
<style scoped>
.delete-btn {
  color: #f56c6c;
}
.app-container {
  padding: 20px;
}
.mb8 {
  margin-bottom: 8px;
}
</style>
src/views/groupManagement/serviceGroup/particulars.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,518 @@
<template>
  <div class="app-container">
    <!-- é¡¶éƒ¨é¢åŒ…屑导航 -->
    <el-breadcrumb separator-class="el-icon-arrow-right" class="breadcrumb">
      <el-breadcrumb-item :to="{ path: '/system/serviceGroup' }">服务组管理</el-breadcrumb-item>
      <el-breadcrumb-item>服务组详情</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- é¡¶éƒ¨åŸºç¡€ä¿¡æ¯ -->
    <el-card class="base-info-card" shadow="never">
      <div slot="header" class="clearfix">
        <span class="card-title">服务组基础信息</span>
        <el-button
          style="float: right; padding: 3px 0"
          type="text"
          icon="el-icon-edit"
          @click="handleEdit"
          >编辑</el-button
        >
      </div>
      <el-row :gutter="20">
        <el-col :span="8">
          <div class="info-item">
            <label>服务组名称:</label>
            <span>{{ baseInfo.groupName }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <label>服务组ID:</label>
            <span>{{ baseInfo.groupId }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <label>状态:</label>
            <el-tag :type="baseInfo.status === 1 ? 'success' : 'info'">
              {{ baseInfo.status === 1 ? '启用' : '停用' }}
            </el-tag>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="24">
          <div class="info-item">
            <label>描述:</label>
            <span>{{ baseInfo.groupDesc || '暂无描述' }}</span>
          </div>
        </el-col>
      </el-row>
      <el-row :gutter="20">
        <el-col :span="8">
          <div class="info-item">
            <label>创建人:</label>
            <span>{{ baseInfo.createBy }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <label>创建时间:</label>
            <span>{{ parseTime(baseInfo.createTime) }}</span>
          </div>
        </el-col>
        <el-col :span="8">
          <div class="info-item">
            <label>更新时间:</label>
            <span>{{ parseTime(baseInfo.updateTime) }}</span>
          </div>
        </el-col>
      </el-row>
    </el-card>
    <!-- ä¸­éƒ¨å…³è”任务 -->
    <el-card class="task-card" shadow="never">
      <div slot="header" class="clearfix">
        <span class="card-title">关联任务</span>
        <el-button
          style="float: right; margin-left: 10px;"
          type="primary"
          icon="el-icon-plus"
          size="mini"
          @click="handleAddTask"
          >关联任务</el-button
        >
        <el-button
          style="float: right;"
          type="text"
          icon="el-icon-setting"
          @click="handleManageTasks"
          >管理</el-button
        >
      </div>
      <el-table
        v-loading="taskLoading"
        :data="taskList"
        style="width: 100%"
      >
        <el-table-column
          label="任务名称"
          prop="taskName"
          min-width="200"
          :show-overflow-tooltip="true"
        />
        <el-table-column
          label="任务类型"
          prop="taskType"
          width="120"
          align="center"
        >
          <template slot-scope="scope">
            <dict-tag :options="dict.type.task_type" :value="scope.row.taskType"/>
          </template>
        </el-table-column>
        <el-table-column
          label="任务状态"
          prop="sendState"
          width="100"
          align="center"
        >
          <template slot-scope="scope">
            <dict-tag :options="dict.type.task_status" :value="scope.row.sendState"/>
          </template>
        </el-table-column>
        <el-table-column
          label="总任务/已随访"
          width="120"
          align="center"
        >
          <template slot-scope="scope">
            <span>{{ scope.row.totalCount }}/{{ scope.row.completedCount }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="创建时间"
          prop="createTime"
          width="180"
          align="center"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.createTime) }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          width="200"
          align="center"
          fixed="right"
        >
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              @click="handleViewTask(scope.row)"
              >查看</el-button
            >
            <el-button
              size="mini"
              type="text"
              class="delete-btn"
              @click="handleRemoveTask(scope.row)"
              >移除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <div v-if="taskList.length === 0 && !taskLoading" class="empty-text">
        æš‚无关联任务,请先关联任务
      </div>
    </el-card>
    <!-- åº•部关联患者 -->
    <el-card class="patient-card" shadow="never">
      <div slot="header" class="clearfix">
        <span class="card-title">关联患者</span>
        <el-button
          style="float: right; margin-left: 10px;"
          type="primary"
          icon="el-icon-plus"
          size="mini"
          @click="handleAddPatient"
          >关联患者</el-button
        >
        <el-button
          style="float: right;"
          type="text"
          icon="el-icon-setting"
          @click="handleManagePatients"
          >管理</el-button
        >
      </div>
      <el-table
        v-loading="patientLoading"
        :data="patientList"
        style="width: 100%"
      >
        <el-table-column
          label="患者姓名"
          prop="patientName"
          width="120"
          align="center"
        />
        <el-table-column
          label="性别"
          prop="gender"
          width="80"
          align="center"
        >
          <template slot-scope="scope">
            <dict-tag :options="dict.type.sys_user_sex" :value="scope.row.gender"/>
          </template>
        </el-table-column>
        <el-table-column
          label="年龄"
          prop="age"
          width="80"
          align="center"
        />
        <el-table-column
          label="手机号"
          prop="phone"
          width="130"
          align="center"
        />
        <el-table-column
          label="病历号"
          prop="medicalRecordNo"
          width="120"
          align="center"
        />
        <el-table-column
          label="诊断"
          prop="diagnosis"
          min-width="200"
          :show-overflow-tooltip="true"
        />
        <el-table-column
          label="关联时间"
          prop="relationTime"
          width="180"
          align="center"
        >
          <template slot-scope="scope">
            <span>{{ parseTime(scope.row.relationTime) }}</span>
          </template>
        </el-table-column>
        <el-table-column
          label="操作"
          width="150"
          align="center"
          fixed="right"
        >
          <template slot-scope="scope">
            <el-button
              size="mini"
              type="text"
              @click="handleViewPatient(scope.row)"
              >详情</el-button
            >
            <el-button
              size="mini"
              type="text"
              class="delete-btn"
              @click="handleRemovePatient(scope.row)"
              >移除</el-button
            >
          </template>
        </el-table-column>
      </el-table>
      <div v-if="patientList.length === 0 && !patientLoading" class="empty-text">
        æš‚无关联患者,请先关联患者
      </div>
    </el-card>
  </div>
</template>
<script>
import { getServiceGroup, getGroupTasks, getGroupPatients } from "@/api/system/serviceGroup";
export default {
  name: "ServiceGroupDetail",
  dicts: ['sys_user_sex', 'task_status', 'task_type'],
  data() {
    return {
      groupId: this.$route.params.groupId || 'SG2025001',
      baseInfoLoading: false,
      taskLoading: false,
      patientLoading: false,
      // æµ‹è¯•数据 - åŸºç¡€ä¿¡æ¯
      baseInfo: {
        groupId: 'SG2025001',
        groupName: '糖尿病随访管理服务组',
        groupDesc: '专门负责糖尿病患者的定期随访、用药指导和健康管理服务,提高患者治疗依从性',
        status: 1,
        createBy: '张医生',
        createTime: '2025-10-15 14:30:00',
        updateTime: '2025-10-20 09:15:00'
      },
      // æµ‹è¯•数据 - å…³è”任务
      taskList: [
        {
          taskId: 'T1001',
          taskName: '糖尿病月度随访评估',
          taskType: 1,
          sendState: 2,
          totalCount: 150,
          completedCount: 120,
          createTime: '2025-10-18 10:00:00'
        },
        {
          taskId: 'T1002',
          taskName: '血糖监测依从性问卷',
          taskType: 2,
          sendState: 1,
          totalCount: 150,
          completedCount: 85,
          createTime: '2025-10-16 14:30:00'
        },
        {
          taskId: 'T1003',
          taskName: '并发症筛查提醒任务',
          taskType: 3,
          sendState: 3,
          totalCount: 150,
          completedCount: 45,
          createTime: '2025-10-10 09:20:00'
        }
      ],
      // æµ‹è¯•数据 - å…³è”患者
      patientList: [
        {
          patientId: 'P2025001',
          patientName: '王小明',
          gender: 1,
          age: 45,
          phone: '13800138000',
          medicalRecordNo: 'MR2025001001',
          diagnosis: '2型糖尿病,伴有周围神经并发症',
          relationTime: '2025-10-16 09:00:00'
        },
        {
          patientId: 'P2025002',
          patientName: '李小红',
          gender: 0,
          age: 62,
          phone: '13900139000',
          medicalRecordNo: 'MR2025001002',
          diagnosis: '1型糖尿病,胰岛素依赖型',
          relationTime: '2025-10-17 14:30:00'
        },
        {
          patientId: 'P2025003',
          patientName: '赵建国',
          gender: 1,
          age: 58,
          phone: '13600136000',
          medicalRecordNo: 'MR2025001003',
          diagnosis: '2型糖尿病,伴有糖尿病肾病',
          relationTime: '2025-10-18 11:20:00'
        }
      ]
    };
  },
  created() {
    this.groupId = this.$route.params.groupId;
    this.getDetail();
    this.getTaskList();
    this.getPatientList();
  },
  methods: {
    /** èŽ·å–æœåŠ¡ç»„è¯¦æƒ… */
    getDetail() {
      // this.baseInfoLoading = true;
      // getServiceGroup(this.groupId).then(response => {
      //   this.baseInfo = response.data;
      //   this.baseInfoLoading = false;
      // });
    },
    /** èŽ·å–å…³è”ä»»åŠ¡åˆ—è¡¨ */
    getTaskList() {
      // this.taskLoading = true;
      // getGroupTasks(this.groupId).then(response => {
      //   this.taskList = response.rows;
      //   this.taskLoading = false;
      // });
    },
    /** èŽ·å–å…³è”æ‚£è€…åˆ—è¡¨ */
    getPatientList() {
      // this.patientLoading = true;
      // getGroupPatients(this.groupId).then(response => {
      //   this.patientList = response.rows;
      //   this.patientLoading = false;
      // });
    },
    /** ç¼–辑服务组 */
    handleEdit() {
      this.$router.push(`/system/serviceGroup/edit/${this.groupId}`);
    },
    /** å…³è”任务 */
    handleAddTask() {
      this.$router.push(`/system/serviceGroup/tasks/${this.groupId}?action=add`);
    },
    /** ç®¡ç†ä»»åŠ¡ */
    handleManageTasks() {
      this.$router.push(`/system/serviceGroup/tasks/${this.groupId}`);
    },
    /** æŸ¥çœ‹ä»»åŠ¡è¯¦æƒ… */
    handleViewTask(task) {
      // æ ¹æ®ä»»åŠ¡ç±»åž‹è·³è½¬åˆ°ä¸åŒçš„ä»»åŠ¡è¯¦æƒ…é¡µ
      const routeMap = {
        1: '/followvisit/particty',
        2: '/followvisit/QuestionnaireTask',
        3: '/followvisit/Missioncreation'
      };
      const route = routeMap[task.type] || '/followvisit/task';
      this.$router.push(`${route}?id=${task.taskid}`);
    },
    /** ç§»é™¤ä»»åŠ¡ */
    handleRemoveTask(task) {
      this.$modal.confirm(`是否确认移除任务"${task.taskName}"?`).then(() => {
        // è°ƒç”¨ç§»é™¤ä»»åŠ¡API
        removeTaskFromGroup(this.groupId, task.taskid).then(() => {
          this.$modal.msgSuccess("移除成功");
          this.getTaskList();
        });
      }).catch(() => {});
    },
    /** å…³è”患者 */
    handleAddPatient() {
      this.$router.push(`/system/serviceGroup/patients/${this.groupId}?action=add`);
    },
    /** ç®¡ç†æ‚£è€… */
    handleManagePatients() {
      this.$router.push(`/system/serviceGroup/patients/${this.groupId}`);
    },
    /** æŸ¥çœ‹æ‚£è€…详情 */
    handleViewPatient(patient) {
      this.$router.push(`/system/patient/detail/${patient.patientId}`);
    },
    /** ç§»é™¤æ‚£è€… */
    handleRemovePatient(patient) {
      this.$modal.confirm(`是否确认移除患者"${patient.patientName}"?`).then(() => {
        // è°ƒç”¨ç§»é™¤æ‚£è€…API
        removePatientFromGroup(this.groupId, patient.patientId).then(() => {
          this.$modal.msgSuccess("移除成功");
          this.getPatientList();
        });
      }).catch(() => {});
    },
  }
};
</script>
<style scoped>
.app-container {
  padding: 20px;
}
.breadcrumb {
  margin-bottom: 20px;
}
.base-info-card,
.task-card,
.patient-card {
  margin-bottom: 20px;
}
.card-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
.info-item {
  margin-bottom: 15px;
  line-height: 1.5;
}
.info-item label {
  display: inline-block;
  width: 100px;
  color: #606266;
  font-weight: normal;
  text-align: right;
  margin-right: 10px;
}
.info-item span {
  color: #303133;
}
.empty-text {
  text-align: center;
  color: #909399;
  padding: 40px 0;
  font-size: 14px;
}
.delete-btn {
  color: #f56c6c;
}
.clearfix:before,
.clearfix:after {
  display: table;
  content: "";
}
.clearfix:after {
  clear: both;
}
</style>
src/views/knowledge/education/compilequer/index copy.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,1322 @@
<template>
  <div class="Questionnairemanagement">
    <!-- å·¦ä¾§æ  -->
    <div class="sidecolumn">
      <el-steps finish-status="success" :active="Editprogress" simple>
        <el-step>
          <template slot="title">
            <span style="cursor: pointer" @click="Editprogress = 1"
              >基础信息设置</span
            >
          </template>
        </el-step>
        <el-step>
          <template slot="title">
            <span style="cursor: pointer" @click="Editprogress = 2"
              >宣教内容</span
            >
          </template>
        </el-step>
      </el-steps>
    </div>
    <!-- å³ä¾§æ•°æ® -->
    <div class="leftvlue">
      <!-- åŸºæœ¬ä¿¡æ¯ -->
      <div v-if="Editprogress == 1">
        <div class="leftvlue-jbxx">基本信息</div>
        <el-divider></el-divider>
        <el-form
          :model="ruleForm"
          :rules="rules"
          ref="ruleForm"
          label-width="100px"
          class="demo-ruleForm"
        >
          <el-form-item label="宣教分类" prop="region">
            <el-select
              v-model="ruleForm.assortid"
              size="medium"
              filterable
              placeholder="请选择分类"
            >
              <el-option-group
                v-for="group in sortlist"
                :key="group.id"
                :label="group.assortname"
              >
                <el-option
                  v-for="item in group.heLibraryAssortList"
                  :key="item.id"
                  :label="item.assortname"
                  :value="item.id"
                >
                </el-option>
              </el-option-group>
            </el-select>
          </el-form-item>
          <el-row>
            <el-col :span="12"> </el-col>
            <el-col :span="12"> </el-col>
          </el-row>
          <el-form-item label="宣教标题" prop="preachname">
            <div style="width: 30%">
              <el-input
                v-model="ruleForm.preachname"
                placeholder="请输入标题"
              ></el-input>
            </div>
          </el-form-item>
          <el-form-item label="宣教描述" prop="preachcontent">
            <div style="width: 60%">
              <el-input
                type="textarea"
                :rows="2"
                v-model="ruleForm.preachcontent"
                placeholder="请输入描述"
              ></el-input>
            </div>
          </el-form-item>
          <el-form-item label="通知变量" prop="name">
            <div style="margin-bottom: 5px" v-for="item in variablelist">
              <el-row>
                <el-col :span="5">
                  <el-input
                    v-model="item.variatename"
                    placeholder="请输入变量名"
                  ></el-input>
                </el-col>
                <el-col :span="8" :offset="1">
                  <el-input
                    v-model="item.variate"
                    placeholder="请输入变量内容"
                  ></el-input>
                </el-col>
                <el-col :span="8" :offset="1">
                  <el-button
                    type="success"
                    icon="el-icon-plus"
                    circle
                    @click="addvariable(item)"
                  ></el-button>
                  <el-button
                    v-if="!item.default"
                    type="danger"
                    icon="el-icon-delete"
                    circle
                    @click="delvariable(item)"
                  ></el-button>
                </el-col>
              </el-row>
            </div>
          </el-form-item>
          <el-form-item label="文件" prop="sickness">
            <div style="width: 40%">
              <el-upload
                class="upload-demo"
                action="https://jsonplaceholder.typicode.com/posts/"
                :on-change="handleChange"
                :file-list="fileList"
              >
                <el-button size="small" type="primary">点击上传</el-button>
                <div slot="tip" class="el-upload__tip">
                  åªèƒ½ä¸Šä¼ jpg/png/xsl文件,且不超过50mb
                </div>
              </el-upload>
            </div>
          </el-form-item>
          <el-form-item label="标签" prop="desc">
            <div class="xinz-inf">
              <el-tag
                :key="tag.tagname"
                type="success"
                v-for="tag in dynamicTags"
                closable
                :disable-transitions="false"
                @close="handleClosetag(tag)"
              >
                {{ tag.tagname }}
              </el-tag>
              <el-select
                v-model="inputValue"
                v-if="inputVisible"
                @change="handleInputConfirm"
                filterable
                remote
                allow-create
                reserve-keyword
                default-first-option
                :remote-method="remoteMethodtag"
                :loading="loading"
                placeholder="请选择"
              >
                <el-option
                  v-for="item in optionstag"
                  :key="item.tagid"
                  :label="item.tagname"
                  :value="item.tagname"
                >
                </el-option>
              </el-select>
              <el-button
                v-else
                class="button-new-tag"
                size="small"
                @click="showInput"
                >+ æ–°å¢žæ ‡ç­¾</el-button
              >
            </div>
          </el-form-item>
          <el-row :gutter="20">
            <el-col :span="6">
              <el-form-item label="版本号" prop="name">
                <el-input
                  v-model="ruleForm.version"
                  placeholder="默认1.0.1"
                ></el-input> </el-form-item
            ></el-col>
            <el-col :span="9">
              <el-form-item label="可用状态" prop="region">
                <el-radio-group v-model="ruleForm.isAvailable">
                  <el-radio
                    v-for="(item, index) in usable"
                    :label="item.value"
                    >{{ item.label }}</el-radio
                  >
                </el-radio-group>
              </el-form-item></el-col
            >
          </el-row>
          <el-form-item label="宣教方式" prop="region">
            <el-select
              v-model="ruleForm.suitway"
              size="medium"
              multiple
              filterable
              placeholder="请选择分类"
            >
              <el-option
                class="ruleFormaa"
                v-for="item in mode"
                :key="item.label"
                :label="item.label"
                :value="item.label"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="适用疾病" prop="region">
            <el-button type="warning" @click="$refs.child.handleAddpatient()"
              >添加疾病</el-button
            >
          </el-form-item>
          <el-form-item label="适用院区" prop="region">
            <el-select
              v-model="ruleForm.campus"
              size="medium"
              multiple
              filterable
              placeholder="请选择分类"
            >
              <el-option
                class="ruleFormaa"
                v-for="item in courtyardlist"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="适用科室" prop="region">
            <el-cascader
              v-model="tempDetpRelevanceslist"
              :options="deptList"
              :props="props"
              :show-all-levels="false"
              clearable
            >
              <template slot-scope="{ node, data }">
                <span>{{ data.deptName }}</span>
                <span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
              </template>
            </el-cascader>
          </el-form-item>
          <el-form-item>
            <el-button type="success" @click="nextstep('ruleForm')"
              >下一步</el-button
            >
            <el-button type="success" @click="Departmenttreatment('ruleForm')"
              >保存</el-button
            >
            <el-button type="info" @click="closeFm('ruleForm')">关闭</el-button>
          </el-form-item>
        </el-form>
      </div>
      <!-- å®£æ•™å†…容 -->
      <div v-if="Editprogress == 2">
        <el-row :gutter="20">
          <el-col :span="4">
            <div class="leftvlue-jbxx">宣教内容</div>
          </el-col>
          <el-col :offset="16" :span="4">
            <el-upload
              class="upload-demo"
              :action="uploadImgUrlword"
              :on-success="uploadEditorSuccessword"
              :on-error="uploadEditorErrorword"
              :before-upload="beforeEditorUploadword"
              :headers="headers"
            >
              <el-button size="small" type="primary">word文件上传</el-button>
            </el-upload>
          </el-col>
        </el-row>
        <div>
          <el-form
            :model="ruleForm"
            :rules="rules"
            ref="ruleForm"
            label-width="100px"
            class="demo-ruleForm"
          >
            <!-- <el-row :gutter="20">
              <el-col :span="12">
                <el-form-item label="资料形式" prop="region">
                  <el-select
                    v-model="ruleForm.shape"
                    placeholder="请选择内容形式"
                  >
                    <el-option
                      v-for="item in xjxsoptions"
                      :key="item.value"
                      :label="item.label"
                      :value="item.value"
                    >
                    </el-option>
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col :span="12"> -->
            <!-- </el-col>
            </el-row> -->
          </el-form>
        </div>
        <!-- <div>
          <div id="quillEditorQiniu">
            <el-upload
              class="avatar-uploader"
              :action="uploadImgUrl"
              :accept="'image/*,video/*'"
              :show-file-list="false"
              :on-success="uploadEditorSuccess"
              :on-error="uploadEditorError"
              :before-upload="beforeEditorUpload"
              :headers="headers"
            >
            </el-upload>
            <quill-editor
              class="editor"
              v-model="content"
              ref="customQuillEditor"
              :options="editorOption"
              @blur="onEditorBlur"
              @focus="onEditorFocus"
              @change="onEditorChange"
            >
            </quill-editor>
          </div>
        </div> -->
        <!-- æ–°ç»„ä»¶ -->
        <div style="border: 1px solid #ccc; margin: 10px">
          <Toolbar
            style="border-bottom: 1px solid #ccc"
            :editor="editor"
            :defaultConfig="toolbarConfig"
            :mode="modes"
          />
          <Editor
            style="height: 500px; overflow-y: hidden"
            v-model="content"
            :defaultConfig="editorConfig"
            :mode="modes"
            @onCreated="onCreated"
          />
        </div>
        <div>
          <el-button @click="laststep('ruleForm')">上一步</el-button>
          <el-button type="success" @click="Departmenttreatment('ruleForm')"
            >保存</el-button
          >
          <el-button type="warning" @click="Departmenttreatment('ruleForm')"
            >另存新版本</el-button
          >
          <el-button type="info" @click="closeFm('ruleForm')">关闭</el-button>
        </div>
      </div>
    </div>
    <!-- æ·»åŠ é€‚ç”¨ç–¾ç—…çª—å£ -->
    <Optional-Form
      ref="child"
      :dialogVisiblepatient="dialogVisiblepatient"
      :overallCase="illnesslist"
      @addoption="dialogVisiblepatient = false"
      @kkoption="dialogVisiblepatient = true"
    />
  </div>
</template>
<script>
import { quillEditor } from "vue-quill-editor";
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
import axios from "axios";
import {
  getheLibraryAssort,
  delheLibraryAssort,
  addheLibraryAssort,
  addtargetillness,
  getlibrarylist,
  dellibraryinfo,
  deltargetillness,
  compilelibrary,
  addrichText,
  getlibraryinfo,
  getillnesslist,
  illnesslistget,
  getillness,
} from "@/api/AiCentre/index";
import OptionalForm from "@/components/OptionalForm"; //正则组件
import { listDept } from "@/api/system/dept";
// import * as Quill from "quill";
import Quill from "quill";
import { listtag } from "@/api/system/label";
import store from "@/store";
// è¿™é‡Œå¼•入修改过的video模块并注册
import Video from "./video";
Quill.register(Video, true);
//获取登录token,引入文件,如果只是简单测试,没有上传文件是否登录的限制的话,
//这个token可以不用获取,文件可以不引入,把上面对应的上传文件携带请求头  :headers="headers" è¿™ä¸ªä»£ç åˆ æŽ‰å³å¯
import { getToken } from "@/utils/auth";
const toolbarOptions = [
  ["bold", "italic", "underline", "strike"], // toggled buttons
  ["blockquote", "code-block"],
  [{ header: 1 }, { header: 2 }], // custom button values
  [{ list: "ordered" }, { list: "bullet" }],
  [{ script: "sub" }, { script: "super" }], // superscript/subscript
  [{ indent: "-1" }, { indent: "+1" }], // outdent/indent
  [{ direction: "rtl" }], // text direction
  [{ size: ["small", false, "large", "huge"] }], // custom dropdown
  [{ header: [1, 2, 3, 4, 5, 6, false] }],
  [{ color: [] }, { background: [] }], // dropdown with defaults from theme
  [{ font: [] }],
  [{ align: [] }],
  ["link", "image", "video"],
  ["clean"], // remove formatting button
];
export default {
  name: "aEducationinfo",
  components: { OptionalForm, Editor, Toolbar },
  data() {
    return {
      editor: null,
      content: "<p>hello</p>",
      toolbarConfig: {},
      editorConfig: {
        placeholder: "请输入内容...",
        menus: [
          "head",
          "bold",
          "italic",
          "underline",
          "image",
          "link",
          "list",
          "undo",
          "redo",
          "file", // æ·»åŠ è‡ªå®šä¹‰æ–‡ä»¶ä¸Šä¼ èœå•
        ],
        uploadImgServer: process.env.VUE_APP_BASE_API + "/common/uploadSort", // å›¾ç‰‡ä¸Šä¼ æŽ¥å£
        uploadImgHeaders: {
          Authorization: "Bearer " + getToken(),
        }, // è‡ªå®šä¹‰ä¸Šä¼ çš„ headers
        uploadImgParams: { key: "value" }, // è‡ªå®šä¹‰ä¸Šä¼ çš„参数
        uploadImgMaxSize: 2 * 1024 * 1024, // å›¾ç‰‡æœ€å¤§å¤§å°ï¼Œå•位 Byte
        uploadImgMaxLength: 1, // ä¸€æ¬¡æœ€å¤šä¸Šä¼ å›¾ç‰‡æ•°é‡
        uploadImgTimeout: 3 * 60 * 1000, // è¶…时时间,单位 ms
        uploadImgHooks: {
          customInsert: (insertImgFn, result) => {
            const url = result.url; // èŽ·å–å›¾ç‰‡åœ°å€
            insertImgFn(url); // æ’入图片
          },
        },
        customMenus: {
          file: {
            tip: "上传文件",
            click: (editor) => {
              const input = document.createElement("input");
              input.type = "file";
              input.accept =
                "application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document"; // æ”¯æŒçš„æ–‡ä»¶ç±»åž‹
              input.onchange = (e) => {
                const file = e.target.files[0];
                if (!file) return;
                const formData = new FormData();
                formData.append("file", file);
                // ç¡®ä¿ process.env.VUE_APP_BASE_API æ˜¯æ­£ç¡®çš„
                const uploadUrl =
                  process.env.VUE_APP_BASE_API + "/common/uploadSort";
                axios
                  .post(uploadUrl, formData, {
                    headers: {
                      Authorization: "Bearer " + getToken(),
                    },
                  })
                  .then((res) => {
                    const url = res.data.url; // èŽ·å–æ–‡ä»¶åœ°å€
                    // æ’入文件链接作为普通文本
                    editor.txt.append(url + " ");
                    // æˆ–者插入文件链接作为超链接
                    // editor.cmd.do('insertLink', { name: '文件链接', url: url });
                  })
                  .catch((err) => {
                    console.error("文件上传失败", err);
                  });
              };
              input.click();
            },
          },
        },
      },
      modes: "default", // or 'simple'
      headers: {
        Authorization: "Bearer " + getToken(),
      },
      uploadImgUrl: process.env.VUE_APP_BASE_API + "/common/uploadSort",
      uploadImgUrlword: process.env.VUE_APP_BASE_API + "/common/uploadShow",
      uploadUrlPath: "没有文件上传",
      quillUpdateImg: false,
      fileList: [
        {
          name: "food.jpeg",
          url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
        },
        {
          name: "food2.jpeg",
          url: "https://fuss10.elemecdn.com/3/63/4e7f3a15429bfda99bce42a18cdd1jpeg.jpeg?imageMogr2/thumbnail/360x360/format/webp/quality/100",
        },
      ],
      content: `<p>测试</p><video class="ql-video" controls="controls" controlslist="nofullscreen" type="video/mp4" style="object-fit:fill;width: 100%;" preload="auto" playsinline="true" x-webkit-airplay="allow" x5-video-orientation="portraint" x5-playsinline="true" x5-video-player-fullscreen="true" src="http://218.108.11.22:8093/profile-api/upload/vadio/营养泵操作规范.mp4"></video><video class="ql-video" controls="controls" controlslist="nofullscreen" type="video/mp4" style="object-fit:fill;width: 100%;" preload="auto" playsinline="true" x-webkit-airplay="allow" x5-video-orientation="portraint" x5-playsinline="true" x5-video-player-fullscreen="true" src="http://218.108.11.22:8093/profile-api/upload/vadio/注射器推注.mp4"></video><p>321</p>"`, //最终保存的内容
      fileName: "", //文件名
      dynamicTags: [],
      inputVisible: false,
      illnessVisible: false,
      dialogVisiblepatient: false, //适用疾病窗口
      inputValue: "",
      // å¯Œæ–‡æœ¬
      editorOption: {
        placeholder: "你想说什么?",
        modules: {
          imageResize: {
            displayStyles: {
              backgroundColor: "black",
              border: "none",
              color: "white",
            },
            modules: ["Resize", "DisplaySize", "Toolbar"],
          },
          toolbar: {
            container: toolbarOptions, // å·¥å…·æ 
            handlers: {
              image: function (value) {
                if (value) {
                  document
                    .querySelector("#quillEditorQiniu .avatar-uploader input")
                    .click();
                } else {
                  this.quill.format("image", false);
                }
              },
              video: function (value) {
                if (value) {
                  document
                    .querySelector("#quillEditorQiniu .avatar-uploader input")
                    .click();
                } else {
                  this.quill.format("video", false);
                }
              },
            },
          },
        },
      },
      sidecolumnrabs: "left", //方向
      Editprogress: 1, //编辑进度
      currentVersion: "1.2.3", //当前版本
      loading: false, // é®ç½©å±‚
      drawer: false, //控制展开
      radio: "false", //单选题选中
      radios: [], //多选题选中
      radioas: "", //填空题答案
      // æ€»æ¡æ•°
      total: 1,
      hetype: "",
      id: null,
      ruleForm: {
        campus: [],
        heLibraryTagList: [],
        tempDetpRelevances: [],
        version: "1.0.1",
      },
      rules: {},
      rulesa: {},
      mode: [],
      editableTabs: [],
      sortlist: [],
      usable: [],
      courtyardlist: [],
      precedencetype: [],
      optionsillness: [],
      illnesslistapi: [],
      illnesslist: [],
      options: [],
      optionstag: [],
      deptList: [],
      tempDetpRelevanceslist: [],
      props: { multiple: true, value: "deptId", label: "deptName" },
      // å†…网的部分(文件)
      oldPattern: "http://192.168.191.181:8095/profile/upload",
      // å†…网的部分(文件)
      oldPatternhtml: "/http:\/\/192\.168\.191\.181:8095\/profile\/upload\//g",
      // å¤–网部分(文件)
      // newPattern: "http://218.108.11.22:8093/profile-api/upload",
      newPattern: "http://m.zjtongde.com:13871/prod-api/profile/upload",
      xjxsoptions: [
        {
          value: "1",
          label: "图文",
        },
        {
          value: "2",
          label: "视频",
        },
        {
          value: "3",
          label: "音频",
        },
      ],
      valssu: [
        {
          idd: 1,
          wssd: "你最近怎么样",
          sdadd: ["sss", "ssccss", "ssaas", "ss"],
        },
      ],
      addvalue: "添加题目",
      variablelist: [
        { variatename: "姓名", variate: "${name}", default: 1 },
        { variatename: "电话", variate: "${phone}", default: 1 },
        { variatename: "病情", variate: "${illness}", default: 1 },
      ],
      // æŸ¥è¯¢å‚æ•°
      queryParams: {
        pageNum: 1,
        pageSize: 10,
      },
    };
  },
  activated() {
    if (this.id != this.$route.query.id) {
      this.gettabList();
      this.getList();
      this.illnessUpdate();
    }
  },
  created() {
    this.gettabList();
    this.getList();
    this.illnessUpdate();
    this.mode = store.getters.mode;
    this.editableTabs = store.getters.editableTabs;
    this.usable = store.getters.usable;
    this.precedencetype = store.getters.precedencetype;
    this.courtyardlist = store.getters.courtyardlist;
  },
  watch: {
    content(newVal, oldVal) {
      //this.$emit('input', newVal);
      console.log(newVal, "A");
      console.log(oldVal, "B");
    },
  },
  beforeDestroy() {
    const editor = this.editor;
    if (editor == null) return;
    editor.destroy(); // ç»„件销毁时,及时销毁编辑器
  },
  methods: {
    onCreated(editor) {
      this.editor = Object.seal(editor); // ä¸€å®šè¦ç”¨ Object.seal(),否则会报错
    },
    // ---------------------------------
    processElement(element) {
      return { ...element, isoperation: null };
    },
    // èŽ·å–é¡µé¢æ•°æ®
    getList() {
      this.loading = true;
      this.id = this.$route.query.id;
      this.hetype = this.$route.query.hetype;
      if (this.id) {
        getlibraryinfo({ id: this.id }).then((res) => {
          this.ruleForm = res.data[0];
          if (this.ruleForm.campus)
            this.ruleForm.campus = this.ruleForm.campus.split(",");
          this.dynamicTags = res.data[0].heLibraryTagList.map(
            this.processElement
          );
          if (this.ruleForm.htmlRichText) {
            this.Getmissioncontent(this.ruleForm.htmlRichText);
          }
          if (this.ruleForm.deptNames) {
            this.tempDetpRelevanceslist = JSON.parse(this.ruleForm.deptNames);
          }
          if (this.ruleForm.suitway) {
            this.ruleForm.suitway = this.ruleForm.suitway.split(",");
          }
          this.variablelist = this.ruleForm.otherdata
            ? JSON.parse(this.ruleForm.otherdata)
            : this.variablelist;
        });
      }
      // å®£æ•™åˆ†ç±»
      getheLibraryAssort({ hetype: 1 }).then((res) => {
        this.sortlist = res.rows;
        console.log(this.sortlist);
      });
      // éƒ¨é—¨
      listDept(this.queryParams).then((response) => {
        this.deptList = this.handleTree(response.data, "deptId");
      });
      // ------------------
      // let html =
      //   '<p>测试</p><video class="ql-video" controls="controls" controlslist="nofullscreen" type="video/mp4" style="object-fit:fill;width: 100%;" preload="auto" playsinline="true" x-webkit-airplay="allow" x5-video-orientation="portraint" x5-playsinline="true" x5-video-player-fullscreen="true" src="http://192.168.191.181:8095/profile/upload/vadio/营养泵介绍.mp4"></video><p>测试111</p><video class="ql-video" controls="controls" controlslist="nofullscreen" type="video/mp4" style="object-fit:fill;width: 100%;" preload="auto" playsinline="true" x-webkit-airplay="allow" x5-video-orientation="portraint" x5-playsinline="true" x5-video-player-fullscreen="true" src="http://192.168.191.181:8095/profile/upload/vadio/注射器推注.mp4"></video><p><br></p>';
      // // html = html.parserdom(this.oldPattern, this.newPattern);
      // html = this.parserdom(html);
      // console.log(html, "html");
      // this.loading = false;
    },
    // parser
    parserdom(html) {
      // åˆ›å»ºä¸€ä¸ªæ–°çš„DOM解析器
      var parser = new DOMParser();
      // å°†å­—符串解析为文档对象
      var doc = parser.parseFromString(html, "text/html");
      // å®šä¹‰è¦æ›¿æ¢çš„æ–°æ—§URL
      var oldUrlBase = this.oldPattern;
      var newUrlBase =this.newPattern;
      // èŽ·å–æ‰€æœ‰çš„video元素
      var videos = doc.querySelectorAll("video");
      // éåŽ†æ‰€æœ‰çš„video元素并替换src属性
      videos.forEach(function (video) {
        var src = video.getAttribute("src");
        if (src.startsWith(oldUrlBase)) {
          video.setAttribute("src", src.replace(oldUrlBase, newUrlBase));
        }
      });
      // å°†ä¿®æ”¹åŽçš„æ–‡æ¡£è½¬æ¢å›žå­—符串
      var newContent = doc.body.innerHTML;
      return newContent;
    },
    submitForm(formName) {
      let tgs = [];
      this.dynamicTags.forEach((item) => {
        tgs.push(item.tagname);
      });
      if (this.ruleForm.campus) {
        this.ruleForm.campus = this.ruleForm.campus.join(",");
      }
      this.ruleForm.labelInfo = tgs.length != 0 ? tgs.join(", ") : "";
      this.ruleForm.otherdata = JSON.stringify(this.variablelist);
      this.ruleForm.hetype = 1;
      console.log(22);
      this.ruleForm.suitway =
        this.ruleForm.suitway.length != 0
          ? this.ruleForm.suitway.join(",")
          : "";
      addrichText({
        content: this.parserdom(this.content),
        fileName: this.generateRandomHtmlFilename(),
      }).then((res) => {
        this.ruleForm.richText = res.msg;
        console.log(this.ruleForm.richText, "this.ruleForm.richText");
        // å¤„理内网html
        addrichText({
          content: this.content,
          fileName: this.generateRandomHtmlFilename(),
        }).then((resf) => {
          this.ruleForm.htmlRichText = resf.msg.replace(
            this.newPattern,
            this.oldPattern
          );
          console.log(this.ruleForm.htmlRichText, "this.ruleForm.htmlRichText");
          if (this.id) {
            this.ruleForm.isoperation = 2;
            compilelibrary(this.ruleForm).then((res) => {
              if (res.code == 200) {
                this.$modal.msgSuccess("编辑成功");
                this.confirmillness();
                this.$router.go(-1);
              }
            });
          } else {
            this.ruleForm.isoperation = 1;
            compilelibrary(this.ruleForm).then((res) => {
              if (res.code == 200) {
                this.$modal.msgSuccess("新增成功");
                this.confirmillness(res.data);
                this.$router.go(-1);
              }
            });
          }
        });
      });
    },
    generateRandomHtmlFilename() {
      // ç”Ÿæˆä¸€ä¸ª0到1之间的随机数,并将其转换为字符串
      let randomNumber = Math.random().toString();
      // ç§»é™¤å‰é¢çš„0和小数点
      randomNumber = randomNumber.substring(6);
      // ç¡®ä¿ç”Ÿæˆçš„随机数是一定长度的,例如8位
      while (randomNumber.length < 8) {
        randomNumber = "0" + randomNumber;
      }
      // æ‹¼æŽ¥ä¸Š.html后缀
      return randomNumber + ".html";
    },
    // ä¿å­˜ç–¾ç—…
    confirmillness(guid) {
      this.illnesslist.forEach((item, index) => {
        if (guid) {
          item.outid = guid;
        } else {
          console.log(this.ruleForm);
          item.outid = this.ruleForm.id;
        }
        item.icd10name = item.icdname;
        item.icd10code = item.icdcode;
        item.type = 6;
        if (!item.id) {
          addtargetillness(item).then((res) => {});
        }
      });
      this.illnessVisible = false;
      this.$modal.msgSuccess("编辑成功");
    },
    getFileNameFromPath(path) {
      const parts = path.split("/");
      return parts[parts.length - 1];
    },
    // ä¸‹ä¸€æ­¥
    nextstep() {
      if (this.Editprogress <= 1) {
        return this.Editprogress++;
      }
    },
    // ä¸Šä¸€æ­¥
    laststep() {
      this.Editprogress = this.Editprogress - 1;
    },
    // å…³é—­
    closeFm() {
      this.$confirm("退出不会保留页面内容更改, æ˜¯å¦ç»§ç»­?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.$router.go(-1);
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消",
          });
        });
    },
    // ç§‘室处理
    Departmenttreatment() {
      this.ruleForm.deptNames = JSON.stringify(this.tempDetpRelevanceslist);
      const result = this.tempDetpRelevanceslist.map(
        (subArr) => subArr[subArr.length - 1]
      );
      // id数组查数组对象
      result.forEach((item) => {
        const condition = this.ruleForm.tempDetpRelevances.some(
          (obj) => obj.deptId === item
        );
        if (!condition) {
          listDept({ deptId: item }).then((res) => {
            console.log("dept");
            res.data[0].type = 2;
            this.ruleForm.tempDetpRelevances.push(res.data[0]);
          });
        }
      });
      // æ•°ç»„对象查id数组
      this.ruleForm.tempDetpRelevances.forEach((item) => {
        const condition = result.some((obj) => obj === item.deptId);
        if (!condition) {
          const index = this.ruleForm.tempDetpRelevances.indexOf(item);
          this.ruleForm.tempDetpRelevances[index].delFlag = 1;
        }
      });
      setTimeout(() => {
        this.submitForm();
      }, 1000);
      // this.submitForm();
    },
    // ä¿å­˜é¢˜ç›®ä¿¡æ¯
    Saveproblem() {},
    /** æŸ¥è¯¢é¢˜ç›®åˆ—表 */
    // æ–°å¢žå˜é‡
    addvariable() {
      this.variablelist.push({
        variatename: "",
        variate: "",
      });
    },
    // åˆ é™¤å˜é‡
    delvariable(item) {
      const index = this.variablelist.indexOf(item);
      if (index !== -1) {
        this.variablelist.splice(index, 1); // ä»Žç´¢å¼•位置删除一个元素
      } else {
        console.log("未找到该对象");
      }
    },
    // æŽ§åˆ¶æ–‡ä»¶
    handleChange(file, fileList) {
      this.fileList = fileList.slice(-3);
    },
    // æ ‡ç­¾-----------------
    gettabList() {
      const tagqueryParams = {
        pageNum: 1,
        pageSize: 1000,
        tagcategoryid: "0",
      };
      listtag(tagqueryParams).then((response) => {
        this.optionstag = response.rows;
      });
    },
    handleClosetag(tag) {
      console.log(tag);
      const lindex = this.ruleForm.heLibraryTagList.findIndex(
        (item) => item.tagname == tag.tagname
      );
      console.log(lindex);
      this.dynamicTags.splice(this.dynamicTags.indexOf(tag), 1);
      this.ruleForm.heLibraryTagList[lindex].isoperation = 3;
    },
    handleInputConfirm() {
      let tagvalue = {};
      let tagname = this.inputValue;
      if (tagname) {
        listtag({
          pageNum: 1,
          pageSize: 1000,
          tagcategoryid: "0",
          tagname: tagname,
        }).then((res) => {
          if (res.rows[0]) {
            tagvalue = res.rows[0];
            tagvalue.isoperation = 1;
          } else {
            tagvalue = {
              tagname: tagname,
              isoperation: 1,
            };
          }
          this.ruleForm.heLibraryTagList.push(tagvalue);
          this.dynamicTags.push(tagvalue);
        });
      }
      this.inputVisible = false;
      this.inputValue = "";
    },
    remoteMethodtag(query) {
      if (query !== "") {
        this.loading = true;
        setTimeout(() => {
          this.loading = false;
          listtag({ tagname: query, tagcategoryid: "0" }).then((res) => {
            this.optionstag = res.rows;
          });
        }, 200);
      } else {
        this.optionstag = [];
      }
    },
    showInput() {
      this.inputVisible = true;
    },
    // ç–¾ç—…-----------------------
    illnessUpdate() {
      if (this.id) {
        getillness({ outid: this.$route.query.id, type: 6 }).then((res) => {
          this.illnesslist = res.rows;
          this.illnesslist.forEach((item) => {
            item.icdname = item.icd10name;
          });
        });
      }
    },
    // --------------------------
    // é¢„览模板
    PreviewTemplate() {
      this.drawer = true;
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
    //上传图片之前async
    beforeEditorUpload(res, file) {
      //显示上传动画
      this.quillUpdateImg = true;
      //  const res1 = await uploadImage()
      // console.log(res1,'=====');
      // this.$emit('before',res, file)
      console.log(res);
      console.log(file);
    },
    // ä¸Šä¼ å›¾ç‰‡æˆåŠŸ
    uploadEditorSuccess(res, file) {
      console.log("上传成功");
      //拼接出上传的图片在服务器的完整地址
      let imgUrl = res.url;
      console.log(res.url);
      imgUrl = imgUrl.replace(this.newPattern, this.oldPattern);
      console.log(imgUrl, "imgUrl");
      let type = imgUrl.substring(imgUrl.lastIndexOf(".") + 1);
      this.fileName = this.getFileNameFromPath(res.url);
      // èŽ·å–å¯Œæ–‡æœ¬ç»„ä»¶å®žä¾‹
      let quill = this.$refs.customQuillEditor.quill;
      // èŽ·å–å…‰æ ‡æ‰€åœ¨ä½ç½®
      let length = quill.getSelection().index;
      // æ’入图片||视频  res.info为服务器返回的图片地址
      if (type == "mp4" || type == "MP4" || type == "avi" || type == "AVI") {
        window.jsValue = imgUrl;
        quill.insertEmbed(length, "video", imgUrl);
      } else {
        quill.insertEmbed(length, "image", imgUrl);
      }
      // è°ƒæ•´å…‰æ ‡åˆ°æœ€åŽ
      quill.setSelection(length + 1);
      //取消上传动画
      this.quillUpdateImg = false;
    },
    // å¤±åŽ»ç„¦ç‚¹äº‹ä»¶
    onEditorBlur(e) {
      console.log("onEditorBlur: ", e);
    },
    // èŽ·å¾—ç„¦ç‚¹äº‹ä»¶
    onEditorFocus(e) {
      console.log("onEditorFocus: ", e);
    },
    // å†…容改变事件
    onEditorChange(e) {
      console.log("onEditorChange: ", e);
    },
    // ä¸Šä¼ (文件)图片失败
    uploadEditorError(res, file) {
      console.log(res, "word");
      console.log(file, "word");
      //页面提示
      this.$message.error("上传图片失败");
      //取消上传动画
      this.quillUpdateImg = false;
    },
    //上传组件返回的结果
    uploadResult: function (res) {
      this.uploadUrlPath = res;
    },
    // ä¸Šä¼ (文件)图片失败
    uploadEditorErrorword(res, file) {
      console.log(res);
      console.log(file);
      //页面提示
      this.$message.error("上传图片失败");
      //取消上传动画
      this.quillUpdateImg = false;
    },
    //上传图片之前async
    beforeEditorUploadword(res, file) {
      //显示上传动画
      this.quillUpdateImg = true;
      //  const res1 = await uploadImage()
      // console.log(res1,'=====');
      // this.$emit('before',res, file)
      console.log(res);
      console.log(file);
    },
    // ä¸Šä¼ word成功
    uploadEditorSuccessword(res, file) {
      console.log("上传word文件成功");
      console.log(res, file, "word");
      let fileurl = res.url.replace(this.newPattern, this.oldPattern);
      axios
        .get(fileurl)
        .then((response) => {
          console.log(response.data, "数据"); // è¾“出获取到的文件内容
          this.$nextTick(() => {
            this.content = response.data;
          });
          this.fileName = this.getFileNameFromPath(response.url);
          console.log(this.fileName, "this.fileName");
        })
        .catch((error) => {
          console.error("Failed to fetch file:", error);
        });
    },
    Getmissioncontent(url) {
      axios
        .get(url)
        .then((response) => {
          console.log(response.data, "数据"); // è¾“出获取到的文件内容
          this.content = response.data;
          this.fileName = this.getFileNameFromPath(response.url);
          console.log(this.fileName, "this.fileName");
        })
        .catch((error) => {
          console.error("Failed to fetch file:", error);
        });
    },
    // å¤„理url
  },
};
</script>
<style src="@wangeditor/editor/dist/css/style.css"></style>
<style src="@/assets/styles/global.css"></style>
<style lang="scss" scoped>
.sidecolumn {
  // width: 300px;
  // min-height: 100vh;
  // text-align: center;
  //   display: flex;
  //   margin-top: 20px;
  margin: 20px;
  margin-bottom: 0;
  padding: 20px;
  background: #edf1f7;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
}
.leftvlue {
  //   display: flex;
  //   flex: 1;
  margin: 20px;
  padding: 30px;
  background: #ffff;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
  .mulsz {
    font-size: 20px;
  }
  .leftvlue-jbxx {
    font-size: 24px;
    height: 30px;
    border-left: 3px solid #41a1be;
    padding-left: 3px;
    span {
      position: absolute;
      right: 80px;
    }
  }
  .demo-cascader {
    margin-right: 20px;
  }
  .PreviewTemplate {
    color: #02a7f0;
    cursor: pointer;
    font-size: 20px;
    margin: 0 20px;
  }
}
.xinz-inf {
  font-size: 18px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  line-height: 48px;
  .el-tag + .el-tag {
    margin-left: 10px;
  }
  .button-new-tag {
    margin-left: 10px;
    height: 32px;
    line-height: 30px;
    padding-top: 0;
    padding-bottom: 0;
  }
  .input-new-tag {
    width: 90px;
    margin-left: 10px;
    vertical-align: bottom;
  }
}
.preview-left {
  margin: 20px;
  //   margin: 20px;
  padding: 30px;
  background: #ffff;
  border: 1px solid #dcdfe6;
  -webkit-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.12),
    0 0 6px 0 rgba(0, 0, 0, 0.04);
  .topic-dev {
    margin-bottom: 25px;
    font-size: 20px !important;
    .dev-text {
      margin-bottom: 10px;
    }
  }
}
.addtopic {
  margin-top: 30px;
}
.presentation {
  margin: 20px 0;
  display: flex;
  .presentation-left {
    width: 50%;
    height: 500px;
    .button-textxg {
      color: #024df0;
    }
    .button-textsc {
      color: #f52727;
    }
  }
  .presentation-right {
    width: 50%;
    height: 500px;
    padding: 20px;
    font-size: 18px;
    border: 1px solid #909091;
    span {
      padding: 0 35px;
      margin-right: 10px;
      border-bottom: 1px solid #909091;
    }
    .headline {
      font-size: 20px;
      border-left: 3px solid #41a1be;
      padding-left: 5px;
      margin: 15px 0;
    }
  }
}
::v-deep .addtopic-input {
  input {
    background: #02a7f0;
    color: #edf1f7;
    width: 150px;
  }
}
::v-deep.el-step.is-vertical .el-step__title {
  font-size: 25px;
}
::v-deep.el-input--medium {
  font-size: 18px !important;
}
::v-deep.ruleFormaa.el-select {
  display: inline-block;
  position: relative;
  width: 700px;
}
.el-select__tags {
  font-size: 20px;
  max-width: 888px !important;
}
::v-deep.el-radio__inner {
  width: 22px;
  height: 22px;
}
// ::v-deep.topic-dev.el-radio__label {
//   font-size: 24px;
// }
::v-deep.el-radio-group {
  span {
    font-size: 24px;
  }
}
::v-deep.el-checkbox-group {
  span {
    font-size: 24px;
  }
}
</style>
在上述文件截断后对比
src/views/knowledge/education/compilequer/index.vue src/views/knowledge/education/index.vue src/views/knowledge/questionbank/particulars/index.vue src/views/knowledge/questionnaire/compilequer/index.vue src/views/knowledge/questionnaire/index.vue src/views/login.vue src/views/loginSSO.vue src/views/outsideChainnew.vue src/views/outsideChainwt.vue src/views/outsideChainwtnew.vue src/views/outsideChainxjnew.vue src/views/patient/follow/index.vue src/views/patient/medtechnician/PatientChart.vue src/views/patient/patient/AwaitingAdmission.vue src/views/patient/patient/ExternalPatient.vue src/views/patient/patient/behospitalized.vue src/views/patient/patient/hospital.vue src/views/patient/patient/index.vue src/views/patient/patient/indexls.vue src/views/patient/patient/operation.vue src/views/patient/patient/outpatient.vue src/views/patient/patient/physical.vue src/views/patient/physical/index.vue src/views/patient/propaganda/Missioncreation.vue src/views/patient/propaganda/Missionotice.vue src/views/patient/propaganda/QuestionnaireTask.vue src/views/patient/propaganda/index.vue src/views/patient/propaganda/particty.vue src/views/patient/questionnaire/index.vue src/views/patient/shadow/index.vue src/views/patient/subsequent/index.vue src/views/satisfaction.vue src/views/sfstatistics/percentage/index.vue src/views/sfstatistics/percentage/satisfaction.vue src/views/shortmessage/healthinformation/compilequer/index.vue src/views/system/user/index.vue vue.config.js