chore:合并了之前智能客服的代码

This commit is contained in:
2026-01-13 16:42:06 +08:00
parent 2280ff68fa
commit 286fcc2142

View File

@@ -1,341 +1,285 @@
<template> <template>
<!-- 悬浮按钮 --> <view v-if="pageCount == 1 || need" class="fixed-box"
<view v-if="pageCount == 1 || need" class="fixed-box" :style="[{ height: fixBtnShow ? '400rpx' : '320rpx' }, customContainerStyle]">
:style="[{ height: fixBtnShow ? '400rpx' : '320rpx' }, customContainerStyle]">
<!-- 切换语言按钮 -->
<button
class="btn-item"
v-if="fixBtnShow && isLanguageSwitchEnabled"
@click="toggleLanguage()"
:style="[{ backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text :style="customTextStyle">{{ currentLangDisplayName }}</text>
</button>
<!-- AI 智能助手 客服按钮根据 enableAIChat 决定 -->
<template v-if="fixBtnShow"> <!-- AI 智能助手优先显示 -->
<!-- 优先显示 AI 助手如果启用 --> <view
<view v-if="fixBtnShow && enableAIChat"
v-if="enableAIChat" class="btn-item"
class="btn-item" @click="openAIChat"
@click="openAIChat" :style="{ backgroundImage: aiAgentimg ? `url(${aiAgentimg})` : '', backgroundSize: '100% 100%' }"
:style="{ backgroundImage: aiAgentimg ? `url(${aiAgentimg})` : '', backgroundSize: '100% 100%' }" >
> <text class="ai-icon" v-if="!aiAgentimg">🤖</text>
<text class="ai-icon" v-if="!aiAgentimg">🤖</text> <view v-if="unreadCount > 0" class="unread-badge">
<view v-if="unreadCount > 0" class="unread-badge"> <text class="badge-text">{{ unreadCount > 99 ? '99+' : unreadCount }}</text>
<text class="badge-text">{{ unreadCount > 99 ? '99+' : unreadCount }}</text> </view>
</view> </view>
<!-- 普通客服仅当未启用 AI 时显示 -->
<template v-else-if="fixBtnShow">
<!-- #ifdef MP-WEIXIN -->
<button
class="btn-item"
hoverClass="none"
openType="contact"
sessionFrom="weapp"
showMessageCard="true"
:style="[{ backgroundImage: kefuimg ? `url(${kefuimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button
class="btn-item"
hoverClass="none"
@click="openKefuSelectPopup"
:style="[{ backgroundImage: kefuimg ? `url(${kefuimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- #endif -->
</template>
<!-- 电话按钮始终显示 -->
<view
v-if="fixBtnShow"
class="btn-item"
@click="call()"
:style="[{ backgroundImage: phoneimg ? `url(${phoneimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
</view>
</view> </view>
<!-- 否则显示客服按钮 -->
<template v-else>
<!-- #ifdef MP-WEIXIN -->
<button
class="btn-item"
hoverClass="none"
openType="contact"
sessionFrom="weapp"
showMessageCard="true"
:style="[{ backgroundImage: kefuimg ? `url(${kefuimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<button
class="btn-item"
hoverClass="none"
@click="openKefuSelectPopup"
:style="[{ backgroundImage: kefuimg ? `url(${kefuimg})` : '', backgroundSize: '100% 100%' }, customButtonStyle]"
>
<text class="icox icox-kefu" v-if="!kefuimg"></text>
<!-- <view>首页</view> -->
</button>
<!-- #endif -->
<view class="btn-item" v-if="fixBtnShow" @click="call()"
:style="[{ backgroundImage: 'url(' + (phoneimg ? phoneimg : '') + ')', backgroundSize: '100% 100%' }, customButtonStyle]">
<text class="iconfont icon-dianhua" v-if="!phoneimg"></text>
</view>
<!-- <view class="btn-item icon-xiala" v-if="fixBtnShow" @click="fixBtnShow ? (fixBtnShow = false) : (fixBtnShow = true)">
<text class="iconfont icon-unfold"></text>
</view>
<view class="btn-item switch" v-else :class="{ show: fixBtnShow }"
@click="fixBtnShow ? (fixBtnShow = false) : (fixBtnShow = true)">
<view class="">快捷</view>
<view>导航</view>
</view> -->
</view>
</template> </template>
<script> <script>
import { mapGetters } from 'vuex' // 去掉mapMutations直接用$store.commit
import { createCustomerService } from '@/common/js/customer-service.js'; import { createCustomerService } from '@/common/js/customer-service.js';
export default { export default {
name: 'hover-nav', name: 'hover-nav',
props: { props: {
need: { need: { type: Boolean, default: false }
type: Boolean, },
default: false data() {
} return {
}, pageCount: 0,
data() { fixBtnShow: true,
return { tel: '',
pageCount: 0, kefuimg: '',
fixBtnShow: true, phoneimg: '',
tel: '', shopInfo: null,
kefuimg: '', enableAIChat: false,
phoneimg: '', aiAgentimg: '',
shopInfo: null, // 店铺信息对象 // 从 Vuex 获取未读数
// 注意:这里不要在 data 里定义 unreadCount用 computed 更好
currentLangIndex: 0,
langIndexMap: {},
isLanguageSwitchEnabled: false,
kefuList: [
{ id: 'weixin-official', name: '微信官方客服', isOfficial: true, type: 'weapp' },
{ id: 'custom-kefu', name: '自定义在线客服', isOfficial: false, type: 'custom' },
{ id: 'qyweixin-kefu', name: '企业微信客服', isOfficial: false, type: 'qyweixin' }
],
selectedKefu: null,
customerService: null,
buttonConfig: null
};
},
created() {
this.initLanguage();
this.kefuimg = this.$util.getDefaultImage().kefu;
this.phoneimg = this.$util.getDefaultImage().phone;
this.pageCount = getCurrentPages().length;
// --- 语言切换相关 --- uni.getStorage({
currentLangIndex: 0, // 当前选中的语言索引 key: 'shopInfo',
langIndexMap: {}, // 语言索引到语言值的映射 success: (e) => {
isLanguageSwitchEnabled: false, // 是否启用语言切换 this.shopInfo = e.data;
this.tel = e.data.mobile;
this.isLanguageSwitchEnabled = !!e.data.ischina;
this.enableAIChat = !!e.data.enableAIChat;
this.aiAgentimg = e.data.aiAgentimg || '';
}
});
},
computed: {
currentLangDisplayName() {
const lang = this.langIndexMap[this.currentLangIndex];
return lang === 'zh-cn' ? 'EN' : 'CN';
},
customContainerStyle() {
return this.shopInfo?.floatingButton?.container || {};
},
customButtonStyle() {
return this.shopInfo?.floatingButton?.button || {};
},
customTextStyle() {
return this.shopInfo?.floatingButton?.text || {};
},
// 👇 关键:从 Vuex 读取未读消息数
unreadCount() {
return this.$store.state.aiUnreadCount || 0;
}
},
methods: {
initLanguage() {
this.langList = this.$langConfig.list();
this.langIndexMap = {};
for (let i = 0; i < this.langList.length; i++) {
this.langIndexMap[i] = this.langList[i].value;
}
const savedLang = uni.getStorageSync('lang');
if (savedLang) {
for (let i = 0; i < this.langList.length; i++) {
if (this.langList[i].value === savedLang) {
this.currentLangIndex = i;
break;
}
}
} else {
this.currentLangIndex = 0;
}
},
call() {
uni.makePhoneCall({ phoneNumber: this.tel + '' });
},
toggleLanguage() {
this.currentLangIndex = this.currentLangIndex === 0 ? 1 : 0;
const targetLang = this.langIndexMap[this.currentLangIndex];
let currentRoute = this.$util.getCurrentRoute().path;
this.$langConfig.change(targetLang, currentRoute);
},
openKefuSelectPopup() {
const kefuNames = this.kefuList.map(item => item.name);
uni.showActionSheet({
itemList: kefuNames,
success: (res) => {
this.selectedKefu = this.kefuList[res.tapIndex];
this.reinitCustomerService(this.selectedKefu);
this.handleSelectedKefu();
}
});
},
reinitCustomerService(kefu) {
this.customerService = createCustomerService(this, kefu);
this.buttonConfig = this.customerService.getButtonConfig();
},
handleSelectedKefu() {
const kefu = this.selectedKefu;
if (!kefu) return;
// --- 其他 --- if (kefu.isOfficial) {
}; uni.openCustomerServiceConversation({
}, sessionFrom: 'weapp',
created() { showMessageCard: true
// 初始化语言设置 });
this.initLanguage(); } else if (kefu.id === 'custom-kefu') {
this.customerService.handleCustomerClick();
this.kefuimg = this.$util.getDefaultImage().kefu } else if (kefu.id === 'qyweixin-kefu') {
this.phoneimg = this.$util.getDefaultImage().phone this.customerService.handleQyWeixinKefuClick();
this.pageCount = getCurrentPages().length; }
},
// 从店铺信息中获取相关信息 openAIChat() {
uni.getStorage({ if (this.enableAIChat) {
key: 'shopInfo', this.$store.commit('setAiUnreadCount', 0);
success: (e) => { }
console.log(`获取到的shopInfo:${JSON.stringify(e.data)}`) this.$util.redirectTo('/pages_tool/ai-chat/index');
this.shopInfo = e.data; }
}
// 从店铺信息中获取手机号
this.tel = e.data.mobile
// 从店铺信息中获取是否启用语言切换
this.isLanguageSwitchEnabled = e.data.ischina;
}
})
},
computed: {
currentLangDisplayName() {
// 语言切换按钮显示名称当前语言为中文时显示EN否则显示CN
// 根据 langIndexMap 获取当前语言值
const lang = this.langIndexMap[this.currentLangIndex];
return lang == 'zh-cn' ? 'EN' : 'CN';
},
};
// 获取容器的自定义样式
customContainerStyle() {
// 只返回非空的样式属性,确保不覆盖原有的默认样式
return this.shopInfo?.floatingButton?.container || {};
data() {
return {
pageCount: 0,
fixBtnShow: true,
tel: '',
kefuimg: '',
phoneimg: '',
customerService: null,
buttonConfig: null,
kefuList: [
{ id: 'weixin-official', name: '微信官方客服', isOfficial: true, type: 'weapp' },
{ id: 'custom-kefu', name: '自定义在线客服', isOfficial: false, type: 'custom' },
{ id: 'qyweixin-kefu', name: '企业微信客服', isOfficial: false, type: 'qyweixin' }
],
selectedKefu: null
}; },
// 获取按钮的自定义样式
customButtonStyle() {
// 只返回非空的样式属性,确保不覆盖原有的默认样式
return this.shopInfo?.floatingButton?.button || {};
},
// 获取文本的自定义样式
customTextStyle() {
// 只返回非空的样式属性,确保不覆盖原有的默认样式
return this.shopInfo?.floatingButton?.text || {};
}
},
methods: {
initLanguage() {
// 初始化语言列表
this.langList = this.$langConfig.list();
// 初始化语言索引映射
for (let i = 0; i < this.langList.length; i++) {
this.langIndexMap[i] = this.langList[i].value;
}
// 获取存储的语言设置
const savedLang = uni.getStorageSync('lang');
if (savedLang) {
for (let i = 0; i < this.langList.length; i++) {
if (this.langList[i].value == savedLang) {
this.currentLangIndex = i;
break;
}
}
} else {
this.currentLangIndex = 0;
}
},
//拨打电话
call() {
uni.makePhoneCall({
phoneNumber: this.tel + ''
})
},
toggleLanguage() {
// 切换语言索引
this.currentLangIndex = this.currentLangIndex == 0 ? 1 : 0;
const targetLang = this.langIndexMap[this.currentLangIndex];
// 获取当前页面路由
let currentRoute = this.$util.getCurrentRoute().path;
// 切换语言并重新加载当前页面
this.$langConfig.change(targetLang, currentRoute);
}
}
}; };
</script> </script>
// 打开客服选择弹窗 <style scoped>
openKefuSelectPopup() { .fixed-box {
const kefuNames = this.kefuList.map(item => item.name); position: fixed;
wx.showActionSheet({ right: 0rpx;
itemList: kefuNames, bottom: 240rpx;
success: (res) => { /* #ifdef H5 */
this.selectedKefu = this.kefuList[res.tapIndex]; bottom: 320rpx;
this.reinitCustomerService(this.selectedKefu); /* #endif */
this.handleSelectedKefu(); z-index: 10;
} border-radius: 120rpx;
}); padding: 20rpx 0;
}, display: flex;
justify-content: center;
// 重新初始化客服实例 flex-direction: column;
reinitCustomerService(kefu) { width: 100rpx;
this.customerService = createCustomerService(this, kefu); box-sizing: border-box;
this.buttonConfig = this.customerService.getButtonConfig(); transition: 0.3s;
}, overflow: hidden;
}
// 执行选中的客服逻辑
handleSelectedKefu() {
const kefu = this.selectedKefu;
if (!kefu) return;
if (kefu.isOfficial) {
wx.openCustomerServiceConversation({ sessionFrom: 'weapp', showMessageCard: true });
} else if (kefu.id === 'custom-kefu') {
this.customerService.handleCustomerClick();
} else if (kefu.id === 'qyweixin-kefu') {
this.customerService.handleQyWeixinKefuClick();
}
},
// 打开AI聊天修复setAiUnreadCount的核心
openAIChat() {
if (this.enableAIChat) {
// 直接用$store.commit调用Vuex mutation绕过映射问题
this.$store.commit('setAiUnreadCount', 0);
}
this.$util.redirectTo('/pages_tool/ai-chat/index')
}
}
};
</script>
} }
//悬浮按钮 .btn-item {
.fixed-box { display: flex;
position: fixed; justify-content: center;
right: 0rpx; text-align: center;
bottom: 240rpx; flex-direction: column;
// #ifdef H5 line-height: 1;
bottom: 320rpx; margin: 14rpx 0;
// #endif transition: 0.1s;
z-index: 10; background: var(--hover-nav-bg-color);
border-radius: 120rpx; color: var(--hover-nav-text-color);
padding: 20rpx 0; border-radius: 40rpx;
display: flex; width: 80rpx;
justify-content: center; height: 80rpx;
flex-direction: column; padding: 0;
width: 100rpx; overflow: hidden;
box-sizing: border-box; }
transition: 0.3s;
overflow: hidden;
.btn-item { .btn-item text {
display: flex; font-size: 28rpx;
justify-content: center; }
text-align: center;
flex-direction: column;
line-height: 1;
margin: 14rpx 0;
transition: 0.1s;
background: var(--hover-nav-bg-color);
color: var(--hover-nav-text-color);
border-radius: 40rpx;
width: 80rpx;
height: 80rpx;
padding: 0;
overflow: hidden;
text { .iconfont,
font-size: 28rpx; .icox {
} font-size: 36rpx;
.iconfont, font-weight: bold;
.icox { width: 100%;
font-size: 36rpx; height: 100%;
font-weight: bold; display: flex;
width: 100%; justify-content: center;
height: 100%; align-items: center;
display: flex; margin: 14rpx 0;
justify-content: center; background: #fff;
align-items: center; border-radius: 50rpx;
margin: 14rpx 0; width: 80rpx;
background: #fff; height: 80rpx;
border-radius: 50rpx; padding: 0;
width: 80rpx; position: relative;
height: 80rpx; }
padding: 0;
position: relative;
text { .iconfont text,
font-size: 36rpx; .icox text {
font-weight: bold; font-size: 36rpx;
} font-weight: bold;
}
.unread-badge { .unread-badge {
position: absolute; position: absolute;
top: -5rpx; top: -5rpx;
right: -5rpx; right: -5rpx;
background-color: #ff4544; background-color: #ff4544;
color: white; color: white;
border-radius: 20rpx; border-radius: 20rpx;
min-width: 30rpx; min-width: 30rpx;
height: 30rpx; height: 30rpx;
font-size: 20rpx; font-size: 20rpx;
line-height: 30rpx; line-height: 30rpx;
text-align: center; text-align: center;
padding: 0 8rpx; padding: 0 8rpx;
box-shadow: 0 2rpx 10rpx rgba(255, 69, 68, 0.3); box-shadow: 0 2rpx 10rpx rgba(255, 69, 68, 0.3);
} }
.ai-icon { .ai-icon {
font-size: 40rpx; font-size: 40rpx;
width: 100%; width: 100%;
height: 100%; height: 100%;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
}
}
}
} }
</style> </style>