WXL
2025-08-06 2eff945fb9fc0d17b2098b26aba74ab192ec3727
测试完成
已添加3个文件
已修改5个文件
675 ■■■■ 文件已修改
dist (2).zip 补丁 | 查看 | 原始文档 | blame | 历史
dist.zip 补丁 | 查看 | 原始文档 | blame | 历史
src/components/CallButton/index.vue 230 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/modules/user.js 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService-xs.js 217 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/sipService.js 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/followvisit/record/detailpage/index.vue 172 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login.vue 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
dist (2).zip
Binary files differ
dist.zip
Binary files differ
src/components/CallButton/index.vue
@@ -3,29 +3,32 @@
    <div class="sip-status" :class="sipStatusClass">
      SIP状态: {{ sipStatus }}
    </div>
    <!-- å·ç è¾“å…¥ -->
    <input
      v-model="phoneNumber"
      type="text"
      placeholder="输入电话号码"
      @keyup.enter="startCall"
    />
    <!-- çŠ¶æ€æ˜¾ç¤º -->
    <div class="call-status" :class="callStatusClass">
      {{ callStatusText }}
    </div>
    <!-- å‘¼å«æŒ‰é’® -->
    <button :class="['call-btn', { calling: isCalling }]" @click="startCall">
      {{ isCalling ? "通话中..." : "一键呼叫" }}
    <button
      :class="['call-btn', { calling: isCalling }]"
      @click="startCall"
      :disabled="isCalling || sipStatus !== '已注册'"
    >
      {{ callButtonText }}
    </button>
    <!-- æŒ‚断按钮 -->
    <button v-if="isCalling" class="end-call-btn" @click="endCall">挂断</button>
    <button
      v-if="isCalling"
      class="end-call-btn"
      @click="endCall"
    >
      æŒ‚æ–­
    </button>
    <!-- éŸ³é¢‘元素(隐藏) -->
    <audio id="remoteAudio" autoplay></audio>
    <!-- çŠ¶æ€æ˜¾ç¤º -->
    <div class="call-status">
      {{ callStatus }}
    </div>
  </div>
