Просмотр исходного кода

Merge branch 'master' of http://git.huaml.com/zhh/front-end

shish 11 месяцев назад
Родитель
Сommit
86d589fe17

+ 176 - 25
hdApp/src/admin/chat/chatPage.vue

@@ -29,6 +29,7 @@
           <view class="avatar-wrapper">
             <image class="avatar" :src="message.avatar" mode="aspectFill" />
           </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">
@@ -36,11 +37,12 @@
             </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 right-bubble">
+            <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">
@@ -55,9 +57,14 @@
               </view>
               <view class="goods-footer">
               </view>
-              <view class="action-buttons">
-                <input class="price-input" type="number" placeholder="输入价格" />
-                <button class="action-btn buy-btn" click="createPrice(message.goodsInfo)">报价</button>
+              <view v-if="message.goodsInfo.goodsPriceType == 0 && isExpired(message.timestamp)" class="action-buttons">
+                <form @submit.prevent="createQrotedPrice(message)">
+                  <input class="price-input" type="number" v-model="message.quotedPrice" placeholder="输入价格" />
+                  <button class="action-btn buy-btn" form-type="submit">报价</button>
+                </form>
+              </view>
+              <view v-else>
+                <text v-if="message.goodsInfo.goodsPriceType == 0">报价窗口期已过,无法报价</text>
               </view>
             </view>
             <view class="message-time align-right">{{ message.time }}</view>
@@ -66,13 +73,49 @@
 
         <!-- 右侧消息(我的) -->
         <view class="message-right" v-if="message.isMine || message.shopId">
-          <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>
             <view class="message-bubble right-bubble">
               <text class="message-text">{{ message.message }}</text>
             </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>
+                <!-- 报价类型显示去购买 -->
+                <button v-if="message.goodsInfo.goodsPriceType == 2" 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>
@@ -117,7 +160,7 @@
 </template>
 
 <script>
