feat: 客服

This commit is contained in:
2026-07-20 15:31:52 +08:00
parent 0099ccaa60
commit 82a8043578
23 changed files with 5071 additions and 360 deletions
+258 -202
View File
@@ -5,37 +5,25 @@
<view class="mine_data">
<up-image
:src="currentUserData.avatarUrl ? currentUserData.avatarUrl : 'https://static.tbmall.xin/static/mine/avatar.png'"
shape="circle"
width="54"
height="54"
bgColor="#f1f6ff00"
></up-image>
shape="circle" width="54" height="54" bgColor="#f1f6ff00"></up-image>
<view class="mine_info">
<view class="mine_info_name">
<text style="margin-right: 20rpx">{{ currentUserData.nickname || '信任桥用户' }}</text>
<view class="level level-grade" @click="jumpToTrustBridge" v-if="currentUserData.agentName">
<up-image
src="https://static.tbmall.xin/static/mine/rz.png"
style="margin-right: 8rpx; margin-top: 6rpx"
shape="circle"
width="13"
height="13"
bgColor="#f1f6ff00"
></up-image>
<up-image src="https://static.tbmall.xin/static/mine/rz.png"
style="margin-right: 8rpx; margin-top: 6rpx" shape="circle" width="13" height="13"
bgColor="#f1f6ff00"></up-image>
<view>{{ currentUserData.agentName }}</view>
</view>
<view :class="['level', currentUserData.grade ? 'level-name' : 'level-grade']" v-if="!currentUserData.agentName && currentUserData.gradeName">
<view :class="['level', currentUserData.grade ? 'level-name' : 'level-grade']"
v-if="!currentUserData.agentName && currentUserData.gradeName">
<view style="display: flex; align-items: center">
<up-image
:src="currentUserData.grade ? '/static/common/level.png' : 'https://static.tbmall.xin/static/mine/rz.png'"
style="margin-right: 8rpx; margin-top: 6rpx"
shape="circle"
width="14"
height="14"
bgColor="#f1f6ff00"
></up-image>
style="margin-right: 8rpx; margin-top: 6rpx" shape="circle" width="14"
height="14" bgColor="#f1f6ff00"></up-image>
<view>{{ currentUserData.gradeName }}</view>
</view>
</view>
@@ -58,17 +46,20 @@
</view>
<view class="mine_info_id">
<text style="margin-right: 20rpx">邀请码:{{ currentUserData.inviteCode }}</text>
<up-image src="/static/common/copy.png" width="14" height="14" bgColor="#f1f6ff00" @click="copyData(currentUserData.inviteCode)"></up-image>
<up-image src="/static/common/copy.png" width="14" height="14" bgColor="#f1f6ff00"
@click="copyData(currentUserData.inviteCode)"></up-image>
</view>
</view>
</view>
<view @click="jumpSetting">
<up-image src="https://static.tbmall.xin/static/mine/icon_arrow_black_off.png" width="22" height="22" bgColor="#f1f6ff00"></up-image>
<up-image src="https://static.tbmall.xin/static/mine/icon_arrow_black_off.png" width="22"
height="22" bgColor="#f1f6ff00"></up-image>
</view>
</view>
<view class="mine_no_login_data" v-else>
<view class="mine_data">
<up-image src="https://static.tbmall.xin/static/avater_not.png" shape="circle" width="80" height="80" bgColor="#f1f6ff00"></up-image>
<up-image src="https://static.tbmall.xin/static/avater_not.png" shape="circle" width="80"
height="80" bgColor="#f1f6ff00"></up-image>
<view class="mine_info_not_login">未登录</view>
</view>
<view @click="jumpLogin" class="login_warp">
@@ -84,20 +75,24 @@
<view>我的订单</view>
<view class="mine_order_title_all" @click="jumpOrder({})">
<view style="margin-right: 10rpx; font-size: 28rpx; font-weight: normal">全部</view>
<up-image style="margin-top: 10rpx" src="/static/common/right.png" width="15" height="15" bgColor="#f1f6ff00"></up-image>
<up-image style="margin-top: 10rpx" src="/static/common/right.png" width="15" height="15"
bgColor="#f1f6ff00"></up-image>
</view>
</view>
<view class="mine_order_list">
<view class="mine_fontsize_comm_warp order_list_item" v-for="item in orderTemplList" :key="item.id" @click="jumpOrder(item)">
<view class="mine_fontsize_comm_warp order_list_item" v-for="item in orderTemplList" :key="item.id"
@click="jumpOrder(item)">
<up-image :src="item.url" width="28" height="28" bgColor="#f1f6ff00"></up-image>
<view>{{ item.title }}</view>
<up-badge :absolute="true" :offset="[0, 8]" max="99" :value="statisticsObj[item.note]"></up-badge>
<up-badge :absolute="true" :offset="[0, 8]" max="99"
:value="statisticsObj[item.note]"></up-badge>
</view>
</view>
</view>
<!-- 应用 -->
<view class="mine_content_comm_warp mine_top_comm_warp apply_list">
<view class="apply_item mine_fontsize_comm_warp" v-for="item in applyList" :key="item.id" @click="jumpTool(item)">
<view class="apply_item mine_fontsize_comm_warp" v-for="item in applyList" :key="item.id"
@click="jumpTool(item)">
<up-image :src="item.url" width="36" height="36" bgColor="#f1f6ff00"></up-image>
<view>{{ item.title }}</view>
</view>
@@ -168,7 +163,11 @@
<view class="mine_tool_title">服务工具</view>
<view class="tool_list">
<view class="tool_item" v-for="item in toolList" :key="item.id" @click="jumpTool(item)">
<up-image :src="item.url" width="28" height="28" bgColor="#f1f6ff00"></up-image>
<view class="tool-icon-wrap">
<up-image :src="item.url" width="28" height="28" bgColor="#f1f6ff00"></up-image>
<view v-if="item.id === 7 && kefuUnreadCount > 0" class="kefu-unread-dot"
aria-label="有新的客服消息"></view>
</view>
<text class="mine_fontsize_comm_warp">{{ item.title }}</text>
</view>
</view>
@@ -236,6 +235,7 @@ import CustomTabBar from '@/components/custom-tab-bar.vue';
import { getStatistics } from '@/api/order.js';
import { getUserInfo } from '@/api/auth.js';
import { CUSTOMER_SERVICE_PHONE_NUMBER } from '@/utils/config.js';
import { getUnreadCount as getKefuUnreadCount } from "@/uni_modules/hashmall-customer-service/js_sdk/api.js";
export default {
computed: {
@@ -411,7 +411,9 @@ export default {
}, // 角标统计
serviceShow: false, // 我的客服弹框
openShow: false, // 暂未开放
platformType: true
platformType: true,
kefuUnreadCount: 0,
kefuUnreadTimer: null,
};
},
onShow() {
@@ -426,16 +428,45 @@ export default {
this.getStatistics();
this.getUserInfo();
this.startKefuUnreadPolling();
},
mounted() {},
onHide() {
this.stopKefuUnreadPolling();
},
onUnload() {
this.stopKefuUnreadPolling();
},
mounted() { },
methods: {
copyData,
async refreshKefuUnreadCount() {
const userId = this.currentUserData && this.currentUserData.id;
if (!userId) {
this.kefuUnreadCount = 0;
return;
}
try {
this.kefuUnreadCount = await getKefuUnreadCount(userId);
} catch (error) {
this.kefuUnreadCount = 0;
}
},
startKefuUnreadPolling() {
this.stopKefuUnreadPolling();
this.refreshKefuUnreadCount();
this.kefuUnreadTimer = setInterval(() => this.refreshKefuUnreadCount(), 15000);
},
stopKefuUnreadPolling() {
if (this.kefuUnreadTimer) clearInterval(this.kefuUnreadTimer);
this.kefuUnreadTimer = null;
},
// 查询用户等级
async getUserInfo() {
const result = await getUserInfo();
if (result && result.bizcode === 100) {
const data = result.data;
this.currentUserData = result.data || {};
this.refreshKefuUnreadCount();
if (!this.currentUserData?.isNewUser) {
this.applyList = this.applyList.filter((item) => item.id !== 5);
}
@@ -464,7 +495,7 @@ export default {
// 客服
if (item.id === 7) {
uni.navigateTo({
url: '/pages/rwa_package/setting/kefu?type=5257'
url: "/pages/rwa_package/setting/kefu",
});
} else if (item.id === 9) {
// 实名认证
@@ -557,274 +588,299 @@ export default {
<style lang="scss">
.mine_warp {
height: calc(100vh - env(safe-area-inset-bottom));
overflow: auto;
position: relative;
background-color: $app-bj;
height: calc(100vh - env(safe-area-inset-bottom));
overflow: auto;
position: relative;
background-color: $app-bj;
&.iosplatformType {
height: 100vh;
&.iosplatformType {
height: 100vh;
.mine_content_warp {
padding-bottom: 100rpx !important;
}
.mine_content_warp {
padding-bottom: 100rpx !important;
}
}
}
.mine_header {
// height: 300rpx;
background: linear-gradient(180deg, #f2ebff 0%, rgba(245, 245, 247, 0) 100%, #ffffff 100%);
/* 将图片居中显示 */
padding: 90rpx 20rpx 0;
// height: 300rpx;
background: linear-gradient(180deg,
#f2ebff 0%,
rgba(245, 245, 247, 0) 100%,
#ffffff 100%);
/* 将图片居中显示 */
padding: 90rpx 20rpx 0;
.mine_login_data {
display: flex;
justify-content: space-between;
align-items: center;
}
}
.mine_content_warp {
margin-top: 20rpx;
margin-left: 30rpx;
width: 690rpx;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
}
.mine_no_login_data {
.mine_login_data {
display: flex;
justify-content: space-between;
align-items: center;
}
}
.login_warp {
display: flex;
align-items: center;
font-weight: 500;
font-size: 26rpx;
color: #111111;
}
.mine_content_warp {
margin-top: 20rpx;
margin-left: 30rpx;
width: 690rpx;
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
}
.mine_no_login_data {
display: flex;
justify-content: space-between;
align-items: center;
.login_warp {
display: flex;
align-items: center;
font-weight: 500;
font-size: 26rpx;
color: #111111;
}
}
.mine_data {
display: flex;
align-items: center;
display: flex;
align-items: center;
}
.mine_info_not_login {
font-weight: bold;
font-size: 40rpx;
color: #111111;
font-weight: bold;
font-size: 40rpx;
color: #111111;
}
.mine_info {
margin-left: 20rpx;
margin-left: 20rpx;
}
.mine_info_name {
font-weight: bold;
font-size: 40rpx;
color: #111111;
font-weight: bold;
font-size: 40rpx;
color: #111111;
display: flex;
justify-content: start;
align-items: center;
.grade_warp {
position: relative;
}
.level {
border-radius: 8rpx;
display: flex;
justify-content: start;
justify-content: center;
align-items: center;
padding: 0rpx 16rpx;
font-size: 24rpx;
height: 44rpx;
.grade_warp {
position: relative;
&.level-name {
background: rgba(248, 185, 75, 0.15);
color: #613700;
margin-left: 10rpx;
}
.level {
border-radius: 8rpx;
display: flex;
justify-content: center;
align-items: center;
padding: 0rpx 16rpx;
font-size: 24rpx;
height: 44rpx;
&.level-name {
background: rgba(248, 185, 75, 0.15);
color: #613700;
margin-left: 10rpx;
}
&.level-grade {
background: rgba(122, 53, 246, 0.1);
color: #7a35f6;
}
&.level-grade {
background: rgba(122, 53, 246, 0.1);
color: #7a35f6;
}
}
.grade_key_warp {
position: absolute;
top: 0rpx;
left: 30%;
font-size: 24rpx;
color: #ffffff;
width: 100rpx;
height: 44rpx;
line-height: 50rpx;
text-align: center;
}
.grade_key_warp {
position: absolute;
top: 0rpx;
left: 30%;
font-size: 24rpx;
color: #ffffff;
width: 100rpx;
height: 44rpx;
line-height: 50rpx;
text-align: center;
}
.grade_key_warp1 {
font-size: 24rpx;
line-height: 48rpx;
}
.grade_key_warp1 {
font-size: 24rpx;
line-height: 48rpx;
}
.grade_key_warp3 {
font-size: 20rpx;
}
.grade_key_warp3 {
font-size: 20rpx;
}
}
.mine_info_id {
font-weight: 500;
font-size: 24rpx;
color: #666666;
display: flex;
margin-top: 10rpx;
font-weight: 500;
font-size: 24rpx;
color: #666666;
display: flex;
margin-top: 10rpx;
}
/* 内容公共 start */
.mine_content_comm_warp {
background: #ffffff;
box-shadow: 0rpx 0rpx 10rpx 0rpx #eeeeee;
border-radius: 30rpx;
padding: 30rpx;
background: #ffffff;
box-shadow: 0rpx 0rpx 10rpx 0rpx #eeeeee;
border-radius: 30rpx;
padding: 30rpx;
}
.mine_fontsize_comm_warp {
font-weight: 500;
font-size: 24rpx;
color: #222222;
font-weight: 500;
font-size: 24rpx;
color: #222222;
}
.mine_top_comm_warp {
margin-top: 30rpx;
margin-top: 30rpx;
.settled_list {
width: 100%;
box-sizing: border-box;
.settled_list {
width: 100%;
box-sizing: border-box;
.enter_item {
width: 100%;
margin-top: 10rpx;
padding: 12rpx 0;
box-sizing: border-box;
.enter_item {
width: 100%;
margin-top: 10rpx;
padding: 12rpx 0;
box-sizing: border-box;
.enter_item_cell {
display: flex;
.enter_item_cell {
display: flex;
.u-transition {
margin-top: 5rpx;
}
.enter_item_text {
margin-left: 10rpx;
.enter_remark {
font-size: 24rpx;
color: #999999;
margin-top: 10rpx;
}
}
}
.u-transition {
margin-top: 5rpx;
}
.enter_item_text {
margin-left: 10rpx;
.enter_remark {
font-size: 24rpx;
color: #999999;
margin-top: 10rpx;
}
}
}
}
}
}
/* 内容公共 end */
.mine_order_title {
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
font-size: 32rpx;
color: #333333;
display: flex;
align-items: center;
justify-content: space-between;
font-weight: bold;
font-size: 32rpx;
color: #333333;
}
.mine_order_title_all {
display: flex;
align-items: center;
justify-content: center;
font-size: 20rpx;
color: #bbbbbb;
display: flex;
align-items: center;
justify-content: center;
font-size: 20rpx;
color: #bbbbbb;
}
.mine_order_list {
display: flex;
justify-content: space-between;
margin: 40rpx 0 10rpx 0;
display: flex;
justify-content: space-between;
margin: 40rpx 0 10rpx 0;
}
.order_list_item {
width: 25%;
text-align: center;
position: relative;
width: 25%;
text-align: center;
position: relative;
}
.apply_list {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
}
.apply_item {
width: 20%;
text-align: center;
width: 20%;
text-align: center;
}
.enter_list {
display: flex;
justify-content: space-between;
display: flex;
justify-content: space-between;
}
.enter_item {
width: 40%;
background: #ffffff;
border-radius: 30rpx;
display: flex;
justify-content: space-between;
padding: 12rpx 30rpx;
align-items: center;
width: 40%;
background: #ffffff;
border-radius: 30rpx;
display: flex;
justify-content: space-between;
padding: 12rpx 30rpx;
align-items: center;
}
.enter_title {
font-weight: bold;
font-size: 26rpx;
color: #333333;
font-weight: bold;
font-size: 26rpx;
color: #333333;
}
.enter_apply {
font-weight: 400;
font-size: 24rpx;
color: #666666;
display: flex;
align-items: center;
margin-top: 10rpx;
font-weight: 400;
font-size: 24rpx;
color: #666666;
display: flex;
align-items: center;
margin-top: 10rpx;
}
.enter_apply_title {
margin-right: 10rpx;
margin-right: 10rpx;
}
.mine_tool_title {
font-weight: bold;
font-size: 30rpx;
color: #333333;
font-weight: bold;
font-size: 30rpx;
color: #333333;
}
.tool_list {
display: flex;
flex-wrap: wrap;
margin-top: 10rpx;
display: flex;
flex-wrap: wrap;
margin-top: 10rpx;
}
.tool_item {
width: 25%;
text-align: center;
margin-top: 20rpx;
width: 25%;
text-align: center;
margin-top: 20rpx;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.tool-icon-wrap {
position: relative;
display: inline-flex;
}
.kefu-unread-dot {
position: absolute;
top: -5rpx;
right: -7rpx;
width: 14rpx;
height: 14rpx;
border: 3rpx solid #ffffff;
border-radius: 50%;
background: #f04444;
box-sizing: content-box;
}
::v-deep .u-modal__content {
padding: 40rpx !important;
padding: 40rpx !important;
}
</style>
+7 -4
View File
@@ -134,7 +134,8 @@
@click="onAfterSaleDetail(item)">
售后详情
</view>
<view class="operate_comm operate_but_1 operate_comm_evaluate" v-if="item.status ==4 && !item.isReview " @click="evaluate(item)">评价</view>
<view class="operate_comm operate_but_1 operate_comm_evaluate" v-if="item.status == 4 && !item.isReview"
@click="evaluate(item)">评价</view>
<!-- <view class="operate_comm operate_but_1" v-if="item.status === ORDER_TYPE.AFTER_SALE">查看售后</view> -->
<view class="operate_comm operate_but_1 operate_comm_delete" v-if="
[
@@ -421,7 +422,7 @@ export default {
},
onKefu() {
uni.navigateTo({
url: "/pages/rwa_package/setting/kefu?type=5261",
url: "/pages/rwa_package/setting/kefu?scene=order",
});
},
// 售后
@@ -484,10 +485,10 @@ export default {
},
// 评价
evaluate(item) {
console.log('订单id',item);
console.log('订单id', item);
// return
uni.navigateTo({
url:"/pages/order_package/evaluate/evaluate?id=" + item.id+"&title="+item.items[0].title+"&mainGraph="+item.items[0].mainGraph+"&goodsSpece="+item.items[0].goodsSpece+"&goodsId="+item.items[0].goodsId,
url: "/pages/order_package/evaluate/evaluate?id=" + item.id + "&title=" + item.items[0].title + "&mainGraph=" + item.items[0].mainGraph + "&goodsSpece=" + item.items[0].goodsSpece + "&goodsId=" + item.items[0].goodsId,
});
},
async againDeleteOrder() {
@@ -1177,10 +1178,12 @@ export default {
color: #f24a00;
border: 1rpx solid #f24a00;
}
.operate_comm_evaluate {
color: #333333;
border: 1rpx solid #8b8b8b;
}
.sure_pay {
::v-deep(.u-modal__content__text) {
text-align: center;
+138 -53
View File
@@ -47,7 +47,8 @@
}}</text>
<text class="price_o">¥{{ orderInfo.adPrice }}</text>
</view>
<view class="text-24" style="color: #ff5a5a; height: 38rpx; display: flex; align-items: center; gap: 5rpx" v-if="orderInfo?.goodsTagList">
<view class="text-24" style="color: #ff5a5a; height: 38rpx; display: flex; align-items: center; gap: 5rpx"
v-if="orderInfo?.goodsTagList">
<image v-for="val in orderInfo.goodsTagList" :src="val.icon" mode="heightFix" style="height: 38rpx">
</image>
</view>
@@ -232,13 +233,13 @@
<view v-for="(item, index) in specificationList" :key="item.id" class="list_warp">
<view class="list_title_warp">{{ item.type }}</view>
<view class="specification_item_warp">
<view :class="oItem.inventory === 0
<view :class="oItem.disabled || oItem.inventory === 0
? 'item_Warp item_Warp_no_warp'
: item.selectVal === oItem.n
? 'item_Warp item_Warp_yes_warp'
: 'item_Warp'
" v-for="(oItem, oIndex) in item.items" :key="oItem.n"
@click="clickSpecification(oIndex, item, oItem)">
" v-for="(oItem, oIndex) in item.items" :key="oItem.id || oItem.n"
@click="clickSpecification(index, item, oItem)">
{{ oItem.n }}
</view>
</view>
@@ -338,7 +339,7 @@
</template>
<script>
import { getGoodsDetail, addGoods, getGoodsSpecs } from "@/api/product.js";
import { getGoodsDetail, addGoods, getGoodsSpecs, getGoodsSpecsInfo } from "@/api/product.js";
import { getValue, PRODUCT_DELIVERY_TIME, ACTIVITY_TYPE } from "@/utils/enumUtils.js";
import { copyData } from "@/utils/index.js";
import Evaluation from "@/pages/other_package/productInfo/evaluation.vue";
@@ -395,6 +396,8 @@ export default {
selectSpecification: [], // 选择的规格
specificationTempl: [], // 规格模版数据
selectSpecificationTempl: {}, // 选择规格模版数据
selectedSpecValueIds: [],
skuBySpecKey: {},
serviceShow: false, // 联系客服弹框
openShow: false,
source: null, // 来源:供应链,自营
@@ -518,8 +521,17 @@ export default {
if (this.isShareUrl) {
return;
}
uni.setStorageSync("kefu-pending-context", {
type: "product",
data: {
goodsId: this.id,
goodsName: this.orderInfo.name,
goodsMainGraph: this.orderInfo.mainGraph,
currentPrice: this.orderInfo.adPrice,
},
});
uni.navigateTo({
url: "/pages/rwa_package/setting/kefu?type=5260",
url: `/pages/rwa_package/setting/kefu?scene=product&goodsId=${this.id}`,
});
},
getValue,
@@ -858,7 +870,6 @@ export default {
const resp = await getGoodsDetail(params);
if (resp && resp.bizcode === 100) {
const data = resp.data;
// 限时秒杀 和 积分兑换 不需要购物车
this.isShowCart = this.activedType === 'seckill' || data.type == 3 ? 1 : 0;
// 商品图
@@ -939,9 +950,11 @@ export default {
}
// 需要查询一下最新的规格
if (val) {
await this.querySpecification();
const data = this.orderInfo;
this.getSpecification(data.specs);
const hasStructuredSpecs = await this.querySpecification();
if (!hasStructuredSpecs) {
uni.showToast({ title: "SKU规格加载失败,请稍后重试", icon: "none" });
return;
}
} else {
this.specificationList = []; //规格
this.selectSpecification = []; // 选择的规格
@@ -950,66 +963,138 @@ export default {
this.specificationShow = val;
this.specificationShowType = type;
},
// 拆解规格
getSpecification(specs) {
const optionArr = [];
if (specs) {
const arr = JSON.parse(specs);
arr.map((item) => {
optionArr.push({
type: item.type,
items: item.items,
selectVal: null,
});
});
}
this.specificationList = optionArr;
},
// 获取规格模版数据
async querySpecification() {
const params = {
id: this.id,
};
if (this.isCoupon) {
params.type = -1;
let infoResp = null;
try {
infoResp = await getGoodsSpecsInfo({ id: this.id });
} catch (e) {
return false;
}
if (this.activedType == "seckill") {
params["activityType"] = this.activedType; // 活动类型
params["activityId"] = this.activityId;// 秒杀活动ID
if (!infoResp || infoResp.bizcode !== 100 || !infoResp.data) {
return false;
}
const resp = await getGoodsSpecs(params);
if (resp && resp.bizcode === 100) {
const data = resp.data;
let stock = 0;
data.map((item) => {
stock = stock + item.stock;
const groups = infoResp.data.specGroups || [];
const skus = infoResp.data.skus || [];
if (!groups.length || !skus.length) {
return false;
}
const skuBySpecKey = {};
skus.forEach((sku) => {
const valueIds = this.getSkuValueIds(sku, groups);
if (valueIds.length !== groups.length) {
return;
}
const normalizedSku = Object.assign({}, sku, {
id: sku.id,
valueIds,
minBuyNum: sku.minBuyNum || 1,
combTypes: this.formatSkuSelection(groups, valueIds),
});
this.specificationTempl = data;
this.orderInfo.inventory = stock; // 库存
skuBySpecKey[valueIds.join("_")] = normalizedSku;
});
if (!Object.keys(skuBySpecKey).length) {
return false;
}
this.skuBySpecKey = skuBySpecKey;
this.specificationTempl = Object.keys(skuBySpecKey).map((key) => skuBySpecKey[key]);
this.orderInfo.inventory = this.specificationTempl.reduce(
(total, sku) => total + Number(sku.stock || 0),
0
);
this.selectedSpecValueIds = groups.map(() => null);
this.selectSpecification = groups.map(() => null);
this.selectSpecificationTempl = {};
this.specificationList = groups.map((group) => ({
id: group.id,
type: group.name,
selectVal: null,
items: (group.values || []).map((value) => ({
id: value.id,
groupId: group.id,
n: value.name,
image: value.image,
disabled: false,
})),
}));
this.refreshSpecOptionState();
return true;
},
getSkuValueIds(sku, groups) {
if (sku.specValueIds) {
try {
const ids = JSON.parse(sku.specValueIds);
if (Array.isArray(ids)) {
return ids.map((id) => Number(id));
}
} catch (e) { }
}
let path = {};
try {
path = JSON.parse(sku.specValuePath || "{}") || {};
} catch (e) { }
const legacyValues = String(path.legacyName || "")
.split(/\s*(?:\/|♧)\s*/)
.filter(Boolean);
return groups.map((group, index) => {
const selectedName = path[group.name] || legacyValues[index];
const value = (group.values || []).find((item) => item.name === selectedName);
return value ? Number(value.id) : null;
}).filter((id) => id !== null);
},
formatSkuSelection(groups, valueIds) {
return groups.map((group, index) => {
const value = (group.values || []).find((item) => Number(item.id) === Number(valueIds[index]));
return value ? `${group.name}:${value.name}` : "";
}).filter(Boolean).join(";");
},
refreshSpecOptionState() {
this.specificationList.forEach((group, groupIndex) => {
group.items.forEach((value) => {
const candidate = this.selectedSpecValueIds.slice();
candidate[groupIndex] = Number(value.id);
value.disabled = !Object.keys(this.skuBySpecKey).some((key) => {
const sku = this.skuBySpecKey[key];
const skuIds = sku.valueIds || [];
return candidate.every((selectedId, index) =>
selectedId == null || Number(skuIds[index]) === Number(selectedId)
) && Number(sku.stock || 0) > 0;
});
});
});
},
//改变规格的样式
clickSpecification(pIndex, item, oItem) {
clickSpecification(groupIndex, item, oItem) {
console.log(
"改变规格的样式",
pIndex,
groupIndex,
item,
oItem,
this.specificationTempl,
this.specificationList
);
const currentSelectSpecification = this.specificationTempl[pIndex];
if (oItem.disabled) {
return;
}
this.selectedSpecValueIds.splice(groupIndex, 1, Number(oItem.id));
this.selectSpecification.splice(groupIndex, 1, oItem.n);
this.specificationList[groupIndex].selectVal = oItem.n;
this.refreshSpecOptionState();
console.log("当前规格", currentSelectSpecification);
this.orderInfo.quantity = currentSelectSpecification?.minBuyNum || 1;
this.specificationList[0].selectVal = oItem.n;
this.selectSpecificationTempl = currentSelectSpecification;
this.orderInfo.inventory = currentSelectSpecification.stock; // 库存
// 计算规格的价格
// this.selectSpecificationFun(pIndex, oItem);
if (this.selectedSpecValueIds.every((id) => id != null)) {
const currentSelectSpecification = this.skuBySpecKey[this.selectedSpecValueIds.join("_")] || {};
this.selectSpecificationTempl = currentSelectSpecification;
this.orderInfo.quantity = currentSelectSpecification.minBuyNum || 1;
this.orderInfo.inventory = currentSelectSpecification.stock || 0;
} else {
this.selectSpecificationTempl = {};
}
},
// 选择规格
selectSpecificationFun(index, item) {
+113 -74
View File
@@ -1,87 +1,126 @@
<template>
<view class="content">
<TopSafe></TopSafe>
<view class="setting-view" v-if="h5Url">
<iframe :src="h5Url" frameborder="no" border="0" class="web-view">
</iframe>
</view>
<!-- 腾讯电子签H5 → 集成到UniApp -->
</view>
<view class="customer-service-page">
<TopSafe />
<view class="customer-service-nav">
<button class="nav-back" aria-label="返回" @click="goBack">
<u-icon name="arrow-left" size="25" color="#2f3037" />
</button>
<text class="nav-title">全媒体智能客服</text>
<view class="nav-placeholder" />
</view>
<button class="history-entry" @click="showHistory">点击查询聊天记录</button>
<view class="customer-service-body">
<hashmall-customer-service v-if="optionsReady" ref="customerService" :entry-options="entryOptions" />
</view>
</view>
</template>
<script>
import { getUserInfo,uploadAvatar } from '@/api/auth.js';
import { uploadImage, uploadImg } from '@/api/common.js';
import { IMG_TYPE } from '@/utils/enumUtils.js';
import { BASE_URL, Authorization } from '@/utils/config.js';
import { getStorageFun, TOKEN_NAME,clear } from '@/utils/auth.js';
import Header from '@/components/common/header.vue';
import TopSafe from '@/components/common/top-safe.nvue'
import MyBtn from '@/components/common/my-btn.vue'
import func from '@/utils/func.js';
import HashmallCustomerService from '@/uni_modules/hashmall-customer-service/components/hashmall-customer-service/hashmall-customer-service.vue'
export default {
components: { Header, TopSafe,MyBtn },
data() {
return {
h5Url:'',
};
},
onShow() {
},
onLoad(option) {
if(option.type ){
this.init(option.type);
}
},
methods: {
init(type) {
this.getUserInfo(type)
},
async getUserInfo(type) {
const result = await getUserInfo();
if (result && result.bizcode === 100) {
const data = result.data;
this.currentUserData = result.data || {};
let url = `https://www.aigcyun.cn/uniChat/#/?skillGroupId=${type}&enterpriseId=1435&extraData=`;
let addHyExtraData = {
customerId:data.id,
customerName:data.nickname,
};
this.h5Url = url + encodeURIComponent(JSON.stringify(addHyExtraData))
}
},
}
components: {
TopSafe,
HashmallCustomerService
},
data() {
return {
entryOptions: {},
optionsReady: false
}
},
onLoad(options) {
this.entryOptions = options || {}
this.optionsReady = true
},
onShow() {
this.$nextTick(() => {
if (this.$refs.customerService) this.$refs.customerService.resume()
})
},
onHide() {
if (this.$refs.customerService) this.$refs.customerService.suspend()
},
onUnload() {
if (this.$refs.customerService) this.$refs.customerService.suspend()
},
methods: {
goBack() {
uni.navigateBack()
},
showHistory() {
if (this.$refs.customerService) this.$refs.customerService.showHistory()
}
}
}
</script>
<style lang="scss" scoped>
.web-view{
height:100%;
width:100%;
position: relative;
}
.setting-view {
width: 100%;
height: calc(100% - var(--status-bar-height));
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
box-sizing: border-box;
position: relative;
<style scoped lang="scss">
.customer-service-page {
display: flex;
width: 100%;
height: 100vh;
height: 100dvh;
box-sizing: border-box;
flex-direction: column;
overflow: hidden;
background: #f4f5fa;
}
</style>
<style>
.chatModel{
height:calc(100% - var(--status-bar-height));
margin-top:var(--status-bar-height);
}
.customer-service-nav {
display: flex;
height: 92rpx;
align-items: center;
justify-content: space-between;
padding: 0 28rpx;
background: #f4f5fa;
}
.nav-back,
.nav-placeholder {
width: 72rpx;
height: 72rpx;
flex: 0 0 72rpx;
}
.nav-back {
display: flex;
align-items: center;
justify-content: flex-start;
margin: 0;
padding: 0;
background: transparent;
}
.nav-title {
color: #101116;
font-size: 38rpx;
font-weight: 500;
line-height: 1.2;
}
.history-entry {
width: 100%;
height: 72rpx;
margin: 0;
padding: 0;
background: transparent;
color: #a2a3aa;
font-size: 27rpx;
font-weight: 400;
line-height: 72rpx;
}
.nav-back::after,
.history-entry::after {
border: 0;
}
.customer-service-body {
width: 100%;
min-height: 0;
flex: 1;
overflow: hidden;
}
</style>