feat: 新增打开企业微信客服组件

This commit is contained in:
2025-12-15 14:26:01 +08:00
parent 1854a85394
commit 5e536afeae
4 changed files with 1257 additions and 681 deletions

187
common/js/wxwork-jssdk.js Normal file
View File

@@ -0,0 +1,187 @@
/**
* 企业微信JS-SDK调用
*/
let WxWork = function () {
// 企业微信JS-SDK
this.wxwork = null;
/**
* 初始化企业微信JS-SDK
* @param {Object} params - 初始化参数
* @param {string} params.corpId - 企业ID
* @param {string} params.agentId - 应用ID
* @param {string} params.timestamp - 时间戳
* @param {string} params.nonceStr - 随机字符串
* @param {string} params.signature - 签名
* @param {Array} params.jsApiList - 需要使用的JS接口列表
*/
this.init = function (params) {
if (typeof wx !== 'undefined' && wx.config) {
// 小程序环境下的企业微信
this.wxwork = wx;
} else if (typeof WWOpenData !== 'undefined') {
// H5环境下的企业微信
this.wxwork = WWOpenData;
} else {
console.error('企业微信JS-SDK未加载');
return false;
}
this.wxwork.config({
beta: true, // 必须这么写否则wx.invoke调用形式的jsapi会有问题
debug: false, // 开启调试模式
corpId: params.corpId, // 必填,企业号的唯一标识
agentId: params.agentId, // 必填企业微信应用ID
timestamp: params.timestamp, // 必填,生成签名的时间戳
nonceStr: params.nonceStr, // 必填,生成签名的随机串
signature: params.signature, // 必填,签名
jsApiList: params.jsApiList || [
'openUserProfile',
'openEnterpriseChat',
'getContext',
'getCurExternalContact',
'openExistedChatWithMsg'
] // 必填需要使用的JS接口列表
});
return true;
};
/**
* 添加企业微信联系人
* @param {Object} params - 参数
* @param {string} params.userId - 用户ID
* @param {Function} success - 成功回调
* @param {Function} fail - 失败回调
*/
this.addContact = function (params, success, fail) {
if (!this.wxwork) {
console.error('企业微信JS-SDK未初始化');
if (fail) fail('企业微信JS-SDK未初始化');
return;
}
this.wxwork.ready(() => {
this.wxwork.invoke('openUserProfile', {
type: 'external', // 外部联系人
userId: params.userId // 用户ID
}, (res) => {
if (res.err_msg === 'openUserProfile:ok') {
if (success) success(res);
} else {
console.error('打开用户资料失败:', res);
if (fail) fail(res.err_msg);
}
});
});
};
/**
* 打开企业微信客服会话
* @param {Object} params - 参数
* @param {string} params.corpId - 企业ID
* @param {string} params.url - 客服URL
* @param {string} params.name - 会话名称
* @param {Function} success - 成功回调
* @param {Function} fail - 失败回调
*/
this.openCustomerService = function (params, success, fail) {
if (!this.wxwork) {
console.error('企业微信JS-SDK未初始化');
if (fail) fail('企业微信JS-SDK未初始化');
return;
}
this.wxwork.ready(() => {
// #ifdef MP-WEIXIN
if (typeof wx !== 'undefined' && wx.openCustomerServiceChat) {
// 微信小程序环境
wx.openCustomerServiceChat({
extInfo: {
url: params.url
},
corpId: params.corpId,
showMessageCard: true,
sendMessageTitle: params.sendMessageTitle || '',
sendMessagePath: params.sendMessagePath || '',
sendMessageImg: params.sendMessageImg || ''
});
if (success) success();
}
// #endif
// #ifdef H5
else if (typeof WWOpenData !== 'undefined') {
// H5环境
window.location.href = params.url;
if (success) success();
}
// #endif
else {
// 直接跳转链接
window.location.href = params.url;
if (success) success();
}
});
};
/**
* 生成企业微信活码链接
* @param {Object} params - 参数
* @param {string} params.configId - 活码配置ID
* @param {string} params.userId - 用户ID
* @returns {string} 活码链接
*/
this.generateContactUrl = function (params) {
// 企业微信活码链接格式
const baseUrl = 'https://work.weixin.qq.com/kfid';
if (params.configId) {
return `${baseUrl}/${params.configId}`;
}
return null;
};
/**
* 检查环境是否支持企业微信
* @returns {boolean} 是否支持
*/
this.isSupported = function () {
// #ifdef MP-WEIXIN
return typeof wx !== 'undefined' && wx.openCustomerServiceChat;
// #endif
// #ifdef H5
return typeof WWOpenData !== 'undefined' || /wxwork/i.test(navigator.userAgent);
// #endif
return false;
};
/**
* 获取当前环境信息
* @returns {Object} 环境信息
*/
this.getEnvironment = function () {
// #ifdef MP-WEIXIN
return {
platform: 'miniprogram',
isWxWork: false,
supportContact: typeof wx !== 'undefined' && wx.openCustomerServiceChat
};
// #endif
// #ifdef H5
const isWxWork = /wxwork/i.test(navigator.userAgent);
return {
platform: 'h5',
isWxWork: isWxWork,
supportContact: isWxWork || typeof WWOpenData !== 'undefined'
};
// #endif
return {
platform: 'unknown',
isWxWork: false,
supportContact: false
};
};
}
export {
WxWork
}

