Преглед изворни кода

1. 花掌柜商家聊天列表修改--从redis中取最近聊天人列表
2. 花惠宝新增聊天列表
3. 创建websocket连接时传递的参数有修改
4. bug修复

shizhongqi пре 11 месеци
родитељ
комит
649447ba68

+ 42 - 13
hdApp/src/admin/chat/chatPage.vue

@@ -62,6 +62,7 @@
 
 <script>
 import { getChatHistory } from '@/api/chat';
+import { WSSHOST } from '@/config.js';
 export default {
   data() {
     return {
@@ -73,10 +74,11 @@ export default {
       isMultiUser: false,
       // 当前用户信息
       currentUser: {
-        id: 0,
-        customId: 0,
-        shopId: 0,
-        username: '',
+        userId: 0,  //客户登录帐户的id(不是 customId)
+        staffId: 0, //门店员工id
+        customId: 0, //客户id
+        shopId: 0, //门店id
+        username: '', //门店名称
         avatar: ''
       },
       // 聊天消息列表
@@ -103,7 +105,9 @@ export default {
       this.isMultiUser = options.isMultiUser === 'true';
     }
 
-    // 如果传入了用户信息,更新当前用户
+    if (options.userId) {
+      this.currentUser.userId = options.userId;
+    }
     if (options.customId) {
       this.currentUser.customId = options.customId;
     }
@@ -114,8 +118,7 @@ export default {
       this.currentUser.username = options.name;
     }
     if (options.staffId) {
-      // 是门店,则使用staffId
-      this.currentUser.id = options.staffId;
+      this.currentUser.staffId = options.staffId;
     }
     if (options.avatar) {
       this.currentUser.avatar = options.avatar;
@@ -176,12 +179,13 @@ export default {
     // 获取主机地址
     getHost() {
       // #ifdef H5
-      return this.$constant.webSocketUrl;
+      return WSSHOST;
       // #endif
 
       // #ifdef MP-WEIXIN || APP-PLUS
       // 小程序和App环境下,需要配置具体的服务器地址
-      return this.$constant.webSocketUrl; // 替换为实际的服务器地址
+      //return 'ws://192.168.10.182:8080';
+      return WSSHOST; // 替换为实际的服务器地址
       // #endif
     },
 
@@ -198,7 +202,7 @@ export default {
 
       try {
         // 构建WebSocket URL
-        const wsUrl = `ws://${this.getHost()}/room`;
+        const wsUrl = `${this.getHost()}/room`;
 
         // 构建子协议参数(模拟POST body传输)
         const protocols = this.buildWebSocketProtocols();
@@ -257,14 +261,39 @@ export default {
     // 构建WebSocket子协议参数(模拟POST body传输)
     buildWebSocketProtocols() {
       try {
+        const userId = parseInt(this.currentUser.userId); //客户登录帐户的id(不是 customId,是customer登录帐户的userId)
+        if (isNaN(userId) || userId <= 0) {
+          this.$msg("当前用户的id出错");
+          return;
+        }
+        
+        const customId = parseInt(this.currentUser.customId);
+        if (isNaN(customId) || customId <= 0) {
+          this.$msg("当前用户的customId出错");
+          return;
+        }
+        
+        const shopId = parseInt(this.currentUser.shopId);
+        if (isNaN(shopId) || shopId <= 0) {
+          this.$msg("当前用户的shopId出错");
+          return;
+        }
+
+        const staffId = parseInt(this.currentUser.staffId);
+        if (isNaN(staffId) || staffId <= 0) {
+          this.$msg("当前用户的staffId出错");
+          return;
+        }
+
         // 创建房间参数对象
         const roomParams = {
-          u_id: parseInt(this.currentUser.id), // TODO 就是 shopId,应该修改为 staffId
-          c_id: parseInt(this.currentUser.customId), //因为是门店客户端,customId为0,好推测出是门店端连接
+          u_id: userId, //不是 customId,是customer的: userId
+          c_id: parseInt(this.currentUser.customId),
           s_id: parseInt(this.currentUser.shopId),
           u_name: this.currentUser.username && this.currentUser.username.length > 30 ? this.currentUser.username.substring(0, 30) : this.currentUser.username,
           platform: this.getPlatformInfo(),
-          type: 'shop' // 客户端类型:shop:门店端, customer:客户端
+          type: 'shop', // 客户端类型:shop:门店端, customer:客户端
+          staff_id: staffId, //门店员工id
         };
 
         // 分段传输(如果参数过长)

+ 87 - 216
hdApp/src/admin/chat/list.vue

@@ -1,83 +1,20 @@
 <template>
   <view class="app-main app-content">
-    <view class="input-wrap_box">
-      <view class="search-container">
-        <AppSearchModule placeholder="这里搜索" v-model="searchText" @input="searchFn" />
-      </view>
-      <button class="admin-button-com middle blue" @click="addCustom()">添加客户</button>
-    </view>
-    <!-- <view class="app-tabs">
-      <app-tabs :tabs="tabs" :isFixed="false" :top="50" :currentTab="tabIndex" :height="100" @change="change" itemWidth="25%" :isAdd="false" />
-    </view> -->
-
     <view class="list-wrap">
       <block v-if="$util.isEmpty(list.data) === false">
         <block v-for="(item, index) in list.data" :key="index">
-          <tui-list-cell :arrow="true" @click="pageTo({ url: '/admin/chat/chatPage', query: {customId: item.id, staffId: getLoginInfo.shopAdminId, shopId: getLoginInfo.shopId, name: getLoginInfo.name, account: getLoginInfo.shopId, avatar: shopAvatar } })">
+          <tui-list-cell :arrow="true" @click="pageTo({ url: '/admin/chat/chatPage', query: {userId: item.userId, customId: item.id, staffId: getLoginInfo.shopAdminId, shopId: getLoginInfo.shopId, name: getLoginInfo.name, avatar: shopAvatar } })">
             <view class="user-card">
               <view class="user-avatar">
-                <image :src="item.avatar" class="avatar-img" mode="aspectFill" />
+                <image :src="item.avatar || getDefaultImage()" class="avatar-img" mode="aspectFill" />
               </view>
               <view class="user-info">
                 <view class="user-main">
                   <view class="user-name">{{ item.name }}</view>
-                  <view class="right-info">
-                    <view v-if="!$util.isEmpty(item.memberName)" class="member-icon">
-                      <image
-                        v-if="item.member == 1"
-                        class="member-badge-icon"
-                        style="width: 76upx; height: 76upx"
-                        src="@/static/member-icons/member-1.svg"
-                        mode="aspectFit"
-                      ></image>
-                      <image
-                        v-else-if="item.member == 2"
-                        class="member-badge-icon"
-                        style="width: 74upx; height: 74upx"
-                        src="@/static/member-icons/member-2.svg"
-                        mode="aspectFit"
-                      ></image>
-                      <image
-                        v-else-if="item.member == 3"
-                        class="member-badge-icon"
-                        style="width: 73upx; height: 73upx"
-                        src="@/static/member-icons/member-3.svg"
-                        mode="aspectFit"
-                      ></image>
-                      <image
-                        v-else-if="item.member == 4"
-                        class="member-badge-icon"
-                        style="width: 82upx; height: 82upx"
-                        src="@/static/member-icons/member-4.svg"
-                        mode="aspectFit"
-                      ></image>
-                      <image
-                        v-else-if="item.member == 5"
-                        class="member-badge-icon"
-                        style="width: 77upx; height: 77upx"
-                        src="@/static/member-icons/member-5.svg"
-                        mode="aspectFit"
-                      ></image>
-                    </view>
-                  </view>
                 </view>
                 <view class="user-meta">
                   <view class="visit-time">
-                    {{ item.visitTime.substr(2, 11) }}
-
-                    <text v-if="Number(item.balance) > 0" class="positive">余额{{ parseFloat(item.balance) }}</text>
-                    <text v-if="Number(item.balance) < 0" class="negative">-{{ parseFloat(Math.abs(item.balance)) }}</text>
-
-                    <block v-if="item.passStatus == 1">
-                      <text v-if="item.overTimeUnExpend && item.overTimeUnExpend == 10000" style="margin-left: 20upx; color: green">没有下过单</text>
-                      <text v-if="item.overTimeUnExpend && item.overTimeUnExpend == 7" style="margin-left: 20upx; color: green">超1周未下单</text>
-                      <text v-if="item.overTimeUnExpend && item.overTimeUnExpend == 30" style="margin-left: 20upx; color: green">超1个月未下单</text>
-                      <text v-if="item.overTimeUnExpend && item.overTimeUnExpend == 180" style="margin-left: 20upx; color: green">超半年未下单</text>
-                      <text v-if="item.overTimeUnExpend && item.overTimeUnExpend == 365" style="margin-left: 20upx; color: green">超1年未下单</text>
-                    </block>
-                    <block v-else>
-                      <text style="font-size: 26upx; margin-left: 20upx; color: #3385ff; font-weight: bold">待审核</text>
-                    </block>
+                    {{ formatChatTime(item.chatTime) }}
                   </view>
                 </view>
               </view>
@@ -94,55 +31,44 @@
   </view>
 </template>
 <script>
-import AppTabs from "@/components/plugin/tabs";
 import TuiListCell from "@/components/plugin/list-cell";
 import AppWrapperEmpty from "@/components/app-wrapper-empty";
-import AppSearchModule from "@/components/module/app-search";
 import { list } from "@/mixins";
-import { getList } from "@/api/member";
+import { getLatestUsers } from "@/api/chat";
 import NotLogin from "@/components/not-login";
 import {mapGetters } from "vuex";
 export default {
-  name: "memebr",
+  name: "member",
   components: {
-    AppTabs,
     TuiListCell,
     AppWrapperEmpty,
-    NotLogin,
-    AppSearchModule
+    NotLogin
   },
   mixins: [list],
   data() {
+    // 预计算常用日期值,提高性能
+    const now = new Date();
+    const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
+
+    // 内联日期格式化函数,避免重复代码
+    const formatDateToString = (date) => {
+      const y = date.getFullYear();
+      const m = String(date.getMonth() + 1).padStart(2, '0');
+      const d = String(date.getDate()).padStart(2, '0');
+      return `${y}-${m}-${d}`;
+    };
+
     return {
-      tabIndex: 0,
       shopAvatar: '',
-      tabs: [
-        {
-          name: "全部",
-          value: 0,
-          type:0
-        },
-        {
-          name: "余额",
-          value: 0,
-          type:2
-        },
-        {
-          name: "消费",
-          value: 0,
-          type:1
-        },
-        {
-          name: "生日",
-          value: 0,
-          type:3
-        }
-      ],
-      customType:0,
-      searchStyle:0,
-      searchText:''
+      currentYear: now.getFullYear(), // 获取当前年份
+      todayStr: formatDateToString(now), // 今日
+      yesterdayStr: formatDateToString(yesterday),// 昨天
     };
   },
+  created() {
+    // 更新日期字符串,确保跨日期时正确
+    this.updateDateStrings();
+  },
   onPullDownRefresh() {
     this.resetList();
     this.getMyCustomList().then(res => {
@@ -151,105 +77,91 @@ export default {
   },
   onReachBottom() {
     if (!this.list.finished) {
-      this.getMyCustomList().then(res => {
-        uni.stopPullDownRefresh();
-      });
-    } else {
-      uni.stopPullDownRefresh();
+      this.getMyCustomList();
     }
   },
   computed: {
     ...mapGetters(["getLoginInfo"]),
   },
   onLoad(options) {
-    console.log("---------- getLoginInfo: ", this.getLoginInfo.avatar);
     this.shopAvatar = this.getLoginInfo.avatar != '' ? this.getLoginInfo.avatar : this.$constant.imgUrl + '/retail/default-img.png';
   },
   methods: {
-    change(e) {
-      if (this.tabIndex == e.index) {
-        return false;
-      }
-      this.tabIndex = e.index;
-      const tab = this.tabs[this.tabIndex];
-      this.customType = tab.type;
-      this.resetList();
-      this.getMyCustomList();
-    },
-    addCustom() {
-      this.pageTo({ url: '/admin/custom/addCustom' });
-    },
     init() {
       this.getMyCustomList();
     },
-    searchFn() {
-      this.resetList();
-      this.getMyCustomList();
-    },
     getMyCustomList() {
-      return getList({ page: this.list.page, type: this.customType, searchText: this.searchText, searchStyle: this.searchStyle }).then(res => {
+      return getLatestUsers({ page: this.list.page}).then(res => {
         if (res.code == 1) {
-          this.completes(res);
+          this.list.data = res.data;
         }
       });
-    }
+    },
+    getDefaultImage() {
+      // 使用 ext 配置中的 imgHost
+      return this.$constant.imgUrl + '/retail/default-img.png';
+    },
+    formatChatTime(chatTime) {
+      if (!chatTime) return '';
+
+      try {
+        // 优化:使用更高效的字符串解析
+        const spaceIndex = chatTime.indexOf(' ');
+        const dateStr = chatTime.substring(0, spaceIndex);
+        const timeStr = chatTime.substring(spaceIndex + 1);
+
+        // 快速解析日期部分
+        const year = parseInt(dateStr.substring(0, 4));
+
+        // 优化:使用预计算的日期字符串进行比较
+        const chatDateStr = dateStr; // 已经是 "YYYY-MM-DD" 格式
+
+        // 判断是否是今天(使用预计算值)
+        if (chatDateStr === this.todayStr) {
+          return timeStr; // 今天只显示时间,如 "12:24"
+        }
+        // 判断是否是昨天(使用预计算值)
+        else if (chatDateStr === this.yesterdayStr) {
+          return `昨天 ${timeStr}`; // 昨天显示 "昨天 12:24"
+        }
+        // 判断是否是今年
+        else if (year === this.currentYear) {
+          // 如果是今年但不是今天或昨天,显示月日和时间,如 "09-01 12:24"
+          return chatTime.substring(5, 14); // 直接截取 "MM-DD HH:mm" 部分
+        }
+        // 如果不是今年,显示完整日期
+        else {
+          return chatTime;
+        }
+      } catch (error) {
+        // 如果解析失败,返回原字符串
+        console.error('时间格式化失败:', error);
+        return chatTime;
+      }
+    },
+    // 更新日期字符串(可用于定时更新或跨日期检查)
+    updateDateStrings() {
+      const now = new Date();
+      const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
+
+      const formatDateToString = (date) => {
+        const y = date.getFullYear();
+        const m = String(date.getMonth() + 1).padStart(2, '0');
+        const d = String(date.getDate()).padStart(2, '0');
+        return `${y}-${m}-${d}`;
+      };
+
+      this.todayStr = formatDateToString(now);
+      this.yesterdayStr = formatDateToString(yesterday);
+      this.currentYear = now.getFullYear();
+    },
   }
 };
 </script>
 <style lang="scss" scoped>
 .app-content {
-  padding-top: 100upx;
   padding-bottom: 20upx;
 }
-.list-wrap {
-  .list {
-    background-color: #fff;
-    margin-bottom: 20upx;
-  }
-}
-.app-tabs {
-  position: fixed;
-  @media screen and (max-width: 1100px) {
-    width: 100%;
-  }
-  z-index: 9;
-}
-.tabs-wrap {
-  position: fixed;
-  .tabs-left {
-    width: 80%;
-  }
-  .tabs-right {
-    width: 19%;
-    border-left: $borderColor;
-    @include disFlex(center, center);
-  }
-}
-.input-wrap_box {
-  background-color: #fff;
-  position: fixed;
-  top: 0;
-  z-index: 10;
-  width: 100%;
-  height: 100upx;
-  display: flex;
-  align-items: center;
-  padding: 0 20upx;
-  gap: 20upx;
-
-  .admin-button-com {
-    flex-shrink: 0;
-    min-width: 120upx;
-  }
-
-  .search-container {
-    flex: 1;
-    min-width: 0;
-    display: flex;
-    align-items: center;
-  }
-}
-
 .user-card {
   display: flex;
   align-items: center;
@@ -301,31 +213,6 @@ export default {
     max-width: calc(100% - 50upx); /* 进一步增加名字显示的最大宽度 */
   }
 
-  .right-info {
-    position: absolute;
-    right: 50upx;
-    top: 8upx;
-    display: flex;
-    flex-direction: column;
-    align-items: flex-end;
-    gap: 4upx;
-    width: 80upx; /* 进一步减小宽度以给名字留出更多空间 */
-    z-index: 1;
-    text-align: right;
-  }
-
-  .positive {
-    color: #3385ff;
-    margin-left: 20upx;
-    font-weight: bold;
-  }
-
-  .negative {
-    color: #ff0000;
-    margin-left: 20upx;
-    font-weight: bold;
-  }
-
   .user-meta {
     display: flex;
     align-items: center;
@@ -339,22 +226,6 @@ export default {
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
-    font-size: 26upx;
-    width: 495upx;
-  }
-
-  .member-icon {
-    display: flex;
-    align-items: center;
-    justify-content: center;
-    flex-shrink: 0;
-  }
-
-  .member-badge-icon {
-    width: 80upx;
-    height: 80upx;
-    flex-shrink: 0;
-    filter: drop-shadow(0 2upx 6upx rgba(0, 0, 0, 0.3));
   }
 }
 

+ 2 - 0
hdApp/src/api/chat/index.js

@@ -1,3 +1,5 @@
 import https from '@/plugins/luch-request_0.0.7/request'
 
+export const getLatestUsers = param => https.post('/chat/latest-users', param)
+
 export const getChatHistory = param => https.post('/chat/chat-history', param)

+ 2 - 0
mallApp/src/api/chat/index.js

@@ -1,3 +1,5 @@
 import https from '@/plugins/luch-request_0.0.7/request'
 
+export const getLatestUsers = param => https.post('/chat/latest-users', param)
+
 export const getChatHistory = param => https.post('/chat/chat-history', param)

+ 37 - 10
mallApp/src/pages/chat/chatPage.vue

@@ -87,6 +87,7 @@
 <script>
 import { getChatHistory } from "@/api/chat";
 import {mapGetters } from "vuex";
+import { WSSHOST } from '@/config.js';
 export default {
   computed: {
     ...mapGetters(["getLoginInfo"]),
@@ -101,7 +102,7 @@ export default {
       isMultiUser: false,
       // 当前用户信息
       currentUser: {
-        id: 0,
+        userId: 0,
         customId: 0,
         shopId: 0,
         username: "",
@@ -125,7 +126,9 @@ export default {
       this.isMultiUser = options.isMultiUser === "true";
     }
 
-    // 如果传入了用户信息,更新当前用户
+    if (this.loginInfo.id) {
+      this.currentUser.userId = this.loginInfo.id;
+    }
     if (options.customId) {
       this.currentUser.customId = options.customId;
     }
@@ -184,9 +187,14 @@ export default {
     init() {},
     // 加载聊天历史记录
     loadChatHistory() {
+      const userId = parseInt(this.currentUser.userId);
+      if (isNaN(userId) || userId <= 0) {
+        this.$msg("当前用户的id出错");
+        return;
+      }
       getChatHistory({
         roomName: this.room,
-        userId: this.currentUser.id,
+        userId: userId,
       }).then((res) => {
         if (res.code == 1 && res.data) {
           this.messageList = res.data;
@@ -200,13 +208,13 @@ export default {
     // 获取主机地址
     getHost() {
       // #ifdef H5
-      return this.$constant.webSocketUrl;
+      return WSSHOST;
       // #endif
 
       // #ifdef MP-WEIXIN || APP-PLUS
       // 小程序和App环境下,需要配置具体的服务器地址
-      // return this.$constant.webSocketUrl;
-      return "192.168.10.182:8080";
+       return WSSHOST;
+      //return "ws://192.168.10.182:8080";
       // #endif
     },
 
@@ -223,7 +231,7 @@ export default {
 
       try {
         // 构建WebSocket URL(移除查询参数)
-        const wsUrl = `ws://${this.getHost()}/room`;
+        const wsUrl = `${this.getHost()}/room`;
         console.log("---------- wsUrl: ", wsUrl);
 
         // 构建子协议参数(模拟POST body传输)
@@ -286,14 +294,33 @@ export default {
     // 构建WebSocket子协议参数(模拟POST body传输)
     buildWebSocketProtocols() {
       try {
+        const userId = parseInt(this.loginInfo.id); //当前用户的id(不是 customId,是customer登录帐户的userId)
+        if (isNaN(userId) || userId <= 0) {
+          this.$msg("当前用户的id出错");
+          return;
+        }
+        
+        const customId = parseInt(this.currentUser.customId);
+        if (isNaN(customId) || customId <= 0) {
+          this.$msg("当前用户的customId出错");
+          return;
+        }
+        
+        const shopId = parseInt(this.currentUser.shopId);
+        if (isNaN(shopId) || shopId <= 0) {
+          this.$msg("当前用户的shopId出错");
+          return;
+        }
+        
         // 创建房间参数对象
         const roomParams = {
-          u_id: parseInt(this.loginInfo.id), // TODO 就是 customId,应该修改为: userId ???
-          c_id: parseInt(this.currentUser.customId),
-          s_id: parseInt(this.currentUser.shopId), //因为是花惠宝端用户,shopId为0,好推测出是花惠宝端连接
+          u_id: userId, //当前用户的id(不是 customId,是customer登录帐户的userId)
+          c_id: customId,
+          s_id: shopId,
           u_name: this.currentUser.username && this.currentUser.username.length > 30 ? this.currentUser.username.substring(0, 30) : this.currentUser.username,
           platform: this.getPlatformInfo(),
           type: "customer", // 客户端类型:shop:门店端, customer:客户端
+          staff_id: parseInt(0),
         };
 
         // 分段传输(如果参数过长)

+ 254 - 0
mallApp/src/pages/chat/list.vue

@@ -0,0 +1,254 @@
+<template>
+  <view class="app-main app-content">
+    <view class="list-wrap">
+      <block v-if="$util.isEmpty(list.data) === false">
+        <block v-for="(item, index) in list.data" :key="index">
+          <tui-list-cell
+            :arrow="true"
+            @click="
+              pageTo({
+                url: '/pages/chat/chatPage',
+                query: {
+                  customId: item.customId,
+                  shopId: item.id,
+                  name: getLoginInfo.name,
+                  avatar: getLoginInfo.avatar,
+                },
+              })
+            "
+          >
+            <view class="user-card">
+              <view class="user-avatar">
+                <image
+                  :src="item.avatar || getDefaultImage()"
+                  class="avatar-img"
+                  mode="aspectFill"
+                />
+              </view>
+              <view class="user-info">
+                <view class="user-main">
+                  <view class="user-name">{{
+                    item.shopName != "首店"
+                      ? item.shopName
+                      : item.merchantName || item.mobile
+                  }}</view>
+                </view>
+                <view class="user-meta">
+                  <view class="visit-time">
+                    {{ formatChatTime(item.chatTime) }}
+                  </view>
+                </view>
+              </view>
+            </view>
+          </tui-list-cell>
+        </block>
+      </block>
+      <block v-else>
+        <app-wrapper-empty :is-empty="true" />
+      </block>
+    </view>
+  </view>
+</template>
+<script>
+import TuiListCell from "@/components/plugin/list-cell";
+import AppWrapperEmpty from "@/components/app-wrapper-empty";
+import { list } from "@/mixins";
+import { getLatestUsers } from "@/api/chat";
+import {mapGetters } from "vuex";
+export default {
+  name: "member",
+  components: {
+    TuiListCell,
+    AppWrapperEmpty
+  },
+  mixins: [list],
+  data() {
+    // 预计算常用日期值,提高性能
+    const now = new Date();
+    const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
+
+    // 内联日期格式化函数,避免重复代码
+    const formatDateToString = (date) => {
+      const y = date.getFullYear();
+      const m = String(date.getMonth() + 1).padStart(2, '0');
+      const d = String(date.getDate()).padStart(2, '0');
+      return `${y}-${m}-${d}`;
+    };
+
+    return {
+      shopAvatar: '',
+      currentYear: now.getFullYear(),
+      todayStr: formatDateToString(now),
+      yesterdayStr: formatDateToString(yesterday),
+    };
+  },
+  created() {
+    // 更新日期字符串,确保跨日期时正确
+    this.updateDateStrings();
+  },
+  onPullDownRefresh() {
+    this.resetList();
+    this.getMyCustomList().then(res => {
+      uni.stopPullDownRefresh();
+    });
+  },
+  onReachBottom() {
+    if (!this.list.finished) {
+      this.getMyCustomList();
+    }
+  },
+  computed: {
+    ...mapGetters(["getLoginInfo"]),
+  },
+  onLoad(options) {
+    this.shopAvatar = this.getLoginInfo.avatar != '' ? this.getLoginInfo.avatar : this.$constant.imgUrl + '/retail/default-img.png';
+
+  },
+  methods: {
+    init() {
+      this.getMyCustomList();
+    },
+    getMyCustomList() {
+      return getLatestUsers({}).then(res => {
+        if (res.code == 1) {
+          this.list.data = res.data;
+        }
+      });
+    },
+    getDefaultImage() {
+      // 使用 ext 配置中的 imgHost
+      return this.$constant.imgUrl + '/retail/default-img.png';
+    },
+    formatChatTime(chatTime) {
+      if (!chatTime) return '';
+
+      try {
+        // 优化:使用更高效的字符串解析
+        const spaceIndex = chatTime.indexOf(' ');
+        const dateStr = chatTime.substring(0, spaceIndex);
+        const timeStr = chatTime.substring(spaceIndex + 1);
+
+        // 快速解析日期部分
+        const year = parseInt(dateStr.substring(0, 4));
+
+        // 优化:使用预计算的日期字符串进行比较
+        const chatDateStr = dateStr; // 已经是 "YYYY-MM-DD" 格式
+
+        // 判断是否是今天(使用预计算值)
+        if (chatDateStr === this.todayStr) {
+          return timeStr; // 今天只显示时间,如 "12:24"
+        }
+        // 判断是否是昨天(使用预计算值)
+        else if (chatDateStr === this.yesterdayStr) {
+          return `昨天 ${timeStr}`; // 昨天显示 "昨天 12:24"
+        }
+        // 判断是否是今年
+        else if (year === this.currentYear) {
+          // 如果是今年但不是今天或昨天,显示月日和时间,如 "09-01 12:24"
+          return chatTime.substring(5, 14); // 直接截取 "MM-DD HH:mm" 部分
+        }
+        // 如果不是今年,显示完整日期
+        else {
+          return chatTime;
+        }
+      } catch (error) {
+        // 如果解析失败,返回原字符串
+        console.error('时间格式化失败:', error);
+        return chatTime;
+      }
+    },
+    // 更新日期字符串(可用于定时更新或跨日期检查)
+    updateDateStrings() {
+      const now = new Date();
+      const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
+
+      const formatDateToString = (date) => {
+        const y = date.getFullYear();
+        const m = String(date.getMonth() + 1).padStart(2, '0');
+        const d = String(date.getDate()).padStart(2, '0');
+        return `${y}-${m}-${d}`;
+      };
+
+      this.todayStr = formatDateToString(now);
+      this.yesterdayStr = formatDateToString(yesterday);
+      this.currentYear = now.getFullYear();
+    },
+  }
+};
+</script>
+<style lang="scss" scoped>
+.app-content {
+  padding-bottom: 20upx;
+}
+.user-card {
+  display: flex;
+  align-items: center;
+  padding: 12upx 0;
+  width: 100%;
+  position: relative;
+  box-sizing: border-box;
+
+  .user-avatar {
+    width: 80upx;
+    height: 80upx;
+    border-radius: 50%;
+    overflow: hidden;
+    margin-left: 16upx;
+    margin-right: 16upx;
+    flex-shrink: 0; /* 防止头像被挤压 */
+
+    .avatar-img {
+      width: 100%;
+      height: 100%;
+    }
+  }
+
+  .user-info {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+    justify-content: space-between;
+    height: 80upx;
+    padding-right: 150upx; /* 减小右侧内边距,给名字留出更多空间 */
+    min-width: 0; /* 确保flex子元素可以正确缩小 */
+  }
+
+  .user-main {
+    display: flex;
+    align-items: flex-start;
+    margin-bottom: 4upx;
+    width: 100%;
+  }
+
+  .user-name {
+    flex: 1;
+    font-size: 34upx;
+    color: #333;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    margin-right: 10upx;
+    max-width: calc(100% - 50upx); /* 进一步增加名字显示的最大宽度 */
+  }
+
+  .user-meta {
+    display: flex;
+    align-items: center;
+    font-size: 26upx;
+    color: #9397a4;
+    width: 495upx;
+    margin-top: 8upx;
+  }
+
+  .visit-time {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+}
+
+// 确保箭头正常显示
+::v-deep .tui-list-cell.tui-cell-arrow:before {
+  z-index: 10;
+}
+</style>

+ 14 - 2
mallApp/src/pages/goods/detail.vue

@@ -212,13 +212,25 @@ export default {
       // #endif
     },
     goChat(){
-      if(!this.customId){
+      if(parseInt(this.loginInfo.id) <= 0){
+        this.$msg("数据异常,请稍后再试");
+        return;
+      }
+      if(parseInt(this.customId) <= 0){
+        this.$msg("数据异常,请稍后再试");
+        return;
+      }
+      if(parseInt(this.shopInfo.shopAdminId) <= 0){
+        this.$msg("数据异常,请稍后再试");
+        return;
+      }
+      if(parseInt(this.shopInfo.shopId) <= 0){
         this.$msg("数据异常,请稍后再试");
         return;
       }
       this.$util.pageTo({
         url: "/pages/chat/chatPage",
-        query: { customId: this.customId, shopId: this.shopInfo.shopId, name: this.shopInfo.name, account: this.shopInfo.account, avatar: this.shopInfo.avatar }
+        query: { userId: this.loginInfo.id, customId: this.customId, staffId: this.shopInfo.shopAdminId, shopId: this.shopInfo.shopId, name: this.loginInfo.name, avatar: this.loginInfo.avatar }
       });
     }
   },

Разлика између датотеке није приказан због своје велике величине
+ 313 - 229
mallApp/src/pages/home/recent.vue


Неке датотеке нису приказане због велике количине промена