Files
dukang/apps/mini-user/src/lib/wecom-cs.ts
T
2026-08-26 09:22:29 +08:00

119 lines
3.3 KiB
TypeScript

export type WecomCsSessionContext = {
orderId?: string;
orderNo?: string;
from?: string;
};
type OpenCsChatOption = {
extInfo: { url: string };
corpId: string;
showMessageCard?: boolean;
sendMessageTitle?: string;
sendMessagePath?: string;
};
type WxCsFail = { errMsg?: string; errCode?: number };
type WxCsChat = {
openCustomerServiceChat?: (
option: OpenCsChatOption & {
success?: () => void;
fail?: (err: WxCsFail) => void;
},
) => void;
};
/**
* 取小程序原生 wx.openCustomerServiceChat。
* 官方不支持 Promise 风格;Taro 模块作用域下也可能读不到全局 wx。
*/
function getWxOpenCsChat(): WxCsChat['openCustomerServiceChat'] | null {
if (process.env.TARO_ENV !== 'weapp') return null;
const candidates: Array<WxCsChat | null | undefined> = [];
try {
// eslint-disable-next-line no-undef
if (typeof wx !== 'undefined') candidates.push(wx as WxCsChat);
} catch {
/* ignore */
}
candidates.push((globalThis as typeof globalThis & { wx?: WxCsChat }).wx);
try {
const fromRuntime = new Function(
'return typeof wx !== "undefined" ? wx : null',
)() as WxCsChat | null;
candidates.push(fromRuntime);
} catch {
/* ignore */
}
for (const api of candidates) {
if (api && typeof api.openCustomerServiceChat === 'function') {
return api.openCustomerServiceChat.bind(api);
}
}
return null;
}
function rawCsError(err: unknown): string {
if (err instanceof Error) return err.message;
if (typeof err === 'string') return err;
if (err && typeof err === 'object' && 'errMsg' in err) {
return String((err as WxCsFail).errMsg || '');
}
return '';
}
/** 把微信 fail 转成可读提示;用户取消返回空串,调用方不 toast */
export function formatOpenCsError(err: unknown): string {
const raw = rawCsError(err);
if (/cancel/i.test(raw)) return '';
if (/not bind|not bound/i.test(raw)) {
return '企业ID未绑定当前小程序。请到微信公众平台「功能 → 客服 → 微信客服」填写同一企业ID';
}
if (!raw || /openCustomerServiceChat:fail$/i.test(raw.trim())) {
return '无法打开微信客服。开发者工具模拟器通常不可用,请用真机预览';
}
return raw;
}
/** 小程序调起企业微信「微信客服」会话 */
export async function openWecomCustomerServiceChat(params: {
url: string;
corpId: string;
session?: WecomCsSessionContext;
}): Promise<void> {
const url = params.url.trim();
const corpId = params.corpId.trim();
if (!url || !corpId) {
throw new Error('企微客服未配置');
}
const wxApi = getWxOpenCsChat();
if (!wxApi) {
throw new Error('当前微信版本不支持企业微信客服');
}
const option: OpenCsChatOption = {
extInfo: { url },
corpId,
};
if (params.session?.orderNo || params.session?.orderId) {
option.showMessageCard = true;
option.sendMessageTitle = params.session.orderNo
? `订单 ${params.session.orderNo}`
: '订单咨询';
if (params.session.orderId) {
option.sendMessagePath = `pages/order-detail/index?id=${params.session.orderId}`;
}
}
await new Promise<void>((resolve, reject) => {
wxApi({
...option,
success: () => resolve(),
fail: (err) => reject(err),
});
});
}