diff --git a/common/js/customer-service.js b/common/js/customer-service.js index a4b7cac..4960919 100644 --- a/common/js/customer-service.js +++ b/common/js/customer-service.js @@ -1,485 +1,511 @@ -/** - * 客服统一处理服务 - * 整合各种客服方式,提供统一的调用接口 - */ - -export class CustomerService { - constructor(vueInstance) { - this.vm = vueInstance; - } - - /** - * 获取平台配置 - * @returns {Object} 平台对应的客服配置 - */ - getPlatformConfig() { - const servicerConfig = this.vm.$store.state.servicerConfig; - console.log(`客服配置:`, servicerConfig) - if (!servicerConfig) return null; - - // #ifdef H5 - return servicerConfig.h5; - // #endif - - // #ifdef MP-WEIXIN - return servicerConfig.weapp; - // #endif - - // #ifdef MP-ALIPAY - return servicerConfig.aliapp; - // #endif - - return null; - } - - /** - * 获取企业微信配置 - * @returns {Object} 企业微信配置 - */ - getWxworkConfig() { - return this.vm.$store.state.wxworkConfig; - } - - /** - * 检查客服配置是否可用 - * @returns {boolean} 是否有可用配置 - */ - isConfigAvailable() { - const config = this.getPlatformConfig(); - return config && typeof config === 'object' && config.type; - } - - /** - * 验证客服配置完整性 - * @returns {Object} 验证结果 - */ - validateConfig() { - const config = this.getPlatformConfig(); - const wxworkConfig = this.getWxworkConfig(); - - const result = { - isValid: true, - errors: [], - warnings: [] - }; - - if (!config) { - result.isValid = false; - result.errors.push('客服配置不存在'); - return result; - } - - if (!config.type) { - result.isValid = false; - result.errors.push('客服类型未配置'); - } - - // 验证企业微信配置 - if (config.type === 'wxwork') { - if (!wxworkConfig) { - result.isValid = false; - result.errors.push('企业微信配置不存在'); - } else { - if (!wxworkConfig.enable) { - result.warnings.push('企业微信功能未启用'); - } - if (!wxworkConfig.contact_url) { - result.warnings.push('企业微信活码链接未配置,将使用原有客服方式'); - } - } - } - - return result; - } - - /** - * 获取客服类型 - * @returns {string} 客服类型 - */ - getServiceType() { - const config = this.getPlatformConfig(); - return config?.type || 'none'; - } - - /** - * 处理客服点击事件 - * @param {Object} options 选项参数 - * @param {Object} options.niushop 牛商客服参数 - * @param {string} options.sendMessageTitle 消息标题 - * @param {string} options.sendMessagePath 消息路径 - * @param {string} options.sendMessageImg 消息图片 - */ - handleCustomerClick(options = {}) { - // 验证配置 - const validation = this.validateConfig(); - if (!validation.isValid) { - console.error('客服配置验证失败:', validation.errors); - this.showConfigErrorPopup(validation.errors); - return; - } - - // 显示警告(如果有) - if (validation.warnings.length > 0) { - console.warn('客服配置警告:', validation.warnings); - } - - const config = this.getPlatformConfig(); - const { - niushop = {}, - sendMessageTitle = '', - sendMessagePath = '', - sendMessageImg = '' - } = options; - - // 检查是否为无客服类型 - if (config.type === 'none') { - this.showNoServicePopup(); - return; - } - - // 根据类型处理客服 - switch (config.type) { - case 'wxwork': - this.openWxworkService(false, config, options); - break; - case 'third': - this.openThirdService(config); - break; - case 'niushop': - this.openNiushopService(niushop); - break; - case 'weapp': - this.openWeappService(config, options); - break; - case 'aliapp': - this.openAliappService(config); - break; - default: - this.makePhoneCall(); - } - } - - /** - * 打开企业微信客服 - * @param {boolean} useOriginalService 是否使用原有客服方式 - * @param {Object} servicerConfig 客服配置 - */ - openWxworkService(useOriginalService = false, servicerConfig = null, options = {}) { - const config = servicerConfig || this.getPlatformConfig(); - const wxworkConfig = this.getWxworkConfig(); - - const { - sendMessageTitle = '', - sendMessagePath = '', - sendMessageImg = '' - } = options; - - // #ifdef MP-WEIXIN - if (wxworkConfig?.enable && wxworkConfig?.contact_url && !useOriginalService) { - // 使用活码链接跳转到企业微信 - wx.navigateToMiniProgram({ - appId: 'wxeb490c6f9b154ef9', // 企业微信官方小程序AppID - path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`, - success: () => { - console.log('跳转企业微信成功'); - }, - fail: (err) => { - console.error('跳转企业微信失败:', err); - this.fallbackToOriginalService(); - } - }); - } else { - // 使用原有的客服会话方式 - wx.openCustomerServiceChat({ - extInfo: { url: config.wxwork_url }, - corpId: config.corpid, - showMessageCard: true, - sendMessageTitle, - sendMessagePath, - sendMessageImg - }); - } - // #endif - - // #ifdef H5 - if (wxworkConfig?.enable && wxworkConfig?.contact_url) { - window.location.href = wxworkConfig.contact_url; - } else { - location.href = config.wxwork_url; - } - // #endif - } - - /** - * 打开第三方客服 - * @param {Object} config 客服配置 - */ - openThirdService(config) { - if (config.third_url) { - location.href = config.third_url; - } - } - - /** - * 打开牛商客服 - * @param {Object} niushop 牛商参数 - */ - openNiushopService(niushop) { - if (Object.keys(niushop).length > 0) { - this.vm.$util.redirectTo('/pages_tool/chat/room', niushop); - } else { - this.makePhoneCall(); - } - } - - /** - * 打开微信小程序客服 - * @param {Object} config 客服配置 - * @param {Object} options 选项参数 - */ - openWeappService(config, options = {}) { - // 如果是官方客服,由 open-type="contact" 自动处理 - if (!this.shouldUseCustomService(config)) { - console.log('使用官方微信小程序客服'); - return; - } - - // 自定义客服处理 - console.log('使用自定义微信小程序客服'); - - // 这里可以实现自定义的客服逻辑 - // 例如:跳转到自定义客服页面、显示客服弹窗等 - const { - sendMessageTitle = '', - sendMessagePath = '', - sendMessageImg = '' - } = options; - - // 实现自定义客服逻辑 - this.handleCustomWeappService(config, options); - } - - /** - * 处理自定义微信小程序客服 - * @param {Object} config 客服配置 - * @param {Object} options 选项参数 - */ - handleCustomWeappService(config, options = {}) { - const { - sendMessageTitle = '', - sendMessagePath = '', - sendMessageImg = '' - } = options; - - // 优先级1: 如果有自定义客服页面URL,跳转到自定义页面 - if (config.customServiceUrl) { - // 构建带参数的URL - let url = config.customServiceUrl; - const params = []; - - if (sendMessageTitle) params.push(`title=${encodeURIComponent(sendMessageTitle)}`); - if (sendMessagePath) params.push(`path=${encodeURIComponent(sendMessagePath)}`); - if (sendMessageImg) params.push(`img=${encodeURIComponent(sendMessageImg)}`); - - if (params.length > 0) { - url += (url.includes('?') ? '&' : '?') + params.join('&'); - } - - uni.navigateTo({ - url: url, - fail: (err) => { - console.error('跳转自定义客服页面失败:', err); - this.tryThirdPartyService(config, options); - } - }); - return; - } - - // 优先级2: 尝试第三方客服 - this.tryThirdPartyService(config, options); - } - - /** - * 尝试使用第三方客服 - * @param {Object} config 客服配置 - * @param {Object} options 选项参数 - */ - tryThirdPartyService(config, options = {}) { - // 如果配置了第三方客服URL - if (config.thirdPartyServiceUrl) { - // #ifdef H5 - window.open(config.thirdPartyServiceUrl, '_blank'); - // #endif - - // #ifdef MP-WEIXIN - // 微信小程序可以使用web-view或者跳转到第三方小程序 - if (config.thirdPartyMiniAppId) { - wx.navigateToMiniProgram({ - appId: config.thirdPartyMiniAppId, - path: config.thirdPartyMiniAppPath || '', - fail: (err) => { - console.error('跳转第三方小程序失败:', err); - this.fallbackToPhoneCall(); - } - }); - } else { - // 设置到剪贴板,让用户手动访问 - uni.setClipboardData({ - data: config.thirdPartyServiceUrl, - success: () => { - uni.showModal({ - title: '客服链接已复制', - content: '客服链接已复制到剪贴板,请在浏览器中粘贴访问', - showCancel: false - }); - } - }); - } - // #endif - return; - } - - // 降级到电话客服 - this.fallbackToPhoneCall(); - } - - /** - * 降级到电话客服 - */ - fallbackToPhoneCall() { - uni.showModal({ - title: '联系客服', - content: '在线客服暂时不可用,是否拨打电话联系客服?', - success: (res) => { - if (res.confirm) { - this.makePhoneCall(); - } - } - }); - } - - /** - * 打开支付宝小程序客服 - * @param {Object} config 客服配置 - */ - openAliappService(config) { - // 支付宝小程序客服会由 contact-button 组件自动处理 - console.log('支付宝小程序客服'); - } - - /** - * 拨打电话 - */ - makePhoneCall() { - this.vm.$api.sendRequest({ - url: '/api/site/shopcontact', - success: res => { - if (res.code === 0 && res.data?.mobile) { - uni.makePhoneCall({ - phoneNumber: res.data.mobile - }); - } - } - }); - } - - /** - * 显示无客服弹窗 - */ - showNoServicePopup() { - const siteInfo = this.vm.$store.state.siteInfo; - const message = siteInfo?.site_tel - ? `请联系客服,客服电话是${siteInfo.site_tel}` - : '抱歉,商家暂无客服,请线下联系'; - - uni.showModal({ - title: '联系客服', - content: message, - showCancel: false - }); - } - - /** - * 显示配置错误弹窗 - * @param {Array} errors 错误列表 - */ - showConfigErrorPopup(errors) { - const message = errors.join('\n'); - uni.showModal({ - title: '配置错误', - content: `客服配置有误:\n${message}`, - showCancel: false - }); - } - - /** - * 降级处理:使用原有客服方式 - */ - fallbackToOriginalService() { - uni.showModal({ - title: '提示', - content: '无法直接添加企业微信客服,是否使用其他方式联系客服?', - success: (res) => { - if (res.confirm) { - console.log('降级处理:使用原有客服方式'); - this.openWxworkService(true); - } - } - }); - } - - /** - * 获取客服按钮配置 - * @returns {Object} 按钮配置 - */ - getButtonConfig() { - const config = this.getPlatformConfig(); - if (!config) return { openType: '' }; - - let openType = ''; - - // #ifdef MP-WEIXIN - if (config.type === 'weapp') { - // 检查是否使用官方客服 - if (config.useOfficial !== false) { // 默认为true,使用官方客服 - openType = 'contact'; - } - } - // #endif - - // #ifdef MP-ALIPAY - if (config.type === 'aliapp') openType = 'contact'; - // #endif - - return { - ...config, - openType - }; - } - - /** - * 判断是否应该使用自定义客服处理 - * @param {Object} config 客服配置 - * @returns {boolean} 是否使用自定义客服 - */ - shouldUseCustomService(config) { - // #ifdef MP-WEIXIN - // 如果是微信小程序且type为weapp,检查useOfficial配置 - if (config?.type === 'weapp') { - return config.useOfficial === false; // 明确设置为false才使用自定义 - } - // #endif - - // 其他平台或类型都使用自定义处理 - return true; - } -} - -/** - * 创建客服服务实例 - * @param {Object} vueInstance Vue实例 - * @returns {CustomerService} 客服服务实例 - */ -export function createCustomerService(vueInstance) { - return new CustomerService(vueInstance); +/** + * 客服统一处理服务 + * 整合各种客服方式,提供统一的调用接口 + */ +export class CustomerService { + constructor(vueInstance, externalConfig = null) { + this.vm = vueInstance; + this.externalConfig = externalConfig; // 外部传入的最新配置(优先级最高) + this.latestPlatformConfig = null; + } + + /** + * 强制刷新配置(支持传入外部配置) + * @param {Object} externalConfig 外部最新配置 + */ + refreshConfig(externalConfig = null) { + this.externalConfig = externalConfig || this.externalConfig; + this.latestPlatformConfig = null; + return this.getPlatformConfig(); + } + + /** + * 获取平台配置 + * @returns {Object} 平台对应的客服配置 + */ + getPlatformConfig() { + if (this.latestPlatformConfig) { + return this.latestPlatformConfig; + } + + // 优先级:外部传入的最新配置 > vuex配置 > 空对象 + const servicerConfig = this.externalConfig || this.vm.$store.state.servicerConfig || {}; + console.log(`【实时客服配置】`, servicerConfig); + + let platformConfig = null; + // #ifdef H5 + platformConfig = servicerConfig.h5 ? (typeof servicerConfig.h5 === 'object' ? servicerConfig.h5 : null) : null; + // #endif + + // #ifdef MP-WEIXIN + platformConfig = servicerConfig.weapp ? (typeof servicerConfig.weapp === 'object' ? servicerConfig.weapp : null) : null; + // #endif + + // #ifdef MP-ALIPAY + platformConfig = servicerConfig.aliapp ? (typeof servicerConfig.aliapp === 'object' ? servicerConfig.aliapp : null) : null; + // #endif + + // #ifdef PC + platformConfig = servicerConfig.pc ? (typeof servicerConfig.pc === 'object' ? servicerConfig.pc : null) : null; + // #endif + + // 处理空数组情况(你的配置中pc/aliapp是空数组,转为null) + if (Array.isArray(platformConfig)) { + platformConfig = null; + } + + this.latestPlatformConfig = platformConfig; + return platformConfig; + } + + /** + * 获取企业微信配置 + * @returns {Object} 企业微信配置 + */ + getWxworkConfig() { + return this.vm.$store.state.wxworkConfig || {}; + } + + /** + * 检查客服配置是否可用 + * @returns {boolean} 是否有可用配置 + */ + isConfigAvailable() { + const config = this.getPlatformConfig(); + return config && typeof config === 'object' && config.type; + } + + /** + * 验证客服配置完整性 + * @returns {Object} 验证结果 + */ + validateConfig() { + const config = this.getPlatformConfig(); + const wxworkConfig = this.getWxworkConfig(); + + const result = { + isValid: true, + errors: [], + warnings: [] + }; + + if (!config) { + result.isValid = false; + result.errors.push('客服配置不存在'); + return result; + } + + if (config.type === 'aikefu') { + return result; + } + + if (!config.type) { + result.isValid = false; + result.errors.push('客服类型未配置'); + } + + if (config.type === 'wxwork') { + if (!wxworkConfig) { + result.isValid = false; + result.errors.push('企业微信配置不存在'); + } else { + if (!wxworkConfig.enable) { + result.warnings.push('企业微信功能未启用'); + } + if (!wxworkConfig.contact_url) { + result.warnings.push('企业微信活码链接未配置,将使用原有客服方式'); + } + } + } + + return result; + } + + /** + * 跳转到Dify客服页面 + */ + openDifyService() { + try { + if (this.vm.setAiUnreadCount) { + this.vm.setAiUnreadCount(0); + } + // 强制跳转,忽略框架层的封装 + uni.redirectTo({ + url: '/pages_tool/ai-chat/index', + fail: (err) => { + // 兜底:使用window.location跳转(H5) + // #ifdef H5 + window.location.href = '/pages_tool/ai-chat/index'; + // #endif + console.error('跳转Dify客服失败:', err); + uni.showToast({ + title: '跳转客服失败', + icon: 'none' + }); + } + }); + } catch (e) { + console.error('跳转Dify客服异常:', e); + uni.showToast({ + title: '跳转客服失败', + icon: 'none' + }); + } + } + + /** + * 处理客服点击事件 + * @param {Object} options 选项参数 + */ + handleCustomerClick(options = {}) { + const validation = this.validateConfig(); + if (!validation.isValid) { + console.error('客服配置验证失败:', validation.errors); + this.showConfigErrorPopup(validation.errors); + return; + } + + if (validation.warnings.length > 0) { + console.warn('客服配置警告:', validation.warnings); + } + + const config = this.getPlatformConfig(); + const { niushop = {}, sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options; + + if (config.type === 'none') { + this.showNoServicePopup(); + return; + } + + // 核心分支:根据最新的type处理 + switch (config.type) { + case 'aikefu': + this.openDifyService(); + break; + case 'wxwork': + this.openWxworkService(false, config, options); + break; + case 'third': + this.openThirdService(config); + break; + case 'niushop': + this.openNiushopService(niushop); + break; + case 'weapp': + this.openWeappService(config, options); + break; + case 'aliapp': + this.openAliappService(config); + break; + default: + this.makePhoneCall(); + } + } + + /** + * 打开企业微信客服 + * @param {boolean} useOriginalService 是否使用原有客服方式 + * @param {Object} servicerConfig 客服配置 + * @param {Object} options 选项参数 + */ + openWxworkService(useOriginalService = false, servicerConfig = null, options = {}) { + const config = servicerConfig || this.getPlatformConfig(); + const wxworkConfig = this.getWxworkConfig(); + const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options; + + // #ifdef MP-WEIXIN + if (wxworkConfig?.enable && wxworkConfig?.contact_url && !useOriginalService) { + wx.navigateToMiniProgram({ + appId: 'wxeb490c6f9b154ef9', + path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(wxworkConfig.contact_url)}`, + success: () => console.log('跳转企业微信成功'), + fail: (err) => { + console.error('跳转企业微信失败:', err); + this.fallbackToOriginalService(); + } + }); + } else { + wx.openCustomerServiceChat({ + extInfo: { url: config.wxwork_url }, + corpId: config.corpid, + showMessageCard: true, + sendMessageTitle, + sendMessagePath, + sendMessageImg + }); + } + // #endif + + // #ifdef H5 + if (wxworkConfig?.enable && wxworkConfig?.contact_url) { + window.location.href = wxworkConfig.contact_url; + } else if (config.wxwork_url) { + location.href = config.wxwork_url; + } else { + this.fallbackToPhoneCall(); + } + // #endif + } + + /** + * 打开第三方客服 + * @param {Object} config 客服配置 + */ + openThirdService(config) { + if (config.third_url) { + window.location.href = config.third_url; + } else { + this.fallbackToPhoneCall(); + } + } + + /** + * 打开牛商客服 + * @param {Object} niushop 牛商参数 + */ + openNiushopService(niushop) { + if (Object.keys(niushop).length > 0) { + this.vm.$util.redirectTo('/pages_tool/chat/room', niushop); + } else { + this.makePhoneCall(); + } + } + + /** + * 打开微信小程序客服 + * @param {Object} config 客服配置 + * @param {Object} options 选项参数 + */ + openWeappService(config, options = {}) { + if (!this.shouldUseCustomService(config)) { + console.log('使用官方微信小程序客服'); + return; + } + + console.log('使用自定义微信小程序客服'); + this.handleCustomWeappService(config, options); + } + + /** + * 处理自定义微信小程序客服 + * @param {Object} config 客服配置 + * @param {Object} options 选项参数 + */ + handleCustomWeappService(config, options = {}) { + const { sendMessageTitle = '', sendMessagePath = '', sendMessageImg = '' } = options; + + if (config.customServiceUrl) { + let url = config.customServiceUrl; + const params = []; + if (sendMessageTitle) params.push(`title=${encodeURIComponent(sendMessageTitle)}`); + if (sendMessagePath) params.push(`path=${encodeURIComponent(sendMessagePath)}`); + if (sendMessageImg) params.push(`img=${encodeURIComponent(sendMessageImg)}`); + + if (params.length > 0) { + url += (url.includes('?') ? '&' : '?') + params.join('&'); + } + + uni.navigateTo({ + url: url, + fail: (err) => { + console.error('跳转自定义客服页面失败:', err); + this.tryThirdPartyService(config, options); + } + }); + return; + } + + this.tryThirdPartyService(config, options); + } + + /** + * 尝试使用第三方客服 + * @param {Object} config 客服配置 + * @param {Object} options 选项参数 + */ + tryThirdPartyService(config, options = {}) { + if (config.thirdPartyServiceUrl) { + // #ifdef H5 + window.open(config.thirdPartyServiceUrl, '_blank'); + // #endif + + // #ifdef MP-WEIXIN + if (config.thirdPartyMiniAppId) { + wx.navigateToMiniProgram({ + appId: config.thirdPartyMiniAppId, + path: config.thirdPartyMiniAppPath || '', + fail: (err) => { + console.error('跳转第三方小程序失败:', err); + this.fallbackToPhoneCall(); + } + }); + } else { + uni.setClipboardData({ + data: config.thirdPartyServiceUrl, + success: () => { + uni.showModal({ + title: '客服链接已复制', + content: '客服链接已复制到剪贴板,请在浏览器中粘贴访问', + showCancel: false + }); + } + }); + } + // #endif + return; + } + + this.fallbackToPhoneCall(); + } + + /** + * 降级到电话客服 + */ + fallbackToPhoneCall() { + uni.showModal({ + title: '联系客服', + content: '在线客服暂时不可用,是否拨打电话联系客服?', + success: (res) => { + if (res.confirm) { + this.makePhoneCall(); + } + } + }); + } + + /** + * 打开支付宝小程序客服 + * @param {Object} config 客服配置 + */ + openAliappService(config) { + console.log('支付宝小程序客服', config); + switch (config.type) { + case 'aikefu': + this.openDifyService(); + break; + case 'third': + this.openThirdService(config); + break; + default: + console.log('使用支付宝官方客服'); + break; + } + } + + /** + * 拨打电话 + */ + makePhoneCall() { + this.vm.$api.sendRequest({ + url: '/api/site/shopcontact', + success: res => { + if (res.code === 0 && res.data?.mobile) { + uni.makePhoneCall({ + phoneNumber: res.data.mobile + }); + } else { + uni.showToast({ + title: '暂无客服电话', + icon: 'none' + }); + } + }, + fail: () => { + uni.showToast({ + title: '获取客服电话失败', + icon: 'none' + }); + } + }); + } + + /** + * 显示无客服弹窗 + */ + showNoServicePopup() { + const siteInfo = this.vm.$store.state.siteInfo || {}; + const message = siteInfo?.site_tel + ? `请联系客服,客服电话是${siteInfo.site_tel}` + : '抱歉,商家暂无客服,请线下联系'; + + uni.showModal({ + title: '联系客服', + content: message, + showCancel: false + }); + } + + /** + * 显示配置错误弹窗 + * @param {Array} errors 错误列表 + */ + showConfigErrorPopup(errors) { + const message = errors.join('\n'); + uni.showModal({ + title: '配置错误', + content: `客服配置有误:\n${message}`, + showCancel: false + }); + } + + /** + * 降级处理:使用原有客服方式 + */ + fallbackToOriginalService() { + uni.showModal({ + title: '提示', + content: '无法直接添加企业微信客服,是否使用其他方式联系客服?', + success: (res) => { + if (res.confirm) { + this.openWxworkService(true); + } + } + }); + } + + /** + * 获取客服按钮配置 + * @returns {Object} 按钮配置 + */ + getButtonConfig() { + const config = this.getPlatformConfig(); + if (!config) return { openType: '' }; + + let openType = ''; + // #ifdef MP-WEIXIN + if (config.type === 'weapp') { + openType = config.useOfficial !== false ? 'contact' : ''; + } + // #endif + + // #ifdef MP-ALIPAY + if (config.type === 'aliapp') openType = 'contact'; + // #endif + + return { ...config, openType }; + } + + /** + * 判断是否应该使用自定义客服处理 + * @param {Object} config 客服配置 + * @returns {boolean} 是否使用自定义客服 + */ + shouldUseCustomService(config) { + // #ifdef MP-WEIXIN + if (config?.type === 'weapp') { + return config.useOfficial === false; + } + // #endif + return true; + } +} + +/** + * 创建客服服务实例 + * @param {Object} vueInstance Vue实例 + * @param {Object} externalConfig 外部最新配置 + * @returns {CustomerService} 客服服务实例 + */ +export function createCustomerService(vueInstance, externalConfig = null) { + return new CustomerService(vueInstance, externalConfig); } \ No newline at end of file diff --git a/components/hover-nav/hover-nav.vue b/components/hover-nav/hover-nav.vue index 3611f55..2f96041 100644 --- a/components/hover-nav/hover-nav.vue +++ b/components/hover-nav/hover-nav.vue @@ -1,60 +1,49 @@