|
|
@@ -2,9 +2,7 @@
|
|
|
<view class="chat-container">
|
|
|
<!-- 连接状态指示器 -->
|
|
|
<view class="connection-status" v-if="!isConnected">
|
|
|
- <text class="status-text">{{
|
|
|
- reconnectCount > 0 ? "重连中..." : "连接中..."
|
|
|
- }}</text>
|
|
|
+ <text class="status-text">"连接中...</text>
|
|
|
</view>
|
|
|
|
|
|
<!-- 聊天消息列表 -->
|
|
|
@@ -44,7 +42,7 @@
|
|
|
|
|
|
<!-- 右侧消息(我的) -->
|
|
|
<view class="message-right" v-if="message.isMine || message.customId">
|
|
|
- <view class="message-content-wrapper">
|
|
|
+ <view v-if="message.messageType == 'text'|| message.messageType != 'goods'" class="message-content-wrapper">
|
|
|
<view class="username align-right" v-if="isMultiUser">{{
|
|
|
message.username
|
|
|
}}</view>
|
|
|
@@ -53,6 +51,38 @@
|
|
|
</view>
|
|
|
<view class="message-time align-right">{{ 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 right-bubble">
|
|
|
+ <view class="goods-main">
|
|
|
+ <image
|
|
|
+ class="goods-image"
|
|
|
+ :src="message.goodsInfo.goodsImg"
|
|
|
+ mode="aspectFill"
|
|
|
+ />
|
|
|
+ <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>
|
|
|
+ <text v-if="message.goodsInfo.goodsPriceType == 1" class="goods-price">¥{{ message.goodsInfo.goodsPrice }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="goods-footer">
|
|
|
+ <!-- <text class="footer-text">破损包退·极速退款·7天无理由退货</text>
|
|
|
+ <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>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="message-time align-right">{{ message.time }}</view>
|
|
|
+ </view>
|
|
|
<view class="avatar-wrapper">
|
|
|
<image class="avatar" :src="message.avatar" mode="aspectFill" />
|
|
|
</view>
|
|
|
@@ -65,8 +95,32 @@
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
|
|
|
+ <!-- 商品信息展示 -->
|
|
|
+ <view class="goods-info-container" v-if="globalGoodsInfo.goodsId">
|
|
|
+ <view class="goods-info-card">
|
|
|
+ <image class="goods-image" :src="globalGoodsInfo.goodsImg" mode="aspectFill" />
|
|
|
+ <view class="goods-details">
|
|
|
+ <text class="goods-name">{{ globalGoodsInfo.goodsName }}</text>
|
|
|
+ <text class="goods-price">¥{{ globalGoodsInfo.goodsPrice }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="goods-actions">
|
|
|
+ <view class="close-icon" @click="closeGoodsCard">
|
|
|
+ <zui-svg-icon icon="general-close" color="#666666" :width="18" :height="18" />
|
|
|
+ </view>
|
|
|
+ <button class="send-goods-button" @click="sendMessageOfGoodsInfo">
|
|
|
+ 发送商品
|
|
|
+ </button>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
<!-- 输入区域 -->
|
|
|
- <view class="input-area" :style="{ 'padding-bottom': keyboardHeight > 0 ? keyboardHeight + 'px' : 'calc(20upx + env(safe-area-inset-bottom))' }">
|
|
|
+ <view
|
|
|
+ class="input-area"
|
|
|
+ :style="{
|
|
|
+ 'padding-bottom': keyboardHeight > 0 ? keyboardHeight + 'px' : 'calc(20upx + env(safe-area-inset-bottom))',
|
|
|
+ }"
|
|
|
+ >
|
|
|
<view class="input-wrapper">
|
|
|
<textarea
|
|
|
class="message-input"
|
|
|
@@ -124,10 +178,10 @@ export default {
|
|
|
messageList: [],
|
|
|
// WebSocket相关
|
|
|
socket: null,
|
|
|
- isConnected: false,
|
|
|
+ isConnected: true,
|
|
|
room: '',
|
|
|
reconnectCount: 0,
|
|
|
- maxReconnectCount: 3,
|
|
|
+ maxReconnectCount: 5,
|
|
|
isManualDisconnect: false, // 添加标志位:是否主动断开连接
|
|
|
scrollWithAnimation: false, // 控制滚动动画
|
|
|
// 分页加载
|
|
|
@@ -138,10 +192,25 @@ export default {
|
|
|
// 键盘相关
|
|
|
keyboardHeight: 0, // 键盘高度
|
|
|
isKeyboardShow: false, // 键盘是否显示
|
|
|
+ globalGoodsInfo: {}, // 商品信息
|
|
|
};
|
|
|
},
|
|
|
onLoad(options) {
|
|
|
console.log("---------- options: ", options);
|
|
|
+
|
|
|
+ if (options.goodsId) {
|
|
|
+ this.globalGoodsInfo = {
|
|
|
+ ...options
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ if (options.chatPerson) {
|
|
|
+ // 设置聊天人名称
|
|
|
+ uni.setNavigationBarTitle({
|
|
|
+ title: options.chatPerson
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
// 根据传入参数判断是否多人聊天
|
|
|
if (options.isMultiUser) {
|
|
|
this.isMultiUser = options.isMultiUser === "true";
|
|
|
@@ -233,10 +302,18 @@ export default {
|
|
|
userId: userId,
|
|
|
}).then((res) => {
|
|
|
if (res.code == 1 && res.data) {
|
|
|
- this.allMessages = res.data.map((item, index) => ({
|
|
|
- ...item,
|
|
|
- id: `msg_${index}`, // 为每条消息添加唯一ID
|
|
|
- }));
|
|
|
+ this.allMessages = res.data.map((item, index) => {
|
|
|
+ const newItem = {
|
|
|
+ ...item,
|
|
|
+ id: `message-${index}`, // 为每条消息添加唯一ID
|
|
|
+ };
|
|
|
+
|
|
|
+ //如果消息类型为 'goods',就调用 parseGoodsMessage 并将结果存入新属性 goodsInfo
|
|
|
+ if (newItem.messageType === 'goods') {
|
|
|
+ newItem.goodsInfo = this.parseGoodsMessage(newItem.message);
|
|
|
+ }
|
|
|
+ return newItem;
|
|
|
+ });
|
|
|
|
|
|
if (this.allMessages.length > this.initialLoadSize) {
|
|
|
// 如果消息总数超过首次加载数量,则只显示最新的50条
|
|
|
@@ -326,7 +403,6 @@ export default {
|
|
|
try {
|
|
|
// 构建WebSocket URL(移除查询参数)
|
|
|
const wsUrl = `${this.getHost()}/room`;
|
|
|
- console.log("---------- wsUrl: ", wsUrl);
|
|
|
|
|
|
// 构建子协议参数(模拟POST body传输)
|
|
|
const protocols = this.buildWebSocketProtocols();
|
|
|
@@ -336,7 +412,7 @@ export default {
|
|
|
url: wsUrl,
|
|
|
protocols: protocols,
|
|
|
success: () => {
|
|
|
- console.log("WebSocket连接创建成功");
|
|
|
+ console.log("创建WebSocket连接...");
|
|
|
},
|
|
|
fail: (error) => {
|
|
|
console.error("WebSocket连接创建失败:", error);
|
|
|
@@ -346,7 +422,7 @@ export default {
|
|
|
|
|
|
// 监听WebSocket连接打开
|
|
|
this.socket.onOpen(() => {
|
|
|
- console.log("WebSocket连接已打开");
|
|
|
+ console.log("成功连接 WebSocket");
|
|
|
this.isConnected = true;
|
|
|
this.reconnectCount = 0;
|
|
|
this.isManualDisconnect = false; // 连接成功时重置标志位
|
|
|
@@ -360,17 +436,18 @@ export default {
|
|
|
// 监听WebSocket连接关闭
|
|
|
this.socket.onClose(() => {
|
|
|
console.log("WebSocket连接已关闭");
|
|
|
- this.isConnected = false;
|
|
|
|
|
|
// 只有在非主动断开的情况下才尝试重连
|
|
|
- if (
|
|
|
- !this.isManualDisconnect &&
|
|
|
- this.reconnectCount < this.maxReconnectCount
|
|
|
- ) {
|
|
|
+ if (!this.isManualDisconnect && this.reconnectCount < this.maxReconnectCount) {
|
|
|
setTimeout(() => {
|
|
|
this.reconnectWebSocket();
|
|
|
}, 3000);
|
|
|
}
|
|
|
+
|
|
|
+ // 尝试重新连接超过 maxReconnectCount 次才标记为未连接状态
|
|
|
+ if (!this.isManualDisconnect && this.reconnectCount >= this.maxReconnectCount) {
|
|
|
+ this.isConnected = false;
|
|
|
+ }
|
|
|
});
|
|
|
|
|
|
// 监听WebSocket错误
|
|
|
@@ -504,7 +581,7 @@ export default {
|
|
|
}
|
|
|
// #endif
|
|
|
|
|
|
- // #ifdef MP-WEIXIN || MP-ALIPAY || APP-PLUS
|
|
|
+ // #ifndef H5
|
|
|
// 小程序和App环境使用自定义编码
|
|
|
return this.customBase64Encode(str);
|
|
|
// #endif
|
|
|
@@ -671,6 +748,9 @@ export default {
|
|
|
// return;
|
|
|
// }
|
|
|
|
|
|
+ const messageType = messageData.messageType || "text";
|
|
|
+ console.log('messageType -- ', messageType);
|
|
|
+
|
|
|
// 创建消息对象
|
|
|
const newMessage = {
|
|
|
id: "msg_receive_" + Math.floor(Date.now() / 1000) + "_" + Math.floor(Math.random() * 100000) + 1,
|
|
|
@@ -678,19 +758,25 @@ export default {
|
|
|
time: this.getCurrentTime(),
|
|
|
isMine: false, // 既然不是自己的消息,就标记为false
|
|
|
username: messageData.name || messageData.username || "未知用户",
|
|
|
- avatar:
|
|
|
- messageData.avatar ||
|
|
|
- this.$constant.imgUrl + "/retail/default-img.png",
|
|
|
+ avatar: messageData.avatar || this.$constant.imgUrl + "/retail/default-img.png",
|
|
|
+ messageType: messageType,
|
|
|
};
|
|
|
|
|
|
+ if (newMessage.messageType === 'goods') {
|
|
|
+ newMessage.goodsInfo = this.parseGoodsMessage(newMessage.message);
|
|
|
+ }
|
|
|
+
|
|
|
console.log("收到 ---------- newMessage: ", newMessage);
|
|
|
// 添加到消息列表
|
|
|
this.messageList.push(newMessage);
|
|
|
|
|
|
// 滚动到底部
|
|
|
- this.$nextTick(() => {
|
|
|
+ // this.$nextTick(() => {
|
|
|
+ // this.scrollToBottom();
|
|
|
+ // });
|
|
|
+ setTimeout(() => {
|
|
|
this.scrollToBottom();
|
|
|
- });
|
|
|
+ }, 100);
|
|
|
} catch (error) {
|
|
|
console.error("解析WebSocket消息失败:", error, data);
|
|
|
}
|
|
|
@@ -723,6 +809,7 @@ export default {
|
|
|
username: this.currentUser.username,
|
|
|
avatar: avatar,
|
|
|
time: this.getCurrentTime(),
|
|
|
+ messageType: "text",
|
|
|
};
|
|
|
|
|
|
// 添加到消息列表
|
|
|
@@ -730,9 +817,12 @@ export default {
|
|
|
// 清空输入框
|
|
|
this.inputText = "";
|
|
|
// 滚动到底部
|
|
|
- this.$nextTick(() => {
|
|
|
- this.scrollToBottom(); // 用户操作时使用动画
|
|
|
- });
|
|
|
+ // this.$nextTick(() => {
|
|
|
+ // this.scrollToBottom(); // 用户操作时使用动画
|
|
|
+ // });
|
|
|
+ setTimeout(() => {
|
|
|
+ this.scrollToBottom();
|
|
|
+ }, 100);
|
|
|
|
|
|
// 构建要发送的消息数据
|
|
|
const messageData = {
|
|
|
@@ -741,7 +831,8 @@ export default {
|
|
|
username: this.currentUser.username,
|
|
|
avatar: avatar,
|
|
|
customId: this.currentUser.customId, // 发送者:客户使用customId (门店使用shopId)
|
|
|
- timestamp: timestamp
|
|
|
+ timestamp: timestamp,
|
|
|
+ messageType: "text"
|
|
|
};
|
|
|
|
|
|
const json = JSON.stringify(messageData);
|
|
|
@@ -803,7 +894,7 @@ export default {
|
|
|
// 微信小程序中,聚焦时延迟滚动到底部
|
|
|
setTimeout(() => {
|
|
|
this.scrollToBottom();
|
|
|
- }, 300);
|
|
|
+ }, 400);
|
|
|
},
|
|
|
|
|
|
// 输入框失焦事件
|
|
|
@@ -822,6 +913,141 @@ export default {
|
|
|
this.inputText = lines.slice(0, 5).join('\n');
|
|
|
}
|
|
|
},
|
|
|
+ // 关闭商品信息卡片
|
|
|
+ closeGoodsCard() {
|
|
|
+ this.globalGoodsInfo = {};
|
|
|
+ },
|
|
|
+
|
|
|
+ // 发送商品信息 -- sendMessageOfGoodsInfo
|
|
|
+ sendMessageOfGoodsInfo() {
|
|
|
+ if (!this.isConnected) {
|
|
|
+ uni.showToast({
|
|
|
+ title: "WebSocket未连接",
|
|
|
+ icon: "none",
|
|
|
+ });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 构建商品信息消息体 -------------------------------- 所看到的消息体 -------------------------------
|
|
|
+ const goodsMessage = {
|
|
|
+ ...this.globalGoodsInfo,
|
|
|
+ };
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ // 立即在本地显示发送的消息(右侧)
|
|
|
+ const myMessage = {
|
|
|
+ id: "msg_local_" + timestamp + "_" + (Math.floor(Math.random() * 100000) + 1),
|
|
|
+ isMine: true,
|
|
|
+ // message: `[商品] ${this.goodsInfo.goodsName}`, // 显示简化信息
|
|
|
+ message: messageContent, // 显示简化信息
|
|
|
+ username: this.currentUser.username,
|
|
|
+ avatar: avatar,
|
|
|
+ time: this.getCurrentTime(),
|
|
|
+ // 可以在这里附加一个字段,用于渲染时区分商品消息
|
|
|
+ messageType: "goods",
|
|
|
+ goodsInfo: goodsMessage,
|
|
|
+ };
|
|
|
+
|
|
|
+ // 添加到消息列表
|
|
|
+ this.messageList.push(myMessage);
|
|
|
+ // 清空商品信息并关闭卡片
|
|
|
+ this.closeGoodsCard();
|
|
|
+ // 滚动到底部
|
|
|
+ // this.$nextTick(() => {
|
|
|
+ // this.scrollToBottom();
|
|
|
+ // });
|
|
|
+ setTimeout(() => {
|
|
|
+ this.scrollToBottom();
|
|
|
+ }, 100);
|
|
|
+
|
|
|
+ // 构建要发送的完整消息数据
|
|
|
+ const messageData = {
|
|
|
+ receiver: this.currentUser.shopId,
|
|
|
+ message: messageContent,
|
|
|
+ username: this.currentUser.username,
|
|
|
+ avatar: avatar,
|
|
|
+ customId: this.currentUser.customId,
|
|
|
+ timestamp: timestamp,
|
|
|
+ messageType: "goods",
|
|
|
+ };
|
|
|
+
|
|
|
+ const json = JSON.stringify(messageData);
|
|
|
+ // 发送到WebSocket服务器
|
|
|
+ this.socket.send({
|
|
|
+ data: json,
|
|
|
+ success: () => {
|
|
|
+ console.log("商品消息发送成功");
|
|
|
+ },
|
|
|
+ fail: (error) => {
|
|
|
+ console.error("商品消息发送失败:", error);
|
|
|
+ uni.showToast({
|
|
|
+ title: "发送失败",
|
|
|
+ icon: "none",
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+ } catch (error) {
|
|
|
+ console.error("发送商品消息异常:", error);
|
|
|
+ uni.showToast({
|
|
|
+ title: "发送异常",
|
|
|
+ icon: "none",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 解析商品消息
|
|
|
+ parseGoodsMessage(messageStr) {
|
|
|
+ try {
|
|
|
+ // 首先检查 messageStr 是否已经是对象
|
|
|
+ if (typeof messageStr === "object" && messageStr !== null) {
|
|
|
+ return messageStr;
|
|
|
+ }
|
|
|
+ // 如果是字符串,则尝试解析
|
|
|
+ if (typeof messageStr === "string") {
|
|
|
+ const parsed = JSON.parse(messageStr);
|
|
|
+ return parsed;
|
|
|
+ }
|
|
|
+ // 如果两者都不是,返回空对象
|
|
|
+ return {};
|
|
|
+ } catch (error) {
|
|
|
+ // console.error("解析商品消息失败:", error, messageStr);
|
|
|
+ // 在解析失败时返回一个包含默认值的对象,以避免模板渲染错误
|
|
|
+ return {
|
|
|
+ goodsImg: "",
|
|
|
+ goodsName: "商品信息解析失败",
|
|
|
+ goodsPrice: "0.00",
|
|
|
+ };
|
|
|
+ }
|
|
|
+ },
|
|
|
+ // 查看商品
|
|
|
+ toGoodsDetail(goodsInfo) {
|
|
|
+ console.log("---------- goodsInfo: ", goodsInfo);
|
|
|
+ const goodsId = parseInt(goodsInfo.goodsId);
|
|
|
+ const categoryId = parseInt(goodsInfo.categoryId);
|
|
|
+ const hdId = parseInt(goodsInfo.hdId);
|
|
|
+ const account = parseInt(goodsInfo.account);
|
|
|
+ if (isNaN(goodsId) || isNaN(categoryId) || isNaN(hdId) || isNaN(account)) {
|
|
|
+ this.$msg("商品数据异常");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.$util.pageTo({
|
|
|
+ url: "/pages/goods/detail",
|
|
|
+ query: { // id=2020&categoryId=1402&hdId=229&account=36548&shopId=36548&name=花仙子&avatar=https://img.theflorist.cn/hhb_small.png
|
|
|
+ id: goodsId,
|
|
|
+ account: goodsInfo.account,
|
|
|
+ hdId: goodsInfo.hdId,
|
|
|
+ categoryId: categoryId,
|
|
|
+ shopId: goodsInfo.shopId,
|
|
|
+ name: goodsInfo.name,
|
|
|
+ avatar: goodsInfo.avatar,
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
},
|
|
|
};
|
|
|
</script>
|
|
|
@@ -832,6 +1058,84 @@ export default {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
background-color: #f5f5f5;
|
|
|
+ position: relative; // 确保 goods-info-card 的定位上下文
|
|
|
+}
|
|
|
+
|
|
|
+.goods-info-container {
|
|
|
+ position: absolute;
|
|
|
+ bottom: 180upx; // 调整位置以适应输入框
|
|
|
+ left: 20upx;
|
|
|
+ right: 20upx;
|
|
|
+ z-index: 10000;
|
|
|
+}
|
|
|
+
|
|
|
+.goods-info-card {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ background-color: #ffffff;
|
|
|
+ padding: 20upx;
|
|
|
+ border-radius: 16upx;
|
|
|
+ box-shadow: 0 4upx 20upx rgba(0, 0, 0, 0.1);
|
|
|
+ transition: all 0.3s ease;
|
|
|
+
|
|
|
+ .goods-image {
|
|
|
+ width: 120upx;
|
|
|
+ height: 120upx;
|
|
|
+ border-radius: 8upx;
|
|
|
+ margin-right: 20upx;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-details {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0; // 允许 flex 子元素收缩
|
|
|
+ margin-right: 20upx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+
|
|
|
+ .goods-name {
|
|
|
+ font-size: 36upx;
|
|
|
+ color: #333;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-price {
|
|
|
+ font-size: 30upx;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #ff5050;
|
|
|
+ margin-top: 4upx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: flex-end;
|
|
|
+
|
|
|
+ .close-icon {
|
|
|
+ margin-bottom: 25upx; // 在关闭按钮和发送按钮之间添加一些间距
|
|
|
+ padding: 8upx;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+
|
|
|
+ .send-goods-button {
|
|
|
+ background-color: #ff9800;
|
|
|
+ color: #ffffff;
|
|
|
+ border: none;
|
|
|
+ border-radius: 40upx;
|
|
|
+ font-size: 26upx;
|
|
|
+ padding: 0 30upx;
|
|
|
+ height: 60upx;
|
|
|
+ line-height: 60upx;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ &::after {
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.connection-status {
|
|
|
@@ -930,7 +1234,7 @@ export default {
|
|
|
height: 0;
|
|
|
border: 12upx solid transparent;
|
|
|
border-right-color: #ffffff;
|
|
|
- z-index: 999999;
|
|
|
+ z-index: 9999;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -948,7 +1252,7 @@ export default {
|
|
|
height: 0;
|
|
|
border: 12upx solid transparent;
|
|
|
border-left-color: rgb(27, 193, 66);
|
|
|
- z-index: 999999;
|
|
|
+ z-index: 9999;
|
|
|
}
|
|
|
|
|
|
.message-text {
|
|
|
@@ -957,6 +1261,122 @@ export default {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+.goods-info.right-bubble {
|
|
|
+ background-color: #fff !important;
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ &::before {
|
|
|
+ border-left-color: #fff !important;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.goods-info {
|
|
|
+ background-color: #fff !important;
|
|
|
+ color: #333;
|
|
|
+ padding: 20rpx;
|
|
|
+ border-radius: 16rpx;
|
|
|
+ &.right-bubble {
|
|
|
+ background-color: #fff !important; // 覆盖默认气泡颜色
|
|
|
+ &::before {
|
|
|
+ border-left-color: #fff; // 确保箭头颜色与背景一致
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .goods-main {
|
|
|
+ display: flex;
|
|
|
+ position: relative;
|
|
|
+ }
|
|
|
+ .goods-image {
|
|
|
+ width: 180rpx;
|
|
|
+ height: 180rpx;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ margin-right: 20rpx;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-details {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+ .goods-name-wrapper {
|
|
|
+ height: 80rpx;
|
|
|
+ }
|
|
|
+ .goods-name {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #333;
|
|
|
+ font-weight: bold;
|
|
|
+ // 多行省略
|
|
|
+ text-overflow: -o-ellipsis-lastline;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ display: -webkit-box;
|
|
|
+ -webkit-line-clamp: 2;
|
|
|
+ line-clamp: 2;
|
|
|
+ -webkit-box-orient: vertical;
|
|
|
+ }
|
|
|
+ .sales-info {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 10rpx;
|
|
|
+ }
|
|
|
+ .delivery-time {
|
|
|
+ background-color: #e8f5ff;
|
|
|
+ color: #007aff;
|
|
|
+ font-size: 20rpx;
|
|
|
+ padding: 4rpx 8rpx;
|
|
|
+ border-radius: 4rpx;
|
|
|
+ }
|
|
|
+ .goods-price {
|
|
|
+ font-size: 36rpx;
|
|
|
+ color: #ff5050;
|
|
|
+ font-weight: bold;
|
|
|
+ margin-top: 10rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .goods-footer {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ padding-top: 10rpx;
|
|
|
+ border-top: 1rpx solid #f5f5f5;
|
|
|
+ .footer-text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ .arrow {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .action-buttons {
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ margin-top: 20rpx;
|
|
|
+ .action-btn {
|
|
|
+ padding: 10rpx 30rpx;
|
|
|
+ border-radius: 40rpx;
|
|
|
+ font-size: 28rpx;
|
|
|
+ margin-left: 20rpx;
|
|
|
+ line-height: 1.5;
|
|
|
+ &::after {
|
|
|
+ border: none;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .spec-btn {
|
|
|
+ background-color: #f5f5ff;
|
|
|
+ color: #333;
|
|
|
+ border: 1px solid #eee;
|
|
|
+ }
|
|
|
+ .buy-btn {
|
|
|
+ background-color: #ff9800;
|
|
|
+ color: white;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
.message-text {
|
|
|
font-size: 32upx;
|
|
|
line-height: 1.4;
|