export type WecomCsSessionContext = { orderId?: string; orderNo?: string; from?: string; }; /** 客服消息卡片必须落主包页;分包(订单详情)作启动页会白屏 */ export function buildCsSharePath(orderId?: string): string { const id = (orderId || '').trim(); return id ? `pages/open/index?id=${encodeURIComponent(id)}` : 'pages/home/index'; } export function buildCsShareTitle(orderNo?: string): string { const no = (orderNo || '').trim(); return no ? `订单 ${no}` : '杜康好客'; } 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 = []; 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 { 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, showMessageCard: true, sendMessageTitle: buildCsShareTitle(params.session?.orderNo), sendMessagePath: buildCsSharePath(params.session?.orderId), }; await new Promise((resolve, reject) => { wxApi({ ...option, success: () => resolve(), fail: (err) => reject(err), }); }); }