</template>
@@ -33,82 +36,86 @@
import sipService from "@/utils/sipService";
export default {
  data() {
    return {
      // phoneNumber: "",
      isCalling: false,
      callStatus: "准备就绪",
      sipStatus: "未连接",
      sipStatusClass: "status-disconnected",
      sipConfig: {
        wsUrl: "wss://9.208.5.18:7443",
        sipUri: "1000@9.208.5.18",
        password: "Smartor@2023",
        displayName: "Web å°é¾™",
        // realm: "9.208.5.18:8090",
      },
    };
  },
  props: {
    phoneNumber: {
      type: String,
      default: ''
    }
  },
  data() {
    return {
      isCalling: false,
      callStatus: 'idle', // idle, calling, connected, ended
      sipStatus: "未连接",
      sipStatusClass: "status-disconnected",
      sipConfig: {
        wsUrl: "wss://192.168.100.6:7443",
        sipUri: "1000@192.168.100.6",
        password: "Smartor@2023",
        displayName: "Web å°é¾™",
      },
    };
  },
  computed: {
    callStatusText() {
      const statusMap = {
        idle: '准备就绪',
        calling: '呼叫中...',
        connected: '通话中',
        ended: '通话结束'
      };
      return statusMap[this.callStatus];
    },
    callStatusClass() {
      return `status-${this.callStatus}`;
    },
    callButtonText() {
      return this.isCalling ? "通话中..." : "一键呼叫";
    }
  },
  mounted() {
    // æµ‹è¯•
    const ws = new WebSocket("wss://9.208.5.18:7443");
    ws.onopen = () => console.log("WebSocket è¿žæŽ¥æˆåŠŸ");
    ws.onerror = (e) => console.error("WebSocket é”™è¯¯:", e);
    // åˆå§‹åŒ–SIP连接
    sipService.init(this.sipConfig);
    sipService.onStatusChange = (status) => {
      this.sipStatus = status.text;
      this.sipStatusClass = `status-${status.type}`;
    };
      // æ ¹æ®çŠ¶æ€æ›´æ–°UI或执行其他操作
      if (status.type === "registered") {
        console.log("SIP注册成功,可以开始呼叫");
      } else if (status.type === "failed") {
        console.error("SIP注册失败");
      }
    // ç›‘听通话状态变化
    sipService.onCallStatusChange = (status) => {
      this.callStatus = status.type;
      this.isCalling = status.type === 'calling' || status.type === 'connected';
      // é€šçŸ¥çˆ¶ç»„件通话状态变化
      this.$emit('call-status-change', status);
    };
  },
  beforeDestroy() {
    // ç»„件销毁时结束通话
    this.endCall();
  },
  methods: {
    // å¼€å§‹å‘¼å«
    async startCall() {
      if (!this.phoneNumber) {
        this.callStatus = "请输入电话号码";
        this.$message.error("请输入电话号码");
        return;
      }
      try {
        this.isCalling = true;
        this.callStatus = "呼叫中...";
        // è°ƒç”¨SIP服务
        sipService.makeCall(this.phoneNumber);
        this.callStatus = "通话已建立";
      try {
        this.callStatus = 'calling';
        this.isCalling = true;
        await sipService.makeCall(this.phoneNumber);
      } catch (error) {
        console.error("呼叫失败:", error);
        this.callStatus = `呼叫失败: ${error.message}`;
        this.callStatus = 'ended';
        this.isCalling = false;
        this.$message.error(`呼叫失败: ${error.message}`);
      }
    },
    // ç»“束通话
    endCall() {
      sipService.endCall();
      this.callStatus = 'ended';
      this.isCalling = false;
      this.callStatus = "通话已结束";
    },
  },
    }
  }
};
</script>
@@ -138,7 +145,106 @@
  border-radius: 4px;
  cursor: pointer;
}
.call-status {
  padding: 8px;
  margin: 10px 0;
  border-radius: 4px;
  text-align: center;
}
.status-idle {
  background-color: #f5f5f5;
  color: #666;
}
.status-calling {
  background-color: #fff8e1;
  color: #ff8f00;
}
.status-connected {
  background-color: #e8f5e9;
  color: #2e7d32;
}
.status-ended {
  background-color: #ffebee;
  color: #c62828;
}
/* åŽŸæœ‰æ ·å¼ä¿æŒä¸å˜ */
.call-container {
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 300px;
  margin: 0 auto;
  padding: 20px;
  border: 1px solid #eee;
  border-radius: 8px;
}
.call-btn {
  padding: 10px;
  background-color: #4caf50;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.call-btn:hover:not(:disabled) {
  background-color: #45a049;
}
.call-btn:disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}
.call-btn.calling {
  background-color: #2196f3;
}
.end-call-btn {
  padding: 10px;
  background-color: #f44336;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
}
.end-call-btn:hover {
  background-color: #d32f2f;
}
.sip-status {
  padding: 8px;
  margin-bottom: 10px;
  border-radius: 4px;
  text-align: center;
}
.status-disconnected {
  background-color: #ffebee;
  color: #c62828;
}
.status-connecting {
  background-color: #fff8e1;
  color: #ff8f00;
}
.status-registered {
  background-color: #e8f5e9;
  color: #2e7d32;
}
.status-failed {
  background-color: #ffebee;
  color: #c62828;
}
.call-btn:hover {
  background-color: #45a049;
}
src/store/modules/user.js
@@ -144,11 +144,19 @@
          localStorage.setItem('deptCode', '01040201');
          }else if (orgid=='47246102433112211A2101') {
          localStorage.setItem('orgname', '缙云县中医医院');
          localStorage.setItem('ZuHuID', '1400360867068907520');
          localStorage.setItem('ZuHuID', '1429338802177000002');
          localStorage.setItem('deptCode', '');
          }else if (orgid=='47240018433118111A2101') {
          localStorage.setItem('orgname', '龙泉市中医医院');
          localStorage.setItem('ZuHuID', '1400360867068907520');
          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', '');
          }
          resolve()
