|
|
@@ -29,15 +29,46 @@
|
|
|
<view class="avatar-wrapper">
|
|
|
<image class="avatar" :src="message.avatar" mode="aspectFill" />
|
|
|
</view>
|
|
|
- <view class="message-content-wrapper">
|
|
|
- <view class="username" v-if="isMultiUser">{{
|
|
|
- message.username
|
|
|
- }}</view>
|
|
|
+
|
|
|
+ <view v-if="message.messageType == 'text'|| message.messageType != 'goods'" class="message-content-wrapper">
|
|
|
+ <view class="username" v-if="isMultiUser">{{ message.username }}</view>
|
|
|
<view class="message-bubble left-bubble">
|
|
|
<text class="message-text">{{ message.message }}</text>
|
|
|
</view>
|
|
|
<view class="message-time">{{ message.time }}</view>
|
|
|
</view>
|
|
|
+
|
|
|
+ <view v-if="message.messageType == 'goods'" class="message-content-wrapper">
|
|
|
+ <view class="username align-right" v-if="isMultiUser">{{
|
|
|
+ message.username
|
|
|
+ }}</view>
|
|
|
+ <view class="goods-info left-bubble">
|
|
|
+ <view class="goods-main">
|
|
|
+ <image class="goods-image" :src="message.goodsInfo.goodsImg" mode="aspectFill"></image>
|
|
|
+ <view class="goods-details">
|
|
|
+ <view class="goods-name-wrapper">
|
|
|
+ <text class="goods-name">{{message.goodsInfo.goodsName}}</text>
|
|
|
+ </view>
|
|
|
+ <!-- <view class="sales-info">
|
|
|
+ <text class="delivery-time">48小时发</text>
|
|
|
+ </view> -->
|
|
|
+ <!-- 2 是报价类型,专门为报价类型使用 -->
|
|
|
+ <text v-if="message.goodsInfo.goodsPriceType == 2" class="goods-price">¥{{ message.goodsInfo.goodsPrice }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="goods-footer">
|
|
|
+ </view>
|
|
|
+ <view v-if="message.goodsInfo.goodsPriceType == 2" class="action-buttons">
|
|
|
+ <button class="action-btn spec-btn" @click="toBuy(message.goodsInfo)">查看商品</button>
|
|
|
+ <button class="action-btn buy-btn" @click="toBuy(message.goodsInfo)">去购买</button>
|
|
|
+ </view>
|
|
|
+ <view v-else>
|
|
|
+ <button class="action-btn spec-btn" @click="toGoodsDetail(message.goodsInfo)">查看商品</button>
|
|
|
+ <text>报价 {{ message.goodsInfo.goodsInfo.price }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="message-time align-right">{{ message.time }}</view>
|
|
|
+ </view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 右侧消息(我的) -->
|
|
|
@@ -77,8 +108,10 @@
|
|
|
<text class="arrow">></text> -->
|
|
|
</view>
|
|
|
<view class="action-buttons">
|
|
|
- <button class="action-btn spec-btn" @click="toGoodsDetail(message.goodsInfo)">查看商品</button>
|
|
|
- <button v-if="message.goodsInfo.goodsPriceType == 1" class="action-btn buy-btn" click="toBuy">去购买</button>
|
|
|
+ <button class="action-btn spec-btn" @click="toBuy(message.goodsInfo)">查看商品</button>
|
|
|
+ <button v-if="message.goodsInfo.goodsPriceType == 1" class="action-btn buy-btn" @click="toGoodsDetail(message.goodsInfo)">去购买</button>
|
|
|
+ <!-- 报价类型显示去购买 -->
|
|
|
+ <button v-if="message.goodsInfo.goodsPriceType == 2" class="action-btn buy-btn" @click="toBuy(message.goodsInfo)">去购买</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
<view class="message-time align-right">{{ message.time }}</view>
|
|
|
@@ -96,12 +129,12 @@
|
|
|
</scroll-view>
|
|
|
|
|
|
<!-- 商品信息展示 -->
|
|
|
- <view class="goods-info-container" v-if="globalGoodsInfo.goodsId">
|
|
|
+ <view class="goods-info-container" v-if="currentGoodsInfo.goodsId">
|
|
|
<view class="goods-info-card">
|
|
|
- <image class="goods-image" :src="globalGoodsInfo.goodsImg" mode="aspectFill" />
|
|
|
+ <image class="goods-image" :src="currentGoodsInfo.goodsImg" mode="aspectFill" />
|
|
|
<view class="goods-details">
|
|
|
- <text class="goods-name">{{ globalGoodsInfo.goodsName }}</text>
|
|
|
- <text class="goods-price">¥{{ globalGoodsInfo.goodsPrice }}</text>
|
|
|
+ <text class="goods-name">{{ currentGoodsInfo.goodsName }}</text>
|
|
|
+ <text v-if="currentGoodsInfo.goodsPriceType == 1" class="goods-price">¥{{ currentGoodsInfo.goodsPrice }}</text>
|
|
|
</view>
|
|
|
<view class="goods-actions">
|
|
|
<view class="close-icon" @click="closeGoodsCard">
|
|
|
@@ -151,7 +184,7 @@
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { getChatHistory } from "@/api/chat";
|
|
|
+import { getChatHistory, getQuotedPrice } from "@/api/chat";
|
|
|
import {mapGetters } from "vuex";
|
|
|
import { WSSHOST } from '@/config.js';
|
|
|
export default {
|
|
|
@@ -192,14 +225,14 @@ export default {
|
|
|
// 键盘相关
|
|
|
keyboardHeight: 0, // 键盘高度
|
|
|
isKeyboardShow: false, // 键盘是否显示
|
|
|
- globalGoodsInfo: {}, // 商品信息
|
|
|
+ currentGoodsInfo: {}, // 进入聊天页面时,带过来的商品信息
|
|
|
};
|
|
|
},
|
|
|
onLoad(options) {
|
|
|
console.log("---------- options: ", options);
|
|
|
|
|
|
if (options.goodsId) {
|
|
|
- this.globalGoodsInfo = {
|
|
|
+ this.currentGoodsInfo = {
|
|
|
...options
|
|
|
};
|
|
|
}
|
|
|
@@ -280,13 +313,13 @@ export default {
|
|
|
},
|
|
|
onHide() {
|
|
|
// 页面隐藏时可以选择性地关闭连接(根据业务需求)
|
|
|
- // this.disconnectWebSocket()
|
|
|
+ this.disconnectWebSocket()
|
|
|
},
|
|
|
onShow() {
|
|
|
// 页面显示时重新连接(如果之前断开了)
|
|
|
- // if (!this.isConnected && this.room) {
|
|
|
- // this.connectWebSocket();
|
|
|
- // }
|
|
|
+ if (!this.isConnected && this.room) {
|
|
|
+ this.connectWebSocket();
|
|
|
+ }
|
|
|
},
|
|
|
methods: {
|
|
|
init() {},
|
|
|
@@ -310,7 +343,7 @@ export default {
|
|
|
|
|
|
//如果消息类型为 'goods',就调用 parseGoodsMessage 并将结果存入新属性 goodsInfo
|
|
|
if (newItem.messageType === 'goods') {
|
|
|
- newItem.goodsInfo = this.parseGoodsMessage(newItem.message);
|
|
|
+ newItem.goodsInfo = this.parseGoodsMessage(newItem.message, newItem.timestamp);
|
|
|
}
|
|
|
return newItem;
|
|
|
});
|
|
|
@@ -759,11 +792,12 @@ export default {
|
|
|
isMine: false, // 既然不是自己的消息,就标记为false
|
|
|
username: messageData.name || messageData.username || "未知用户",
|
|
|
avatar: messageData.avatar || this.$constant.imgUrl + "/retail/default-img.png",
|
|
|
+ timestamp: messageData.timestamp,
|
|
|
messageType: messageType,
|
|
|
};
|
|
|
|
|
|
if (newMessage.messageType === 'goods') {
|
|
|
- newMessage.goodsInfo = this.parseGoodsMessage(newMessage.message);
|
|
|
+ newMessage.goodsInfo = this.parseGoodsMessage(messageData.message, messageData.timestamp);
|
|
|
}
|
|
|
|
|
|
console.log("收到 ---------- newMessage: ", newMessage);
|
|
|
@@ -809,6 +843,7 @@ export default {
|
|
|
username: this.currentUser.username,
|
|
|
avatar: avatar,
|
|
|
time: this.getCurrentTime(),
|
|
|
+ timestamp: timestamp,
|
|
|
messageType: "text",
|
|
|
};
|
|
|
|
|
|
@@ -881,8 +916,13 @@ export default {
|
|
|
},
|
|
|
|
|
|
// 获取当前时间
|
|
|
- getCurrentTime() {
|
|
|
- const now = new Date();
|
|
|
+ getCurrentTime(timestamp=null) {
|
|
|
+ let now;
|
|
|
+ if (timestamp) {
|
|
|
+ now = new Date(timestamp * 1000);
|
|
|
+ } else {
|
|
|
+ now = new Date();
|
|
|
+ }
|
|
|
const hours = String(now.getHours()).padStart(2, "0");
|
|
|
const minutes = String(now.getMinutes()).padStart(2, "0");
|
|
|
return `${hours}:${minutes}`;
|
|
|
@@ -915,7 +955,7 @@ export default {
|
|
|
},
|
|
|
// 关闭商品信息卡片
|
|
|
closeGoodsCard() {
|
|
|
- this.globalGoodsInfo = {};
|
|
|
+ this.currentGoodsInfo = {};
|
|
|
},
|
|
|
|
|
|
// 发送商品信息 -- sendMessageOfGoodsInfo
|
|
|
@@ -927,15 +967,20 @@ export default {
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
+ const avatar = this.currentUser.avatar || this.$constant.imgUrl + "/retail/default-img.png";
|
|
|
+ const timestamp = Math.floor(Date.now() / 1000);
|
|
|
+ // 生成一个唯一的字符串,结合时间戳和随机数
|
|
|
+ // const randomString = timestamp.toString() + "_" + Math.random().toString(36).substring(2, 10);
|
|
|
+ // console.log("---------- randomString: ", randomString);
|
|
|
+ const keyValue = this.currentGoodsInfo.shopId + "-" + this.currentGoodsInfo.userId + "-" + this.currentGoodsInfo.goodsId;
|
|
|
|
|
|
// 构建商品信息消息体 -------------------------------- 所看到的消息体 -------------------------------
|
|
|
const goodsMessage = {
|
|
|
- ...this.globalGoodsInfo,
|
|
|
+ ...this.currentGoodsInfo,
|
|
|
+ key: keyValue,
|
|
|
};
|
|
|
|
|
|
const messageContent = JSON.stringify(goodsMessage); // 所看到的消息体
|
|
|
- const avatar = this.currentUser.avatar || this.$constant.imgUrl + "/retail/default-img.png";
|
|
|
- const timestamp = Math.floor(Date.now() / 1000);
|
|
|
|
|
|
try {
|
|
|
// 立即在本地显示发送的消息(右侧)
|
|
|
@@ -943,10 +988,10 @@ export default {
|
|
|
id: "msg_local_" + timestamp + "_" + (Math.floor(Math.random() * 100000) + 1),
|
|
|
isMine: true,
|
|
|
// message: `[商品] ${this.goodsInfo.goodsName}`, // 显示简化信息
|
|
|
- message: messageContent, // 显示简化信息
|
|
|
+ message: messageContent,
|
|
|
username: this.currentUser.username,
|
|
|
avatar: avatar,
|
|
|
- time: this.getCurrentTime(),
|
|
|
+ time: this.getCurrentTime(timestamp),
|
|
|
// 可以在这里附加一个字段,用于渲染时区分商品消息
|
|
|
messageType: "goods",
|
|
|
goodsInfo: goodsMessage,
|
|
|
@@ -1000,7 +1045,7 @@ export default {
|
|
|
},
|
|
|
|
|
|
// 解析商品消息
|
|
|
- parseGoodsMessage(messageStr) {
|
|
|
+ parseGoodsMessage(messageStr, timestamp) {
|
|
|
try {
|
|
|
// 首先检查 messageStr 是否已经是对象
|
|
|
if (typeof messageStr === "object" && messageStr !== null) {
|
|
|
@@ -1009,6 +1054,52 @@ export default {
|
|
|
// 如果是字符串,则尝试解析
|
|
|
if (typeof messageStr === "string") {
|
|
|
const parsed = JSON.parse(messageStr);
|
|
|
+ if(parsed.goodsPriceType == 0) {
|
|
|
+ const key = parsed.key || '';
|
|
|
+ if (key == '') {
|
|
|
+ // console.log("key == ''", key)
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+ const keyArr = key.split('-');// shopId-userId-goodsId
|
|
|
+ if (keyArr.length != 3) {
|
|
|
+ //console.log("keyArr.length != 3", keyArr)
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+ const shopId = parseInt(keyArr[0]);
|
|
|
+ const userId = parseInt(keyArr[1]);
|
|
|
+ const goodsId = parseInt(keyArr[2]);
|
|
|
+ if (isNaN(shopId) || isNaN(userId) || isNaN(goodsId)) {
|
|
|
+ //console.log("isNaN(shopId) || isNaN(userId) || isNaN(goodsId)", shopId, userId, goodsId)
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isNaN(timestamp)) {
|
|
|
+ //console.log("isNaN(timestamp)", timestamp)
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+
|
|
|
+ const now = Math.floor(Date.now()/1000);
|
|
|
+ const timeDiff = now - timestamp;
|
|
|
+ const expireTime = 24 * 60 * 60;
|
|
|
+ if (timeDiff > expireTime) {
|
|
|
+ //console.log("timeDiff > expireTime", timeDiff, expireTime)
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+
|
|
|
+ getQuotedPrice({
|
|
|
+ key: key,
|
|
|
+ }).then(res => {
|
|
|
+ //console.log("res", res)
|
|
|
+ if (res.code == 1) {
|
|
|
+ if (res.data.price != -1) {
|
|
|
+ parsed.goodsPriceType = 2; // 报价类型
|
|
|
+ parsed.goodsPrice = res.data.price;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }).catch(err => {
|
|
|
+ console.log("getQuotedPrice error: ", key, err)
|
|
|
+ })
|
|
|
+ }
|
|
|
return parsed;
|
|
|
}
|
|
|
// 如果两者都不是,返回空对象
|
|
|
@@ -1025,6 +1116,7 @@ export default {
|
|
|
},
|
|
|
// 查看商品
|
|
|
toGoodsDetail(goodsInfo) {
|
|
|
+ this.disconnectWebSocket();
|
|
|
console.log("---------- goodsInfo: ", goodsInfo);
|
|
|
const goodsId = parseInt(goodsInfo.goodsId);
|
|
|
const categoryId = parseInt(goodsInfo.categoryId);
|
|
|
@@ -1048,6 +1140,37 @@ export default {
|
|
|
},
|
|
|
});
|
|
|
},
|
|
|
+ // 去购买
|
|
|
+ toBuy(goodsInfo) {
|
|
|
+ this.disconnectWebSocket();
|
|
|
+ console.log("---------- goodsInfo: ", goodsInfo);
|
|
|
+ const goodsId = parseInt(goodsInfo.goodsId);
|
|
|
+ const categoryId = parseInt(goodsInfo.categoryId);
|
|
|
+ const hdId = parseInt(goodsInfo.hdId);
|
|
|
+ const account = parseInt(goodsInfo.account);
|
|
|
+ const price = parseFloat(goodsInfo.goodsPrice);
|
|
|
+ const priceType = goodsInfo.goodsPriceType;
|
|
|
+ if (isNaN(goodsId) || isNaN(categoryId) || isNaN(hdId) || isNaN(account) || isNaN(price)) {
|
|
|
+ this.$msg("商品数据异常");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$util.pageTo({
|
|
|
+ url: "/pages/goods/detail",
|
|
|
+ query: {
|
|
|
+ id: goodsId,
|
|
|
+ account: goodsInfo.account,
|
|
|
+ hdId: goodsInfo.hdId,
|
|
|
+ categoryId: categoryId,
|
|
|
+ shopId: goodsInfo.shopId,
|
|
|
+ name: goodsInfo.name,
|
|
|
+ avatar: goodsInfo.avatar,
|
|
|
+ price: price,
|
|
|
+ priceType: priceType,
|
|
|
+ key: goodsInfo.key,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
@@ -1330,7 +1453,7 @@ export default {
|
|
|
font-size: 36rpx;
|
|
|
color: #ff5050;
|
|
|
font-weight: bold;
|
|
|
- margin-top: 10rpx;
|
|
|
+ margin-top: 12rpx;
|
|
|
}
|
|
|
}
|
|
|
|