283e6651aa
Co-authored-by: Cursor <cursoragent@cursor.com>
124 lines
3.5 KiB
TypeScript
124 lines
3.5 KiB
TypeScript
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<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,
|
|
showMessageCard: true,
|
|
sendMessageTitle: buildCsShareTitle(params.session?.orderNo),
|
|
sendMessagePath: buildCsSharePath(params.session?.orderId),
|
|
};
|
|
|
|
await new Promise<void>((resolve, reject) => {
|
|
wxApi({
|
|
...option,
|
|
success: () => resolve(),
|
|
fail: (err) => reject(err),
|
|
});
|
|
});
|
|
}
|