Files
xk-client-wx/request/api/request.js
李琦 8b455b1920 feat(home/chat): 列表骨架屏、聊天室己方头像与协议/医生详情优化
- 新增分包组件 ListSkeleton(doctor/message/product/article/category/zone)
- 首页/消息/资讯首次加载改用骨架屏,刷新不再清空旧数据避免闪空
  · 去掉 home getList、消息 onShow 中的数组清空
  · 三页 pages.json 增加 easycom 与 componentPlaceholder 主包异步引用
- 聊天室己方头像改为读取本地 userinfo.avatarurl(登录后微信头像),
  mycollection 上传头像成功后同步写回 storage
- 修复协议抽屉点不开:page-container 改为 v-if + :show;
  协议名单独可点节点,匹配放宽 name/desc 双向 includes
- 医生气泡头像可点击进入 doctor-detail?readonly=1 只读医生详情,
  隐藏预约挂号区
2026-07-21 16:05:09 +08:00

407 lines
16 KiB
JavaScript
Raw Permalink 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 {Base64} from "js-base64";
import {checkDev} from "@/utils/utils";
import {isGuestMode, getGuestStoreId} from "@/common/utils/auth.js";
const arr = [
'mobile',
'express_mobile',
'doctor',
'accept_tel',
'patient_mobile',
'id_card',
'idcard',
'password'
]
// 敏感数据:响应解密后保留明文原字段,脱敏值写入 {field}_tm
const sensitiveData = [
'id_card',
'idcard',
'guardian_id_card'
]
// 仅展示脱敏、编辑需明文的字段
const displayMaskFields = [
'mobile',
'express_mobile',
'patient_mobile',
]
/**
* 脱敏展示值(写入 field_tm
*/
function maskFieldValue(key, plainText) {
if (plainText == null || plainText === '') {
return plainText
}
const str = String(plainText)
switch (key) {
case 'express_name':
case 'express_region':
case 'accept_name':
case 'patient':
return str.slice(0, 1).padEnd(str.length, '*')
case 'mobile':
case 'express_mobile':
case 'patient_mobile':
case 'guardian_mobile':
if (str.length <= 7) {
return str
}
return str.slice(0, 3).padEnd(str.length - 4, '*') + str.slice(-4)
case 'id_card':
case 'idcard':
case 'guardian_id_card':
if (str.length <= 10) {
return str
}
return str.slice(0, 6).padEnd(str.length - 4, '*') + str.slice(-4)
default:
return str
}
}
/** 只读展示:优先 field_tm */
export function displayTm(obj, field) {
if (obj == null) {
return ''
}
const tm = obj[field + '_tm']
if (tm != null && tm !== '') {
return tm
}
return obj[field] ?? ''
}
const isDev = checkDev('dev');
// 环境URL配置
const ENV_URLS = {
// 生产环境
prod: {
main: "https://app.xiaokang88.com/member",
xkApi: "https://api.xiaokang88.com/api/mobile",
shop: "https://shop.xiaokang88.com/member",
im: "https://api.ws.g.xiaokang88.com/api",
},
// 测试环境
test: {
main: "https://xk.test.saas.api-yii.nailaoyun.cn/member",
xkApi: "https://xk.test.saas.api.nailaoyun.cn/api/mobile",
shop: "https://test.xk.shop.nailaoyun.cn/member1",
im: "https://xk.ws.nailaoyun.cn/api"
},
// 本地开发环境
local: {
main: "http://127.0.0.1:18000/member",
xkApi: "http://127.0.0.1:18001/api/mobile",
shop: "http://127.0.0.1:18018/member",
im: "http://127.0.0.1:12080/api"
}
};
// 获取当前环境配置
function getEnvUrls() {
// 如果是本地开发环境,使用本地配置
if (isDev === true) {
return ENV_URLS.local;
}
// 检查是否开启了开发者测试模式
const devMode = checkDev('test');
return devMode ? ENV_URLS.test : ENV_URLS.prod;
}
function request(url, params, method = 0) {
const envUrls = getEnvUrls();
let baseURL = envUrls.main;
// 判断是否为guest模式且未登录
const guestMode = isGuestMode();
const token = uni.getStorageSync("token");
const isGuestRequest = guestMode && !token;
// 如果是guest模式且是xkApi请求转换为guest路由
if (isGuestRequest && url.indexOf('/xkApi') !== -1) {
if (url.indexOf('/xkApi/online-consultation/get-default-doctor-id') !== -1) {
url = url.replace('/xkApi/online-consultation/get-default-doctor-id', '/guest/guest-home/get-default-doctor-id');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/product/drug-list-by-salesperson') !== -1) {
url = url.replace('/xkApi/product/drug-list-by-salesperson', '/guest/guest-product/drug-list-by-salesperson');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/product/home-top-products') !== -1) {
url = url.replace('/xkApi/product/home-top-products', '/guest/guest-product/home-top-products');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/product/home-zones') !== -1) {
url = url.replace('/xkApi/product/home-zones', '/guest/guest-product/home-zones');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/platform/config') !== -1) {
url = url.replace('/xkApi/platform/config', '/guest/guest-platform/config');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/platform/qualifications') !== -1) {
url = url.replace('/xkApi/platform/qualifications', '/guest/guest-platform/qualifications');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/store/info') !== -1) {
url = url.replace('/xkApi/store/info', '/guest/guest-home/store-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/store/list') !== -1) {
url = url.replace('/xkApi/store/list', '/guest/guest-home/store-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/xkApi/special-prescription/') !== -1) {
url = url.replace('/xkApi/special-prescription/', '/guest/guest-special-prescription/');
baseURL = envUrls.xkApi;
}
}
// 如果是guest模式且是oldApi请求转换为guest路由需要在替换之前处理
if (isGuestRequest && url.indexOf('/oldApi') !== -1) {
// 将 /oldApi/v1/xxx 转换为 /guest/guest-xxx/xxx
// 注意:路由前缀是 api/mobile所以完整路径是 api/mobile/guest/guest-doctor/list
if (url.indexOf('/oldApi/v1/doctor/all-list') !== -1) {
url = url.replace('/oldApi/v1/doctor/all-list', '/guest/guest-doctor/list');
baseURL = envUrls.xkApi; // guest路由使用xkApi的baseURL
} else if (url.indexOf('/oldApi/v1/doctor/detail') !== -1) {
url = url.replace('/oldApi/v1/doctor/detail', '/guest/guest-doctor/detail');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/doctor/comment-list') !== -1) {
url = url.replace('/oldApi/v1/doctor/comment-list', '/guest/guest-doctor/comment-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/patient/list') !== -1) {
url = url.replace('/oldApi/v1/patient/list', '/guest/guest-patient/list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/store/info') !== -1) {
url = url.replace('/oldApi/v1/store/info', '/guest/guest-home/store-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/store/detail') !== -1) {
url = url.replace('/oldApi/v1/store/detail', '/guest/guest-home/store-detail');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/store/list') !== -1) {
url = url.replace('/oldApi/v1/store/list', '/guest/guest-home/store-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/register/doc-register-info') !== -1) {
url = url.replace('/oldApi/v1/register/doc-register-info', '/guest/guest-doctor/doc-register-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/doctor/office-list') !== -1) {
url = url.replace('/oldApi/v1/doctor/office-list', '/guest/guest-doctor/office-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/info/categories') !== -1) {
url = url.replace('/oldApi/v1/info/categories', '/guest/guest-info/categories');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/info/article-list') !== -1) {
url = url.replace('/oldApi/v1/info/article-list', '/guest/guest-info/article-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/oldApi/v1/info/article-info') !== -1) {
url = url.replace('/oldApi/v1/info/article-info', '/guest/guest-info/article-info');
baseURL = envUrls.xkApi;
}
}
// 如果是guest模式且是member/v1请求也需要转换为guest路由
if (isGuestRequest && (url.indexOf('/member/v1') !== -1 || url.indexOf('/v1/') !== -1)) {
if (url.indexOf('/v1/doctor/comment-list') !== -1 || url.indexOf('/member/v1/doctor/comment-list') !== -1) {
url = url.replace(/\/member\/v1\/doctor\/comment-list|\/v1\/doctor\/comment-list/, '/guest/guest-doctor/comment-list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/v1/patient/list') !== -1 || url.indexOf('/member/v1/patient/list') !== -1) {
url = url.replace(/\/member\/v1\/patient\/list|\/v1\/patient\/list/, '/guest/guest-patient/list');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/v1/register/doc-register-info') !== -1 || url.indexOf('/member/v1/register/doc-register-info') !== -1) {
url = url.replace(/\/member\/v1\/register\/doc-register-info|\/v1\/register\/doc-register-info/, '/guest/guest-doctor/doc-register-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/v1/store/info') !== -1 || url.indexOf('/member/v1/store/info') !== -1) {
url = url.replace(/\/member\/v1\/store\/info|\/v1\/store\/info/, '/guest/guest-home/store-info');
baseURL = envUrls.xkApi;
} else if (url.indexOf('/v1/store/list') !== -1 || url.indexOf('/member/v1/store/list') !== -1) {
url = url.replace(/\/member\/v1\/store\/list|\/v1\/store\/list/, '/guest/guest-home/store-list');
baseURL = envUrls.xkApi;
}
}
if (url.split('/').indexOf('imApi') !== -1) {
baseURL = envUrls.im;
}
if (url.split('/').indexOf('newApi') !== -1) {
baseURL = envUrls.shop;
}
if (url.split('/').indexOf('xkApi') !== -1) {
baseURL = envUrls.xkApi;
// 如果是guest模式且是xkApi请求添加/guest/前缀(但避免重复添加)
// 转诊相关接口需要登录不转换为guest路由
if (isGuestRequest && url.indexOf('/guest/') === -1 && url.indexOf('/transfer-prescription') === -1) {
url = url.replace('/xkApi', '/guest/xkApi');
}
}
url = url.replace('/oldApi', '');
url = url.replace('/newApi', '');
url = url.replace('/imApi', '');
url = url.replace('/xkApi', '');
// 加密
if (params != null) {
if (!params.data) {
params.data = {};
}
params.data = getRes(params.data, false);
const isProductApi = url.indexOf('/product/') !== -1;
if (isProductApi && !params.data.store_id) {
const localStoreId = uni.getStorageSync('store_id');
params.data.store_id = localStoreId || getGuestStoreId();
}
// 如果是guest模式设置store_id优先使用用户传入或本地存储的值
if (isGuestRequest && params.data) {
// 优先使用用户传入的 store_id
if (!params.data.store_id) {
// 如果用户没传,尝试从本地存储获取
const localStoreId = uni.getStorageSync('store_id');
if (localStoreId) {
params.data.store_id = localStoreId;
} else {
// 如果本地存储也没有,使用默认值(保持向后兼容)
params.data.store_id = getGuestStoreId();
}
}
// 如果用户已经传了 store_id就使用用户传入的值不做任何修改
}
}
let num = 0;
// promise导出
return new Promise((resolve, reject) => {
uni.request({
// 基地址 + 传过来的地址
url: baseURL + url,
timeout: 8000, // 8秒超时时间单位ms
// 展开传过来的数据 请求方式 所需数据 wxfc279dcf921a38f7
// store_id: '11001'
...params,
// 请求头
header: {
"authorization": 'Bearer ' + uni.getStorageSync("token"),
"Content-Type": method === 0? "application/x-www-form-urlencoded": 'application/json'
},
// 成功钩子
success(res) {
if (res.data.errcode === 401) {
const currentPage = getCurrentPages()[getCurrentPages().length - 1];
const currentRoute = currentPage ? currentPage.route : '';
const isLoginPage = currentRoute === 'pages/login/login';
// 如果当前不在登录页,则跳转到登录页
if (!isLoginPage) {
uni.removeStorageSync('token')
uni.removeStorageSync('userinfo')
uni.removeStorageSync('token_h5')
uni.removeStorageSync('user_id')
uni.reLaunch({url: '/pages/login/login'})
}
}
if (res.data?.data != null) {
res.data.data = getRes(res.data.data)
}
// 处理 xkApi 返回的 result 格式
if (res.data?.result != null) {
res.data.result = getRes(res.data.result)
}
resolve(res)
},
// 失败钩子
fail(err) {
reject(err)
uni.showToast({
title: "请求失败",
duration: 1500,
mask: false,
icon: "error"
})
},
// 成功失败都会执行的钩子
complete() {
// 成功或者失败停止下拉刷新
uni.stopPullDownRefresh()
}
})
})
}
function getRes(obj, isDecode = true) {
try {
if (obj == null || typeof obj !== 'object') {
return obj
}
for (const key in obj) {
if (key.endsWith('_tm')) {
continue
}
if (Array.isArray(obj[key])) {
obj[key] = getRes(obj[key], isDecode)
} else if (obj[key] !== null && typeof obj[key] === 'object') {
obj[key] = getRes(obj[key], isDecode)
} else if (arr.indexOf(key) !== -1) {
let plainValue = ''
if (isDecode === true) {
plainValue = customBase64Decode(obj[key])
} else {
plainValue = customBase64Encode(obj[key])
}
const isGarbled = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\xFF]|[\u{E000}-\u{F8FF}]/u.test(
typeof plainValue === 'string' ? plainValue : ''
)
if (isGarbled) {
plainValue = obj[key]
}
if (isDecode === true) {
const needTm = sensitiveData.indexOf(key) !== -1
|| displayMaskFields.indexOf(key) !== -1
obj[key] = plainValue
if (needTm) {
obj[key + '_tm'] = maskFieldValue(key, plainValue)
}
} else {
obj[key] = plainValue
}
}
}
return obj
} catch (error) {
console.error('错误:', error)
return obj
}
}
function customBase64Decode(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 default request