View File

@@ -1,171 +1,217 @@
<template> <template>
<view class="contact-wrap"> <view class="contact-wrap">
<slot></slot> <slot></slot>
<!-- #ifdef MP-ALIPAY --> <!-- #ifdef MP-ALIPAY -->
<view class="contact-button" @click="contactServicer"> <view class="contact-button" @click="contactServicer">
<contact-button :tnt-inst-id="config.instid" :scene="config.scene" size="1000rpx" v-if="config.type == 'aliapp'"/> <contact-button :tnt-inst-id="config.instid" :scene="config.scene" size="1000rpx" v-if="config.type == 'aliapp'"/>
</view> </view>
<!-- #endif --> <!-- #endif -->
<!-- #ifndef MP-ALIPAY --> <!-- #ifndef MP-ALIPAY -->
<button <button
type="default" type="default"
hover-class="none" hover-class="none"
:open-type="openType" :open-type="openType"
class="contact-button" class="contact-button"
@click="contactServicer" @click="contactServicer"
:send-message-title="sendMessageTitle" :send-message-title="sendMessageTitle"
:send-message-path="sendMessagePath" :send-message-path="sendMessagePath"
:send-message-img="sendMessageImg" :send-message-img="sendMessageImg"
:show-message-card="true"></button> :show-message-card="true"></button>
<!-- #endif --> <!-- #endif -->
<uni-popup ref="servicePopup" type="center"> <uni-popup ref="servicePopup" type="center">
<view class="service-popup-wrap"> <view class="service-popup-wrap">
<view class="head-wrap" @click="$refs.servicePopup.close()"> <view class="head-wrap" @click="$refs.servicePopup.close()">
<text>联系客服</text> <text>联系客服</text>
<text class="iconfont icon-close"></text> <text class="iconfont icon-close"></text>
</view> </view>
<view class="body-wrap">{{ siteInfo.site_tel ? '请联系客服,客服电话是' + siteInfo.site_tel : '抱歉,商家暂无客服,请线下联系' }}</view> <view class="body-wrap">{{ siteInfo.site_tel ? '请联系客服,客服电话是' + siteInfo.site_tel : '抱歉,商家暂无客服,请线下联系' }}</view>
</view> </view>
</uni-popup> </uni-popup>
</view> </view>
</template> </template>
<!-- 客服组件 --> <!-- 客服组件 -->
<script> <script>
export default { export default {
name: 'ns-contact', name: 'ns-contact',
props: { props: {
niushop: { niushop: {
type: Object, type: Object,
default: function() { default: function() {
return {}; return {};
} }
}, },
sendMessageTitle: { sendMessageTitle: {
type: String, type: String,
default: '' default: ''
}, },
sendMessagePath: { sendMessagePath: {
type: String, type: String,
default: '' default: ''
}, },
sendMessageImg: { sendMessageImg: {
type: String, type: String,
default: '' default: ''
} }
}, },
data() { data() {
return { return {
config: null, config: null,
openType: '' openType: ''
}; };
}, },
created() { created() {
if (this.servicerConfig) { if (this.servicerConfig) {
// #ifdef H5 // #ifdef H5
this.config = this.servicerConfig.h5; this.config = this.servicerConfig.h5;
// #endif // #endif
// #ifdef MP-WEIXIN // #ifdef MP-WEIXIN
this.config = this.servicerConfig.weapp; this.config = this.servicerConfig.weapp;
if (this.config.type == 'weapp') this.openType = 'contact'; if (this.config.type == 'weapp') this.openType = 'contact';
// #endif // #endif
// #ifdef MP-ALIPAY // #ifdef MP-ALIPAY
this.config = this.servicerConfig.aliapp; this.config = this.servicerConfig.aliapp;
if (this.config.type == 'aliapp') this.openType = 'contact'; if (this.config.type == 'aliapp') this.openType = 'contact';
// #endif // #endif
} }
}, },
methods: { methods: {
/** /**
* 联系客服 * 联系客服
*/ */
contactServicer() { contactServicer() {
if (this.config.type == 'none') { if (this.config.type == 'none') {
this.$refs.servicePopup.open(); this.$refs.servicePopup.open();
} }
if (this.openType == 'contact') return; if (this.openType == 'contact') return;
switch (this.config.type) { switch (this.config.type) {
case 'wxwork': case 'wxwork':
// #ifdef H5 this.openWxWorkService();
location.href = this.config.wxwork_url; break;
// #endif case 'third':
// #ifdef MP-WEIXIN location.href = this.config.third_url;
wx.openCustomerServiceChat({ break;
extInfo: { url: this.config.wxwork_url }, case 'niushop':
corpId: this.config.corpid, this.$util.redirectTo('/pages_tool/chat/room', this.niushop);
showMessageCard: true, break;
sendMessageTitle: this.sendMessageTitle, default:
sendMessagePath: this.sendMessagePath, this.makePhoneCall();
sendMessageImg: this.sendMessageImg }
}); },
// #endif /**
break; * 打开企业微信客服
case 'third': * @param {boolean} useOriginalService 是否使用原有客服方式
location.href = this.config.third_url; */
break; openWxWorkService(useOriginalService = false) {
case 'niushop': // #ifdef MP-WEIXIN
this.$util.redirectTo('/pages_tool/chat/room', this.niushop); if (this.config.wxwork_contact_url && !useOriginalService) {
break; // 直接使用活码链接跳转
default: wx.navigateToMiniProgram({
this.makePhoneCall(); appId: 'wxeb490c6f9b154ef9', // 企业微信小程序AppID
} path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(this.config.wxwork_contact_url)}`,
}, success: () => {
/** console.log('跳转企业微信成功');
* 店铺联系方式 },
*/ fail: (err) => {
makePhoneCall() { console.error('跳转企业微信失败:', err);
this.$api.sendRequest({ // 降级处理:使用原有客服方式
url: '/api/site/shopcontact', this.fallbackToOriginalService();
success: res => { }
if (res.code == 0 && res.data.mobile) uni.makePhoneCall({ });
phoneNumber: res.data } else {
.mobile // 使用原有的客服会话方式
}); wx.openCustomerServiceChat({
} extInfo: { url: this.config.wxwork_url },
}); corpId: this.config.corpid,
} showMessageCard: true,
} sendMessageTitle: this.sendMessageTitle,
}; sendMessagePath: this.sendMessagePath,
</script> sendMessageImg: this.sendMessageImg
});
<style lang="scss"> }
.contact-wrap { // #endif
width: 100%;
height: 100%; // #ifdef H5
position: relative; if (this.config.wxwork_contact_url) {
// H5环境直接跳转活码链接
.contact-button { window.location.href = this.config.wxwork_contact_url;
width: 100%; } else {
height: 100%; location.href = this.config.wxwork_url;
position: absolute; }
left: 0; // #endif
top: 0; },
z-index: 5;
padding: 0; /**
margin: 0; * 降级处理:使用原有客服方式
opacity: 0; */
overflow: hidden; fallbackToOriginalService() {
} uni.showModal({
} title: '提示',
content: '无法直接添加企业微信客服,是否使用其他方式联系客服?',
.service-popup-wrap { success: (res) => {
width: 600rpx; if (res.confirm) {
console.log('降级处理:使用原有客服方式');
.head-wrap { this.openWxWorkService(true);
display: flex; }
justify-content: space-between; }
align-items: center; });
padding: 0 30rpx; },
height: 90rpx; /**
} * 店铺联系方式
*/
.body-wrap { makePhoneCall() {
text-align: center; this.$api.sendRequest({
padding: 30rpx; url: '/api/site/shopcontact',
height: 100rpx; success: res => {
} if (res.code == 0 && res.data.mobile) uni.makePhoneCall({
} phoneNumber: res.data
.mobile
});
}
});
}
}
};
</script>
<style lang="scss">
.contact-wrap {
width: 100%;
height: 100%;
position: relative;
.contact-button {
width: 100%;
height: 100%;
position: absolute;
left: 0;
top: 0;
z-index: 5;
padding: 0;
margin: 0;
opacity: 0;
overflow: hidden;
}
}
.service-popup-wrap {
width: 600rpx;
.head-wrap {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 30rpx;
height: 90rpx;
}
.body-wrap {
text-align: center;
padding: 30rpx;
height: 100rpx;
}
}
</style> </style>

View File

@@ -0,0 +1,285 @@
<template>
<view class="wxwork-contact-wrap">
<slot></slot>
<button
type="default"
hover-class="none"
class="contact-button"
@click="addWxWorkContact">
<text class="btn-text">{{ btnText }}</text>
</button>
<!-- 确认弹窗 -->
<uni-popup ref="confirmPopup" type="center">
<view class="confirm-popup">
<view class="popup-header">
<text>添加企业微信客服</text>
</view>
<view class="popup-body">
<text>点击确定后将跳转至企业微信添加专业客服为您服务</text>
</view>
<view class="popup-footer">
<button class="cancel-btn" @click="closePopup">取消</button>
<button class="confirm-btn" @click="confirmAdd">确定</button>
</view>
</view>
</uni-popup>
</view>
</template>
<script>
import { WxWork } from '@/common/js/wxwork-jssdk.js';
export default {
name: 'wxwork-contact',
props: {
// 按钮文字
btnText: {
type: String,
default: '添加企业微信客服'
},
// 企业微信配置
corpId: {
type: String,
default: ''
},
// 客服ID或活码配置ID
contactId: {
type: String,
default: ''
},
// 活码链接
contactUrl: {
type: String,
default: ''
},
// 是否显示确认弹窗
showConfirm: {
type: Boolean,
default: true
}
},
data() {
return {
wxWorkSDK: null
};
},
mounted() {
this.initWxWork();
},
methods: {
/**
* 初始化企业微信SDK
*/
async initWxWork() {
try {
// 获取企业微信配置
const res = await this.$api.sendRequest({
url: '/api/wxwork/config',
data: {
corp_id: this.corpId
}
});
if (res.code === 0 && res.data) {
this.wxWorkSDK = new WxWork();
const initResult = this.wxWorkSDK.init({
corpId: res.data.corp_id,
agentId: res.data.agent_id,
timestamp: res.data.timestamp,
nonceStr: res.data.nonceStr,
signature: res.data.signature,
jsApiList: ['openUserProfile', 'openEnterpriseChat']
});
if (!initResult) {
console.error('企业微信SDK初始化失败');
}
}
} catch (error) {
console.error('获取企业微信配置失败:', error);
}
},
/**
* 点击添加企业微信客服
*/
addWxWorkContact() {
if (this.showConfirm) {
this.$refs.confirmPopup.open();
} else {
this.confirmAdd();
}
},
/**
* 确认添加
*/
confirmAdd() {
this.closePopup();
// #ifdef MP-WEIXIN
if (this.contactUrl) {
// 方案1直接跳转到企业微信活码
this.jumpToWxWorkContact();
} else if (this.contactId) {
// 方案2使用SDK打开用户资料
this.openUserProfile();
} else {
this.showError('未配置企业微信客服信息');
}
// #endif
// #ifdef H5
if (this.contactUrl) {
// H5环境直接跳转
window.location.href = this.contactUrl;
} else {
this.showError('未配置企业微信客服信息');
}
// #endif
},
/**
* 跳转到企业微信客服
*/
jumpToWxWorkContact() {
uni.navigateToMiniProgram({
appId: 'wxeb490c6f9b154ef9', // 企业微信小程序AppID
path: `pages/contacts/externalContactDetail?url=${encodeURIComponent(this.contactUrl)}`,
success: () => {
console.log('跳转企业微信成功');
this.$util.showToast({
title: '跳转成功',
icon: 'success'
});
},
fail: (err) => {
console.error('跳转企业微信失败:', err);
this.showError('跳转失败,请检查企业微信配置');
}
});
},
/**
* 打开用户资料
*/
openUserProfile() {
if (!this.wxWorkSDK) {
this.showError('企业微信SDK未初始化');
return;
}
this.wxWorkSDK.addContact({
userId: this.contactId
}, (res) => {
console.log('打开用户资料成功:', res);
}, (err) => {
console.error('打开用户资料失败:', err);
this.showError('打开用户资料失败');
});
},
/**
* 显示错误提示
*/
showError(message) {
uni.showModal({
title: '提示',
content: message,
showCancel: false
});
},
/**
* 关闭弹窗
*/
closePopup() {
this.$refs.confirmPopup.close();
}
}
};
</script>
<style lang="scss" scoped>
.wxwork-contact-wrap {
width: 100%;
height: 100%;
position: relative;
.contact-button {
width: 100%;
height: 100%;
background: linear-gradient(135deg, #1e7dd8 0%, #1482e0 100%);
color: #fff;
border: none;
border-radius: 8rpx;
font-size: 28rpx;
display: flex;
align-items: center;
justify-content: center;
.btn-text {
font-weight: 500;
}
&:active {
opacity: 0.8;
}
}
}
.confirm-popup {
width: 600rpx;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
.popup-header {
padding: 40rpx 30rpx 20rpx;
text-align: center;
font-size: 32rpx;
font-weight: 600;
color: #333;
border-bottom: 1px solid #f0f0f0;
}
.popup-body {
padding: 30rpx;
text-align: center;
font-size: 28rpx;
color: #666;
line-height: 1.6;
}
.popup-footer {
display: flex;
border-top: 1px solid #f0f0f0;
button {
flex: 1;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 30rpx;
border: none;
border-radius: 0;
&.cancel-btn {
background: #fff;
color: #999;
border-right: 1px solid #f0f0f0;
}
&.confirm-btn {
background: #1e7dd8;
color: #fff;
}
&:active {
opacity: 0.8;
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff