Files
xk-doctor-wx/common/js/index.js
李琦 cb290f3e95 fix(login): 密码登录补充密码非空前端校验
- doctor-login submitLogin 增加「请输入密码」前置校验,
  与 getCode 一致,避免空密码误触发登录请求
- 配合后端 service_user 空密码回退 PC 医生/药师密码校验
2026-07-19 16:08:23 +08:00

310 lines
9.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Request from './request.js';
import errorCode from './errorCode.js';
import {Base64} from "js-base64";
import { getDoctorWxEnv } from '@/config/app-env.js';
import { resolveDoctorWxAuthToken } from '@/utils/doctorWxAuthToken.js';
const { legacyApiBase } = getDoctorWxEnv();
export const config = {
baseUrl: legacyApiBase
}
const arr = [
'mobile',
'call_mobile',
'express_mobile',
'doctor',
'accept_tel',
'patient_mobile',
'id_card',
'idcard',
'password',
'phone'
]
// 敏感数据(解密后做脱敏;需与下方 switch 分支一致)
// 注意:call_mobile 只解密不脱敏,供回访拨号使用
const sensitiveData = [
'id_card',
'idcard',
'mobile',
'express_mobile',
'patient_mobile',
'phone'
]
/**
* 请求拦截器
* @param {*} options
*/
const reqInterceptor = async (options) => {
// 加密
if (options.data != null && options.type !== 'upload') {
options.data = getRes(options.data, false)
}
options.header = {
...options.header
}
const skipToken = options.header.isToken === false
const isClinicAdminReq = options.header._clinicAdmin === '1'
|| (options.url && String(options.url).indexOf('clinic-admin') !== -1)
const isPlatformAdminReq = options.header._platformAdmin === '1'
|| (options.url && String(options.url).indexOf('platform-admin') !== -1)
const isSalespersonReq = options.header._salesperson === '1'
|| (options.url && String(options.url).indexOf('salesperson-') !== -1)
const isClinicSalespersonReq = options.header._clinicSalesperson === '1'
|| (options.url && String(options.url).indexOf('clinic-salesperson-') !== -1)
const isDoctorWxAuthReq = options.url && String(options.url).indexOf('doctor-wx-auth') !== -1
// 内部控制位,不得作为 HTTP 头发出
delete options.header.isToken
delete options.header._clinicAdmin
delete options.header._platformAdmin
delete options.header._salesperson
delete options.header._clinicSalesperson
const clinicToken = uni.getStorageSync('clinic_admin_token')
const platformToken = uni.getStorageSync('platform_admin_token')
const salespersonToken = uni.getStorageSync('salesperson_token')
const clinicSalespersonToken = uni.getStorageSync('clinic_salesperson_token')
const doctorToken = uni.getStorageSync('token')
const loginMode = uni.getStorageSync('loginMode')
if (!skipToken) {
if (isDoctorWxAuthReq) {
const authToken = resolveDoctorWxAuthToken()
if (authToken) {
options.header['authorization'] = `Bearer ${authToken}`
}
} else if (isPlatformAdminReq && platformToken) {
options.header['authorization'] = `Bearer ${platformToken}`
} else if (isSalespersonReq && salespersonToken) {
options.header['authorization'] = `Bearer ${salespersonToken}`
} else if (isClinicSalespersonReq && clinicSalespersonToken) {
options.header['authorization'] = `Bearer ${clinicSalespersonToken}`
} else if (isClinicAdminReq && clinicToken) {
options.header['authorization'] = `Bearer ${clinicToken}`
} else if (doctorToken) {
options.header['authorization'] = `Bearer ${doctorToken}`
}
}
return options;
}
/**
* 响应拦截器
*/
const resInterceptor = (response, conf = {}) => {
// 网络状态码
const statusCode = response.statusCode|| response.errcode;
// 响应拦截器
if (statusCode >= 200 && statusCode < 300) {
_responseLog(response, conf, "response 200-299")
// 解密(Yii:业务在 data;Laravel jok:业务在 result)
let res = response.data
if (res == null || typeof res !== 'object') {
return res
}
if (res.data != null) {
res.data = getRes(res.data)
}
if (res.result != null) {
res.result = getRes(res.result)
}
return res
} else if (statusCode === 500) {
uni.showToast({
icon: 'error',
title: response['msg'] || errorCode[statusCode]
})
_responseLog(response, conf, "response 500")
// 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控
return {
// 根据当前字段来判断是否reject
wakaryReqToReject: true,
// 下面可以配置其他返回信息,方便统一处理reject
// 以下内容作为reject的返回,根据需求处理,返回具体错误信息
msg: "服务器错误",
res: response
}
} else if (statusCode === 401) {
uni.showToast({
icon: 'error',
title: response['msg'] || response['message'] || errorCode[statusCode]
})
const loginMode = uni.getStorageSync('loginMode')
if (loginMode === 'platform_admin') {
uni.removeStorageSync('platform_admin_token')
uni.removeStorageSync('platform_admin_user')
uni.removeStorageSync('loginMode')
} else if (loginMode === 'salesperson') {
uni.removeStorageSync('salesperson_token')
uni.removeStorageSync('salesperson_user')
uni.removeStorageSync('loginMode')
} else if (loginMode === 'clinic_salesperson') {
uni.removeStorageSync('clinic_salesperson_token')
uni.removeStorageSync('clinic_salesperson_user')
uni.removeStorageSync('loginMode')
} else if (loginMode === 'clinic_admin') {
uni.removeStorageSync('clinic_admin_token')
uni.removeStorageSync('clinic_admin_user')
uni.removeStorageSync('loginMode')
} else {
uni.removeStorageSync('token')
uni.removeStorageSync('loginInfo')
uni.removeStorageSync('identity')
}
setTimeout(() => {
uni.reLaunch({ url: '/pages/login/index' })
}, 1000)
_responseLog(response, conf, "response 401")
// 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控
return {
// 根据当前字段来判断是否reject
wakaryReqToReject: true,
// 下面可以配置其他返回信息,方便统一处理reject
// 以下内容作为reject的返回,根据需求处理,返回具体错误信息
msg: response['msg'] || errorCode[statusCode],
res: response
}
} else {
uni.showToast({
icon: 'error',
title: response['msg'] || errorCode[statusCode]
})
_responseLog(response, conf, "response 300-499");
// 增加一个控制字段wakaryReqToReject 使 reject的内容更加可控
return {
// 根据当前字段来判断是否reject
wakaryReqToReject: true,
// 下面可以配置其他返回信息,方便统一处理reject
// 以下内容作为reject的返回,根据需求处理,返回具体错误信息
msg: "response 300-499",
res: response
}
}
}
/**
* request log
*/
function _requestLog(req, describe = null) {
if (process.env.NODE_ENV === 'development') {}
}
/**
* response log
*/
function _responseLog(res, conf = {}, describe = null) {
let _statusCode = res.statusCode;
if (process.env.NODE_ENV === 'development') {}
if (_statusCode === 500) {
// save log to server
}
}
function getRes(obj, isDecode = true) {
if (obj == null || typeof obj !== 'object') {
return obj
}
try {
for (const key in obj) {
if (Array.isArray(obj[key])) {
obj[key] = getRes(obj[key])
} else if (typeof obj[key] === 'object' && obj[key] !== null) {
obj[key] = getRes(obj[key])
} else {
// 判断obj[key]是否在arr中
if (arr.indexOf(key) !== -1) {
if (obj[key] == null || obj[key] === '') continue
let aseFile = ''
if (isDecode === true) {
aseFile = customBase64Decode(obj[key])
} else {
aseFile = customBase64Encode(obj[key])
}
const isGarbled = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]|[\u{E000}-\u{F8FF}]/u.test(
typeof aseFile === 'string' ? aseFile : ''
)
if (isGarbled) {
aseFile = obj[key]
}
if (isDecode === true) {
if (sensitiveData.indexOf(key) !== -1) {
// 数据脱敏,自动匹配姓名、手机号、身份证
switch (key) {
case 'express_name':
case 'express_region':
case 'accept_name':
case 'patient':
// 保留前两个字符,其余用星号代替
aseFile = aseFile.slice(0, 1).padEnd(aseFile.length, '*');
break;
case 'mobile':
case 'express_mobile':
case 'phone':
// 保留前三位和后四位,中间用星号代替
aseFile = aseFile.slice(0, 3).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4);
break;
case 'id_card':
case 'idcard':
// 保留前六位和后四位,中间用星号代替
aseFile = aseFile.slice(0, 6).padEnd(aseFile.length - 4, '*') + aseFile.slice(-4);
break;
default:
// 默认情况下,全部用星号代替
break;
}
}
}
obj[key] = aseFile
}
}
}
return obj
} catch (error) {
console.error('错误', error)
return obj
}
}
function customBase64Decode(data) {
if (data == null || data === '') return data
// return data
try {
// 第一次Base64解码
const decodedFirst = Base64.decode(data)
// 截取从第30个字符开始往后的内容
const subStr = decodedFirst.slice(30)
// 第二次Base64解码
return Base64.decode(subStr) != ''? Base64.decode(subStr): data
} catch (error) {
console.error(`解码Base64字符串【${data}】时发生错误`, error)
return data
}
}
// 加密
function customBase64Encode(data) {
if (data == null || data === '') return data
// return data
try {
// 和加密方法差不多但是是要生成30个随机字符
let randomString = ''
for (let i = 0; i < 30; i++) {
const randomChar = String.fromCharCode(Math.floor(Math.random() * (126 - 32 + 1)) + 32)
randomString += randomChar
}
const encoded = Base64.encode(data)
return Base64.encode(randomString + encoded)
} catch (error) {
console.error(`加密字符串【${data}】时发生错误`, error)
return data
}
}
/**
* 请求方法封装
*/
export const req = new Request(config, reqInterceptor, resInterceptor)