src/utils/sipService-xs.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,217 @@
import JsSIP from "jssip";
class SipService {
  constructor() {
    this.ua = null;
    this.currentSession = null;
    this.onStatusChange = null;
    this.onCallStatusChange = null;
    this.onIncomingCall = null;
  }
  init(config) {
    try {
      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: [{ urls: "stun:stun.l.google.com:19302" }],
        register: true,
        session_expires: 180,
        sessionTimersExpires: 300,
        extraHeaders: ["Min-SE: 120"],
        register_expires: 300,
        connection_recovery_min_interval: 2,
        connection_recovery_max_interval: 30,
        pcConfig: {
          iceTransportPolicy: "all",
          rtcpMuxPolicy: "require",
          bundlePolicy: "max-bundle"
        }
      });
      this.ua.start();
      // äº‹ä»¶ç›‘听
      this.ua.on("registered", () => this.updateStatus("registered", "已注册"));
      this.ua.on("registrationFailed", (e) =>
        this.updateStatus("failed", `注册失败: ${e.cause}`));
      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;
    }
  }
  makeCall(targetNumber) {
    return new Promise((resolve, reject) => {
      try {
        if (!this.ua) {
          throw new Error("SIP客户端未初始化");
        }
        if (!this.ua.isRegistered()) {
          throw new Error("SIP未注册,无法呼叫");
        }
        const options = {
          sessionTimers: false, // æš‚时禁用以减少兼容性问题
          extraHeaders: [
            "Min-SE: 120",
            "Accept: application/sdp",
            "Supported: outbound"
          ],
          mediaConstraints: { audio: true, video: false },
          rtcOfferConstraints: {
            offerToReceiveAudio: true,
            offerToReceiveVideo: false
          },
          eventHandlers: {
            progress: () => this.updateCallStatus("calling", "呼叫中..."),
            failed: (e) => {
              this.handleCallFailure(e, reject);
            },
            ended: () => this.updateCallStatus("ended", "通话结束"),
            confirmed: () => {
              this.updateCallStatus("connected", "通话已接通");
              resolve();
            }
          }
        };
        this.currentSession = this.ua.call(
          `sip:${targetNumber}@9.208.5.18`,
          options
        );
        this.setupPeerConnection(this.currentSession);
        this.setupAudio(this.currentSession);
      } catch (error) {
        this.updateCallStatus("failed", `呼叫失败: ${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;
        }
      };
    });
  }
  normalizeSDP(offer) {
    let sdp = offer.sdp;
    // 1. æ ‡å‡†åŒ–连接行
    sdp = sdp.replace(/c=IN IP4.*\r\n/, "c=IN IP4 0.0.0.0\r\n");
    // 2. æ ‡å‡†åŒ–音频媒体行
    sdp = sdp.replace(/m=audio \d+.*\r\n/,
      "m=audio 9 UDP/TLS/RTP/SAVPF 0 8\r\n");
    // 3. ç¡®ä¿åŒ…含基本编解码器
    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";
    }
    // 4. æ·»åŠ å¿…è¦å±žæ€§
    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) {
    console.error("呼叫失败详情:", {
      cause: e.cause,
      message: e.message,
      response: e.response && e.response.status_code
    });
    let errorMessage = "呼叫失败";
    switch(e.cause) {
      case "Incompatible SDP":
        errorMessage = "媒体协商失败,请检查编解码器配置";
        break;
      case "488":
      case "606":
        errorMessage = "对方设备不支持当前媒体配置";
        break;
      default:
        errorMessage = `呼叫失败: ${e.cause || e.message}`;
    }
    this.updateCallStatus("failed", errorMessage);
    reject(new Error(errorMessage));
  }
  setupAudio(session) {
    session.connection.addEventListener("addstream", (e) => {
      const audioElement = document.getElementById("remoteAudio");
      if (audioElement) {
        audioElement.srcObject = e.stream;
      }
    });
  }
  endCall() {
    if (this.currentSession) {
      this.currentSession.terminate();
      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
@@ -5,6 +5,7 @@
    this.ua = null;
    this.currentSession = null;
    this.onStatusChange = null; // çŠ¶æ€å˜åŒ–å›žè°ƒ
    this.onCallStatusChange = null; // æ–°å¢žé€šè¯çŠ¶æ€å›žè°ƒ
  }
  // åˆå§‹åŒ–SIP客户端
@@ -17,7 +18,7 @@
        uri: config.sipUri,
        password: config.password,
        display_name: config.displayName,
        iceservers:[],
        iceservers: [],
        // realm: config.realm,
        register: true,
        session_expires: 180,
@@ -97,10 +98,18 @@
        "Allow: INVITE, ACK, BYE, CANCEL, OPTIONS",
      ],
      eventHandlers: {
        progress: (e) => console.log("呼叫中..."),
        failed: (e) => console.error("呼叫失败:", e),
        ended: (e) => console.log("通话结束"),
        confirmed: (e) => console.log("通话已接通"),
        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,
@@ -135,6 +144,7 @@
    );
    // åœ¨ä¼šè¯åˆ›å»ºåŽä¿®æ”¹ SDP
    this.currentSession.on("peerconnection", (pc) => {
       this.updateCallStatus('calling', '呼叫中...');
      pc.createOffer = (offerOptions) => {
        return RTCPeerConnection.prototype.createOffer
          .call(pc, offerOptions)
@@ -149,6 +159,17 @@
          });
      };
    });
    this.currentSession.on('failed', (e) => {
        this.updateCallStatus('failed', `呼叫失败2: ${e.cause}`);
      });
      this.currentSession.on('ended', () => {
        this.updateCallStatus('ended', '通话已结束');
      });
      this.currentSession.on('confirmed', () => {
        this.updateCallStatus('connected', '通话已接通');
      });
    this.setupAudio(this.currentSession);
  }
  setupAudio(session) {
@@ -161,12 +182,19 @@
  }
  // æŒ‚断当前通话
  endCall() {
    if (this.currentSession) {
  if (this.currentSession) {
      this.currentSession.terminate();
      this.updateCallStatus('ended', '通话已结束');
      this.currentSession = null;
    }
  }
  // æ–°å¢žæ–¹æ³•:更新通话状态
  updateCallStatus(type, text) {
    console.log(`通话状态更新: ${type} - ${text}`);
    if (this.onCallStatusChange) {
      this.onCallStatusChange({ type, text });
    }
  }
}
export default new SipService();
src/views/followvisit/record/detailpage/index.vue
@@ -260,13 +260,37 @@
                  ></el-input> </el-form-item
              ></el-col>
            </el-row>
            <div style="margin-left: 30px">
              <el-button type="primary" plain @click="Editsingletasksonyic('')"
                >保存服务</el-button
              >
            </div>
          </div>
          <el-row :gutter="20" v-if="callStatus !== 'idle'">
            <el-col :span="24">
              <el-alert
                :title="callStatusText"
                :type="callStatusType"
                :closable="false"
                show-icon
              />
            </el-col>
          </el-row>
          <!-- æŒ‚断按钮(仅在通话中显示) -->
          <el-row :gutter="20" v-if="callStatus === 'connected'">
            <el-col :span="24" style="text-align: center; margin-top: 10px">
              <el-button
                type="danger"
                icon="el-icon-phone"
                @click="endCurrentCall"
                :loading="isEndingCall"
              >
                æŒ‚断电话
              </el-button>
            </el-col>
          </el-row>
          <el-form-item label="随访记录">
            <el-input type="textarea" v-model="form.remark"></el-input>
          </el-form-item>
@@ -796,6 +820,10 @@
      userid: "",
      currentPhoneNumber: "",
      callType: "", // ç”¨äºŽåŒºåˆ†æ˜¯å“ªä¸ªç”µè¯
      // å·²æœ‰æ•°æ®...
      callStatus: "idle", // idle, calling, connected, ended, failed
      isEndingCall: false,
      currentCall: null, // å½“前通话对象
      input: "今天身体还不错",
      radio: "2",
      taskname: "",
@@ -854,9 +882,9 @@
      },
      pickerOptions: {
        disabledDate(time) {
        // ç¦ç”¨ä»Šå¤©åŠä¹‹å‰çš„æ—¥æœŸ
        return time.getTime() < Date.now() - 24 * 60 * 60 * 1000;
      },
          // ç¦ç”¨ä»Šå¤©åŠä¹‹å‰çš„æ—¥æœŸ
          return time.getTime() < Date.now() - 24 * 60 * 60 * 1000;
        },
        shortcuts: [
          {
            text: "七天后",
@@ -948,7 +976,28 @@
      patid: null,
    };
  },
  computed: {
    callStatusText() {
      const statusMap = {
        idle: "准备呼叫",
        calling: `正在呼叫 ${this.currentPhoneNumber}...`,
        connected: `已接通 ${this.currentPhoneNumber}`,
        ended: "通话已结束",
        failed: "呼叫失败",
      };
      return statusMap[this.callStatus];
    },
    callStatusType() {
      const typeMap = {
        idle: "info",
        calling: "warning",
        connected: "success",
        ended: "info",
        failed: "error",
      };
      return typeMap[this.callStatus];
    },
  },
  created() {
    this.taskid = this.$route.query.taskid;
    this.id = this.$route.query.id;
@@ -1217,28 +1266,95 @@
          console.error("发生错误:", error);
        });
    },
    // éªŒè¯æ‰‹æœºå·æ ¼å¼
    isValidPhone(phone) {
      return /^1[3-9]\d{9}$/.test(phone);
    },
    // å‘¼å«å¤„理
    // éªŒè¯ç”µè¯å·ç æ ¼å¼å¹¶è¿”回错误信息
validatePhoneNumber(phone) {
  if (!phone) {
    return { isValid: false, message: '请输入电话号码' };
  }
  // æ‰‹æœºå·æ­£åˆ™
  const mobileRegex = /^1[3-9]\d{9}$/;
  // å¸¦åŒºå·çš„固定电话(完整格式)
  const landlineFullRegex = /^0\d{2,3}-?\d{7,8}$/;
  // ä¸å¸¦åŒºå·çš„固定电话(仅本地号码)
  const landlineLocalRegex = /^\d{7,8}$/;
  if (mobileRegex.test(phone)) {
    return { isValid: true, type: 'mobile' };
  } else if (landlineFullRegex.test(phone)) {
    return { isValid: true, type: 'landline' };
  } else if (landlineLocalRegex.test(phone)) {
    return {
      isValid: false,
      message: '本地号码请添加区号(如028-1234567)'
    };
  } else {
    return {
      isValid: false,
      message: '请输入正确的电话号码(手机号或带区号的固定电话)'
    };
  }
},
// ä½¿ç”¨ç¤ºä¾‹
isValidPhone(phone) {
  return this.validatePhoneNumber(phone).isValid;
},
    handleCall(phone, type) {
      if (this.isValidPhone(phone)) {
        this.currentPhoneNumber = phone;
        this.callType = type;
        // ç­‰å¾…下一个tick确保值已更新
        this.$nextTick(() => {
          this.$refs.callButton.startCall();
          // å¯é€‰ï¼šæ ¹æ®ä¸åŒç±»åž‹åšä¸åŒå¤„理
          if (type === "tel") {
            console.log("正在呼叫患者本人:", phone);
          } else {
            console.log("正在呼叫联系人:", phone);
          }
        });
      if (!this.isValidPhone(phone)) {
        this.$message.error("请输入正确的手机号码");
        return;
      }
      this.currentPhoneNumber = phone;
      this.callType = type;
      this.callStatus = "calling";
      this.$nextTick(() => {
        this.$refs.callButton.startCall();
        // ç›‘听通话状态变化
        this.$refs.callButton.$on("call-status-change", (status) => {
          this.handleCallStatusChange(status);
        });
      });
    },
    // å¤„理通话状态变化
    handleCallStatusChange(status) {
      console.log(status,'status');
      this.callStatus = status.type;
      if (status.type === "connected") {
        this.currentCall = {
          phone: this.currentPhoneNumber,
          type: this.callType,
          startTime: new Date(),
        };
      } else if (status.type === "ended" || status.type === "failed") {
        this.currentCall = null;
      }
      // å¯ä»¥æ ¹æ®çŠ¶æ€æ‰§è¡Œå…¶ä»–æ“ä½œ
      if (status.type === "failed") {
        this.$message.error(`呼叫失败: ${status.text}`);
      }
    },
    // ç»“束当前通话
    endCurrentCall() {
      if (!this.currentCall) return;
      this.isEndingCall = true;
      this.$refs.callButton.endCall();
      // 3秒后重置状态
      setTimeout(() => {
        this.isEndingCall = false;
      }, 3000);
    },
    yuyingetdetail() {
      this.tableDatatop.forEach((item, index) => {
@@ -1285,7 +1401,7 @@
            })
            .catch(() => {
              if (this.form.serviceType == 13) {
                if (this.visitCount!=1) {
                if (this.visitCount != 1) {
                  this.$router.push({
                    path: "/logisticsservice/zbAgain",
                  });
@@ -1295,7 +1411,7 @@
                  });
                }
              } else if (form.serviceType == 2) {
                if (this.visitCount!=1) {
                if (this.visitCount != 1) {
                  this.$router.push({
                    path: "/followvisit/again",
                  });
@@ -1331,7 +1447,7 @@
          this.form = res.rows[0].serviceSubtaskList.find(
            (item) => item.id == this.id
          );
          console.log(this.form.serviceType,'serviceType');
          console.log(this.form.serviceType, "serviceType");
          this.logsheetlist = res.rows[0].serviceSubtaskList;
          this.templateid = this.logsheetlist[0].templateid;
@@ -1500,7 +1616,7 @@
          }
          // form.id = null;
          form.sendstate = 2;
          console.log(form.serviceType,'form.serviceType');
          console.log(form.serviceType, "form.serviceType");
          addserviceSubtask(form).then((res) => {
            if (res.code == 200) {
src/views/login.vue
@@ -119,6 +119,8 @@
        { value: "47231022633110211A2101", label: "丽水市中医院" },
        { value: "47246102433112211A2101", label: "缙云县中医医院 " },
        { value: "47240018433118111A2101", label: "龙泉市中医医院 " },
        { value: "47243006833112611A2101", label: "庆元县中医医院 " },
        { value: "47234002X33112111A2101", label: "青田县中医医院 " },
       ],
      loginRules: {
        username: [