-import { getChatHistory } from '@/api/chat';
+import { getChatHistory, createQuotedPrice } from '@/api/chat';
 import { WSSHOST } from '@/config.js';
 export default {
   data() {
@@ -157,6 +200,7 @@ export default {
       isKeyboardShow: false, // 键盘是否显示
       inputFocus: false, // 输入框焦点
       isInputFocused: false, // 标记输入框是否聚焦
+      expireTime: 12 * 60 * 60, // 商品报价有效窗口时长 -- 12小时
     };
   },
   onLoad(options) {
@@ -234,13 +278,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() {},
@@ -324,7 +368,6 @@ export default {
     // 连接WebSocket
     connectWebSocket() {
       if (!this.room) {
-        console.error('房间号不能为空');
         uni.showToast({
           title: '房间号不能为空',
           icon: 'none'
@@ -333,12 +376,11 @@ export default {
       }
 
       try {
-        // 构建WebSocket URL
         const wsUrl = `${this.getHost()}/room`;
 
         // 构建子协议参数(模拟POST body传输)
         const protocols = this.buildWebSocketProtocols();
-        // 创建WebSocket连接
+        // 创建 WebSocket连接
         this.socket = uni.connectSocket({
           url: wsUrl,
           protocols: protocols,
@@ -641,12 +683,6 @@ export default {
         this.reconnectCount++;
         console.log(`正在尝试第${this.reconnectCount}次重连...`);
 
-        // uni.showToast({
-        //   title: `重连中(${this.reconnectCount}/${this.maxReconnectCount})`,
-        //   icon: 'loading',
-        //   duration: 1500
-        // });
-
         this.connectWebSocket();
       } else {
         uni.showToast({
@@ -696,7 +732,8 @@ export default {
           message: messageData.message || messageData || '',
           username: messageData.name || messageData.username || '未知用户',
           avatar: messageData.avatar || this.$constant.imgUrl + '/retail/default-img.png',
-          time: this.getCurrentTime(),
+          time: this.getCurrentTime(), // 收到消息时间点
+          timestamp: messageData.timestamp, // 消息发送时间点
           messageType: messageType,
         };
 
@@ -746,7 +783,8 @@ export default {
           message: messageContent,
           username: this.currentUser.username,
           avatar: avatar,
-          time: this.getCurrentTime(),
+          time: this.getCurrentTime(timestamp), // 收到消息时间点
+          timestamp: timestamp, // 消息发送时间点
           messageType: "text",
         };
 
@@ -827,10 +865,15 @@ export default {
     },
 
     // 获取当前时间
-    getCurrentTime() {
-      const now = new Date();
-      const hours = String(now.getHours()).padStart(2, '0');
-      const minutes = String(now.getMinutes()).padStart(2, '0');
+    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}`;
     },
     // 输入框聚焦事件
@@ -888,6 +931,106 @@ export default {
         };
       }
     },
+
+    // 发送商品报价信息
+    sendMessageOfGoodsPrice(currentGoodsInfo) {
+      if (!this.isConnected) {
+        uni.showToast({
+          title: "WebSocket未连接",
+          icon: "none",
+        });
+        return;
+      }
+      const avatar = this.currentUser.avatar || this.$constant.imgUrl + "/retail/default-img.png";
+      const timestamp = Math.floor(Date.now() / 1000);
+
+      // 构建商品信息消息体 -------------------------------- 所看到的消息体 -------------------------------
+      const goodsMessage = {
+        ...currentGoodsInfo,
+      };
+      const messageContent = JSON.stringify(goodsMessage); // 所看到的消息体
+
+      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(timestamp),
+          messageType: "goods", // 附加一个字段,用于渲染时区分商品消息
+          goodsInfo: goodsMessage,
+        };
+
+        // 添加到消息列表
+        this.messageList.push(myMessage);
+        // 滚动到底部
+        // this.$nextTick(() => {
+        //   this.scrollToBottom();
+        // });
+        setTimeout(() => {
+          this.scrollToBottom();
+        }, 100);
+
+        // 构建要发送的消息数据
+        const messageData = {
+          receiver: this.currentUser.customId,
+          message: messageContent,
+          username: this.currentUser.username,
+          avatar: avatar,
+          shopId: this.currentUser.shopId, // 发送者:门店使用shopId,客户使用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",
+        });
+      }
+    },
+    createQrotedPrice(goodsInfo) {
+      console.log("createPrice", goodsInfo)
+      createQuotedPrice({
+        key: goodsInfo.goodsInfo.key,
+        price: goodsInfo.quotedPrice,
+      }).then(res => {
+        console.log("createQuotedPrice", res)
+        if (res.code == 1) {
+          this.$msg("报价成功")
+          this.sendMessageOfGoodsPrice(goodsInfo.goodsInfo)
+        }
+      }).catch(err => {
+        console.log("createQuotedPrice error", err)
+      })
+    },
+    isExpired(timestamp) {
+      if (isNaN(timestamp)) {
+        return false;
+      }
+      const now = Math.floor(Date.now()/1000);
+      const timeDiff = now - timestamp;
+      return timeDiff < this.expireTime;
+    }
   },
 };
 </script>
@@ -1110,7 +1253,13 @@ export default {
     justify-content: flex-end;
     align-items: center;
     margin-top: 20rpx;
+    form {
+      display: flex;
+      align-items: center;
+    }
     .price-input {
+      display: inline-block;
+      vertical-align: middle;
       width: 180rpx;
       height: 60rpx;
       border: 1px solid #666666;
@@ -1120,6 +1269,8 @@ export default {
       text-align: center;
     }
     .action-btn {
+      display: inline-block;
+      vertical-align: middle;
       padding: 10rpx 30rpx;
       border-radius: 40rpx;
       font-size: 28rpx;

+ 25 - 23
hdApp/src/admin/home/workbench.vue

@@ -512,14 +512,15 @@ export default {
         this.backAction = ''
       }
 
+      // 判断是否登录
       if(!this.$util.isEmpty(this.loginInfo.admin) && this.loginInfo.admin.currentShopId > 0){
           if(!this.$util.isEmpty(this.loginInfo.onlyCg)){
             this.handleLoginStyle(this.loginInfo.onlyCg)
           }
-      }
 
-      this.showShopName()
-      this.getUnReadMsgCount()
+          this.getUnReadMsgCount()
+          this.showShopName()
+      }
   },
 	onShareAppMessage(res) {
 		return {
@@ -530,6 +531,27 @@ export default {
 		}
 	},
   methods: {
+    init () {
+        this.getGHSList()
+
+        // #ifdef MP-WEIXIN
+        this.isMini = 1
+        // #endif
+
+        let that = this
+        consoleIndex({isMini:this.isMini}).then(res => {
+          this.data = res.data
+          this.lookMoney = res.data.lookMoney
+        })
+
+        getStat().then(res=>{
+          if(res.code == 1){
+            that.overview = res.data.overview
+          }
+        })
+        this.showShopName()
+        this.getUnReadMsgCount()
+    },
     handleLoginStyle(onlyCg){
       if(Number(onlyCg) == 1){
         this.loginStyle  = 1
@@ -801,26 +823,6 @@ export default {
       }
       this.$util.pageTo(item)
     },
-    init () {
-        this.getGHSList()
-
-        // #ifdef MP-WEIXIN
-        this.isMini = 1
-        // #endif
-
-        let that = this
-        consoleIndex({isMini:this.isMini}).then(res => {
-          this.data = res.data
-          this.lookMoney = res.data.lookMoney
-        })
-
-        getStat().then(res=>{
-          if(res.code == 1){
-            that.overview = res.data.overview
-          }
-        })
-        this.showShopName()
-    },
     toRemindCustom(){
         if(this.loginStyle==1){
           hasMoreGhs().then(ret=>{

+ 4 - 1
hdApp/src/api/chat/index.js

@@ -4,4 +4,7 @@ export const getLatestUsers = param => https.post('/chat/latest-users', param)
 
 export const getChatHistory = param => https.post('/chat/chat-history', param)
 
-export const unReadMsgCount = param => https.post('/chat/un-read-msg-count', param)
+export const unReadMsgCount = param => https.post('/chat/un-read-msg-count', param)
+
+// 报价
+export const createQuotedPrice = param => https.post('/chat/create-quoted-price', param)

+ 4 - 1
mallApp/src/api/chat/index.js

@@ -4,4 +4,7 @@ export const getLatestUsers = param => https.post('/chat/latest-users', param)
 
 export const getChatHistory = param => https.post('/chat/chat-history', param)
 
-export const unReadMsgCount = param => https.post('/chat/un-read-msg-count', param)
+export const unReadMsgCount = param => https.post('/chat/un-read-msg-count', param)
+
+// 获取商品报价
+export const getQuotedPrice = param => https.post('/chat/get-quoted-price', param)

+ 152 - 29
mallApp/src/pages/chat/chatPage.vue

@@ -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;
     }
   }
 

+ 1 - 0
mallApp/src/pages/goods/components/buy-foot.vue

@@ -18,6 +18,7 @@
         </view> -->
         <div class="btn-right">
             <button v-if="priceType == 1" class="button-com red" @click="buy">购买</button>
+            <button v-if="priceType == 2" class="button-com red" @click="buy">购买</button>
             <button v-else class="button-com red" @click.stop="goChat">询价</button>
         </div>
     </view>

+ 9 - 3
mallApp/src/pages/goods/detail.vue

@@ -8,9 +8,15 @@
     <view class="shop-intro">
       <view class="app-price app-bold" v-if="data.priceType == 1">
         <text>¥</text>
-        <text class="app-size-34">{{
-          data.price ? parseFloat(data.price).toFixed(2) : 0
-        }}</text>
+        <text class="app-size-34">
+          {{ data.price ? parseFloat(data.price).toFixed(2) : 0}}
+        </text>
+      </view>
+      <view class="app-price app-bold" v-if="data.priceType == 2">
+        <text>¥</text>
+        <text class="app-size-34">
+          {{ data.price ? parseFloat(data.price).toFixed(2) : 0}}
+        </text>
       </view>
       <view class="tui-pro-titbox">
         <view class="tui-pro-title app-size-32">{{ data.name }}</view>