diff --git a/common/http.interceptor.js b/common/http.interceptor.js index aab639c..82ae3b0 100644 --- a/common/http.interceptor.js +++ b/common/http.interceptor.js @@ -16,7 +16,7 @@ const install = (Vue, vm) => { // }, // }); Vue.prototype.$u.http.setConfig({ - // baseUrl: 'http://192.168.2.18:4301', + // baseUrl: 'http://192.168.2.84:4301', baseUrl: 'https://bao.chuangtewl.com/prod-api', loadingText: '努力加载中~', loadingTime: 800, diff --git a/page_user/diandan/index.vue b/page_user/diandan/index.vue index 534a09a..a915d53 100644 --- a/page_user/diandan/index.vue +++ b/page_user/diandan/index.vue @@ -16,8 +16,14 @@ - - + + {{ product.name }} @@ -34,7 +40,7 @@ 当前没有更多商品咯... - + @@ -119,7 +125,7 @@ {{shopobj.name}} - + {{specGroup.name}} @@ -185,12 +191,15 @@ import { number } from 'echarts' totalPrice: 0, boothId:'', skuList:[], - xiadanflag:true + xiadanflag:true, + scrollTop: 0, + isScrolling: false, + scrollTimer: null } }, computed: { filteredProducts() { - // 这里可以根据currentCategoryIndex进行分类过滤,示例中暂不区分 + // 显示所有商品,不进行过滤,以便滚动联动功能正常工作 return this.products } }, @@ -201,6 +210,9 @@ import { number } from 'echarts' onShow() { this.xiadanflag = true }, + onReady() { + // 页面渲染完成后的初始化 + }, methods: { // 获取购物车中商品的数量 getCartItemQuantity(productId) { @@ -235,17 +247,54 @@ import { number } from 'echarts' this.$u.get(`/app/goodsCategory/list?pageNum=1&pageSize=999`).then(res =>{ if(res.code == 200){ this.categories = res.data + // 清理分类名称 + this.categories.forEach(category => { + if (category.name) { + category.name = category.name.trim() + } + }) this.categoryId = res.data[0].id + console.log('分类数据加载完成:', this.categories) + // 分类加载完成后再加载商品 this.getshoplist() } }) }, // 请求商品列表 getshoplist(){ - this.$u.get(`/app/goods/list?pageNum=1&pageSize=999&categoryId=${this.categoryId}`).then(res =>{ + this.$u.get(`/app/goods/list?pageNum=1&pageSize=999`).then(res =>{ if(res.code == 200){ this.products = res.data + // 确保每个商品都有categoryId和categoryName属性 + this.products.forEach((product, index) => { + // 如果商品没有categoryId,使用当前选中的分类ID + if (!product.categoryId && this.categoryId) { + product.categoryId = this.categoryId + } + // 确保categoryId是数字类型 + product.categoryId = parseInt(product.categoryId) || this.categoryId + + // 如果商品没有categoryName,从分类列表中查找 + if (!product.categoryName) { + const category = this.categories.find(cat => cat.id === product.categoryId) + if (category) { + product.categoryName = category.name + } + } + + // 清理分类名称,移除空格和特殊字符 + if (product.categoryName) { + product.categoryName = product.categoryName.trim() + } + + console.log(`商品${index}:`, product.name, '分类ID:', product.categoryId, '分类名称:', product.categoryName) + }) + console.log('商品列表:', this.products) + console.log('分类列表:', this.categories) + console.log('当前分类ID:', this.categoryId) } + }).catch(err => { + console.error('获取商品列表失败:', err) }) }, // 点击我已选好 @@ -398,11 +447,91 @@ import { number } from 'echarts' chooseSeat() { this.addmenflag = true }, - // 点击分类请求商品列表 - selectCategory(item,index) { - this.categoryId = item.id - this.currentCategoryIndex = index - this.getshoplist() + // 点击分类滚动到对应商品 + selectCategory(item, index) { + console.log('点击分类:', item.name, '分类ID:', item.id, '索引:', index) + + // 更新当前分类 + this.currentCategoryIndex = index + this.categoryId = item.id + + // 设置滚动标志,防止用户滚动事件干扰 + this.isScrolling = true + + // 如果是第一个分类,直接回到顶部 + if (index === 0) { + console.log('点击第一个分类,直接回到顶部') + this.scrollTop = 0 + setTimeout(() => { + this.isScrolling = false + }, 500) + return + } + + // 查找该分类下的第一个商品 + const firstProductOfCategory = this.products.find(product => + product.categoryName === item.name + ) + + if (firstProductOfCategory) { + // 先回到顶部,再滚动到目标位置 + this.scrollToCategoryWithReset(item.name) + } else { + console.log('未找到分类商品:', item.name) + // 即使没找到商品也要重置滚动状态 + setTimeout(() => { + this.isScrolling = false + }, 500) + } + }, + // 先回到顶部,再滚动到指定分类 + scrollToCategoryWithReset(categoryName) { + // 先回到顶部 + this.scrollTop = 0 + + // 延迟执行滚动到目标位置,确保回到顶部的动画完成 + setTimeout(() => { + this.scrollToCategory(categoryName) + }, 300) + }, + // 滚动到指定分类 + scrollToCategory(categoryName) { + const query = uni.createSelectorQuery().in(this) + query.selectAll('.product-item').boundingClientRect() + query.select('.product-list').boundingClientRect() + query.exec((res) => { + if (res && res[0] && res[1]) { + const items = res[0] // 商品项列表 + const container = res[1] // 滚动容器 + + // 找到该分类的第一个商品 + for (let i = 0; i < items.length; i++) { + const item = items[i] + if (item.dataset.categoryName === categoryName) { + // 计算正确的滚动位置 + // 由于已经回到顶部,现在item.top就是相对于容器的位置 + const targetScrollTop = Math.max(0, item.top - 20) + + console.log('滚动到分类:', categoryName, '位置:', targetScrollTop) + + // 设置滚动位置 + this.scrollTop = targetScrollTop + + // 延迟重置滚动状态 + // setTimeout(() => { + // this.isScrolling = false + // }, 1000) + + break + } + } + } else { + console.log('获取元素位置失败,重置滚动状态') + setTimeout(() => { + this.isScrolling = false + }, 500) + } + }) }, // 选择规格 selectSpec(groupId, optionId) { @@ -554,7 +683,64 @@ import { number } from 'echarts' // 点击显示购物车列表商品 btngw(){ this.gouwuflag = true - } + }, + // 商品列表滚动事件 + onProductScroll(event) { + // 如果正在程序滚动,不处理用户滚动 + if (this.isScrolling) { + return + } + + const scrollTop = event.detail.scrollTop + + // 清除之前的定时器 + if (this.scrollTimer) { + clearTimeout(this.scrollTimer) + } + + // 延迟执行,避免频繁触发 + this.scrollTimer = setTimeout(() => { + this.updateCurrentCategoryByScroll(scrollTop) + }, 200) + }, + // 根据滚动位置更新当前分类 + updateCurrentCategoryByScroll(scrollTop) { + const query = uni.createSelectorQuery().in(this) + query.selectAll('.product-item').boundingClientRect() + query.select('.product-list').boundingClientRect() + query.exec((res) => { + if (res && res[0] && res[1]) { + const items = res[0] + const container = res[1] + + // 找到当前可见的商品分类 + let currentCategoryName = null + const threshold = 100 // 可视区域阈值 + + for (let i = 0; i < items.length; i++) { + const item = items[i] + // 计算商品相对于容器的位置 + const itemTop = item.top - container.top + const itemBottom = itemTop + item.height + + // 如果商品在可视区域内 + if (itemTop <= threshold && itemBottom > threshold) { + currentCategoryName = item.dataset.categoryName + break + } + } + + // 更新当前分类索引 + if (currentCategoryName !== null) { + const categoryIndex = this.categories.findIndex(cat => cat.name === currentCategoryName) + if (categoryIndex !== -1 && categoryIndex !== this.currentCategoryIndex) { + this.currentCategoryIndex = categoryIndex + this.categoryId = this.categories[categoryIndex].id + } + } + } + }) + }, } } @@ -699,7 +885,6 @@ import { number } from 'echarts' .area-list { width: 200rpx; background: #1A1A1A; - // padding: 20rpx 0; .area-item { height: 80rpx; @@ -916,7 +1101,7 @@ import { number } from 'echarts' } .category-item { - padding: 15rpx 20rpx; + padding: 30rpx 20rpx; cursor: pointer; } @@ -932,8 +1117,9 @@ import { number } from 'echarts' .product-list { flex: 1; padding: 20rpx; - overflow-y: auto; background-color: #1A1A1A; + padding-bottom: 200rpx; + box-sizing: border-box; } .product-item { @@ -998,4 +1184,5 @@ import { number } from 'echarts' .product-actions button:disabled { opacity: 0.5; } - \ No newline at end of file + + diff --git a/page_user/pingzhuo/qunliao.vue b/page_user/pingzhuo/qunliao.vue index 2c42595..c55cf9a 100644 --- a/page_user/pingzhuo/qunliao.vue +++ b/page_user/pingzhuo/qunliao.vue @@ -41,23 +41,51 @@ 倡导聊天过程文明善意~ 谨慎判断切勿私下交易,谨防网络诈骗,切勿违法乱纪!!! - {{ messages.createTime }} - {{ messages.content }} - - - + + + + + 已发送 + + {{ messages.createTime }} + {{ messages.content }} + + + + + + + 已发送 + + {{ messages.createTime }} + + + + + + + + + 已发送 + + {{ messages.createTime }} + + - - {{ messages.content }} - {{ messages.content }} + + + {{ messages.createTime }} + + {{ messages.content }} + @@ -240,11 +268,42 @@ }) this.socketTask.onMessage((res) => { const message = JSON.parse(res.data) - this.$nextTick(() => { - this.messages.push(message) - console.log(message,this.messages,'asdasdasdas') - // this.scrollToBottom() - }) + console.log('收到WebSocket消息:', message) + console.log('当前用户ID:', this.userId) + console.log('消息发送者ID:', message.sendId) + + // 检查是否是自己发送的消息,如果是则不重复添加 + const isOwnMessage = this.messages.some(msg => + msg.tempId && + msg.sendId == this.userId && + message.sendId == this.userId && + msg.content === message.content && + msg.type === message.type + ) + + console.log('是否为自己的消息:', isOwnMessage) + + if (!isOwnMessage) { + this.$nextTick(() => { + this.messages.push(message) + console.log('添加新消息:', message) + // this.scrollToBottom() + }) + } else { + // 如果是自己的消息,更新状态为已发送 + const index = this.messages.findIndex(msg => + msg.tempId && + msg.sendId == this.userId && + msg.content === message.content && + msg.type === message.type + ) + console.log('找到匹配的消息索引:', index) + if (index !== -1) { + console.log('更新消息状态为已发送') + this.messages[index].status = 'sent' + this.sendingMessages.delete(this.messages[index].tempId) + } + } }) this.socketTask.onClose(() => { console.log('WebSocket连接已关闭') @@ -306,12 +365,18 @@ type: type, status: 'sending', tempId: tempId, - senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar' + senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar', + createTime: this.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss') } + + // 立即将消息添加到聊天列表中 + this.messages.push(tempMessage) this.sendingMessages.add(tempId) + this.$nextTick(() => { this.scrollToBottom() }) + let data = { teamId: this.teamId, content: this.inputContent, @@ -319,11 +384,17 @@ } this.$u.post(`/app/chat/sendMessage`, data).then(res => { if (res.code == 200) { - const index = this.messages.findIndex(msg => msg.tempId === tempId) - if (index !== -1) { - this.messages[index].status = 'sent' - } - this.sendingMessages.delete(tempId) + // 状态更新由WebSocket处理,这里不需要重复更新 + console.log('消息发送成功') + // 备用机制:如果3秒后WebSocket没有更新状态,则手动更新 + setTimeout(() => { + const index = this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1 && this.messages[index].status === 'sending') { + console.log('WebSocket未响应,手动更新状态为已发送') + this.messages[index].status = 'sent' + this.sendingMessages.delete(tempId) + } + }, 3000) } else { const index = this.messages.findIndex(msg => msg.tempId === tempId) if (index !== -1) { @@ -403,35 +474,67 @@ if (index !== -1) { this.messages[index].status = 'sending' this.sendingMessages.add(message.tempId) - let data = { - teamId: this.teamId, - content: message.content, - type: message.type - } - this.$u.post(`/app/chat/sendMessage`, data).then(res => { - if (res.code == 200) { - this.messages[index].status = 'sent' - this.sendingMessages.delete(message.tempId) - } else { + + // 根据消息类型处理重发 + if (message.type === 2 || message.type === 3) { + // 图片或视频消息,需要重新上传 + this.reuploadMedia(message, index) + } else { + // 文本消息,直接发送 + let data = { + teamId: this.teamId, + content: message.content, + type: message.type + } + this.$u.post(`/app/chat/sendMessage`, data).then(res => { + if (res.code == 200) { + this.messages[index].status = 'sent' + this.sendingMessages.delete(message.tempId) + } else { + this.messages[index].status = 'failed' + this.sendingMessages.delete(message.tempId) + uni.showToast({ + title: res.msg, + icon: 'none', + duration: 2000 + }) + } + }).catch(err => { this.messages[index].status = 'failed' this.sendingMessages.delete(message.tempId) uni.showToast({ - title: res.msg, + title: '发送失败,请重试', icon: 'none', duration: 2000 }) - } - }).catch(err => { - this.messages[index].status = 'failed' - this.sendingMessages.delete(message.tempId) - uni.showToast({ - title: '发送失败,请重试', - icon: 'none', - duration: 2000 }) - }) + } } }, + // 重新上传媒体文件 + reuploadMedia(message, index) { + // 这里需要根据实际情况处理,可能需要用户重新选择文件 + uni.showModal({ + title: '提示', + content: '媒体文件重发需要重新选择文件,是否重新选择?', + success: (res) => { + if (res.confirm) { + if (message.type === 2) { + this.chooseImage() + } else if (message.type === 3) { + this.takeVideo() + } + // 移除失败的消息 + this.messages.splice(index, 1) + this.sendingMessages.delete(message.tempId) + } else { + // 取消重发,恢复失败状态 + this.messages[index].status = 'failed' + this.sendingMessages.delete(message.tempId) + } + } + }) + }, // 获取七牛云token gettoken(){ this.$u.get(`/common/qiniuToken`).then(res => { @@ -465,6 +568,24 @@ type: 'all', success(res) { const tempFilePaths = res.tempFiles + // 立即添加一个发送中的消息 + const tempId = Date.now().toString() + const tempMessage = { + sendId: _this.userId, + content: tempFilePaths[0].path, // 临时路径 + type: 2, + status: 'sending', + tempId: tempId, + senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar', + createTime: _this.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss') + } + _this.messages.push(tempMessage) + _this.sendingMessages.add(tempId) + + _this.$nextTick(() => { + _this.scrollToBottom() + }) + wx.uploadFile({ url: 'https://up-z2.qiniup.com', name: 'file', @@ -476,9 +597,68 @@ success: function(res) { console.log(res, 'resres') let str = JSON.parse(res.data) - _this.inputContent = 'https://api.ccttiot.com/' + str.key - console.log(_this.inputContent) - _this.sendMessage(2) + const imageUrl = 'https://api.ccttiot.com/' + str.key + + // 更新消息内容为真实URL + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].content = imageUrl + } + + // 发送消息到服务器 + let data = { + teamId: _this.teamId, + content: imageUrl, + type: 2 + } + _this.$u.post(`/app/chat/sendMessage`, data).then(res => { + if (res.code == 200) { + console.log('图片消息发送成功') + // 备用机制:如果3秒后WebSocket没有更新状态,则手动更新 + setTimeout(() => { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1 && _this.messages[index].status === 'sending') { + console.log('WebSocket未响应,手动更新图片状态为已发送') + _this.messages[index].status = 'sent' + _this.sendingMessages.delete(tempId) + } + }, 3000) + } else { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].status = 'failed' + } + _this.sendingMessages.delete(tempId) + uni.showToast({ + title: res.msg, + icon: 'none', + duration: 2000 + }) + } + }).catch(err => { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].status = 'failed' + } + _this.sendingMessages.delete(tempId) + uni.showToast({ + title: '发送失败,请重试', + icon: 'none', + duration: 2000 + }) + }) + }, + fail(err) { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].status = 'failed' + } + _this.sendingMessages.delete(tempId) + uni.showToast({ + title: '上传失败,请重试', + icon: 'none', + duration: 2000 + }) } }) } @@ -496,6 +676,25 @@ compressed: true, // 压缩视频 success(res) { const tempFilePath = res.tempFilePath + + // 立即添加一个发送中的消息 + const tempId = Date.now().toString() + const tempMessage = { + sendId: _this.userId, + content: tempFilePath, // 临时路径 + type: 3, + status: 'sending', + tempId: tempId, + senderAvatar: uni.getStorageSync('user').avatar || 'https://api.ccttiot.com/smartmeter/img/static/default-avatar', + createTime: _this.$u.timeFormat(new Date(), 'yyyy-mm-dd hh:MM:ss') + } + _this.messages.push(tempMessage) + _this.sendingMessages.add(tempId) + + _this.$nextTick(() => { + _this.scrollToBottom() + }) + // 显示上传loading uni.showLoading({ title: '视频上传中...', @@ -512,15 +711,69 @@ success: function(uploadRes) { uni.hideLoading() let responseData = JSON.parse(uploadRes.data); - _this.inputContent = 'https://api.ccttiot.com/' + responseData.key + const videoUrl = 'https://api.ccttiot.com/' + responseData.key + + // 更新消息内容为真实URL + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].content = videoUrl + } + // 视频特殊处理:保存缩略图 if(res.thumbTempFilePath){ _this.uploadThumb(res.thumbTempFilePath) } - _this.sendMessage(3); // 类型改为3表示视频 + + // 发送消息到服务器 + let data = { + teamId: _this.teamId, + content: videoUrl, + type: 3 + } + _this.$u.post(`/app/chat/sendMessage`, data).then(res => { + if (res.code == 200) { + console.log('视频消息发送成功') + // 备用机制:如果3秒后WebSocket没有更新状态,则手动更新 + setTimeout(() => { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1 && _this.messages[index].status === 'sending') { + console.log('WebSocket未响应,手动更新视频状态为已发送') + _this.messages[index].status = 'sent' + _this.sendingMessages.delete(tempId) + } + }, 3000) + } else { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].status = 'failed' + } + _this.sendingMessages.delete(tempId) + uni.showToast({ + title: res.msg, + icon: 'none', + duration: 2000 + }) + } + }).catch(err => { + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].status = 'failed' + } + _this.sendingMessages.delete(tempId) + uni.showToast({ + title: '发送失败,请重试', + icon: 'none', + duration: 2000 + }) + }) }, fail(err) { uni.hideLoading() + const index = _this.messages.findIndex(msg => msg.tempId === tempId) + if (index !== -1) { + _this.messages[index].status = 'failed' + } + _this.sendingMessages.delete(tempId) uni.showToast({ title: '上传失败', icon: 'none' @@ -604,27 +857,51 @@ padding: 15rpx 20rpx; border-radius: 10rpx; max-width: 70%; - position: relative; + display: flex; + flex-direction: column; + gap: 8rpx; - .message-status { - position: absolute; - right: -40rpx; - bottom: 0; + .message-header { display: flex; align-items: center; + gap: 10rpx; + flex-shrink: 0; - .status-icon { - width: 32rpx; - height: 32rpx; + .message-status { + display: flex; + align-items: center; - &.sending { - animation: rotate 1s linear infinite; + .status-icon { + width: 24rpx; + height: 24rpx; + + &.sending { + animation: rotate 1s linear infinite; + } + + &.failed { + opacity: 0.7; + } } - &.failed { - opacity: 0.7; + .status-text { + font-size: 18rpx; + color: #999; + white-space: nowrap; } } + + .message-time { + font-size: 18rpx; + color: #999; + white-space: nowrap; + } + } + + .message-text { + word-break: break-all; + line-height: 1.4; + margin-top: 4rpx; } } .message-image { @@ -655,6 +932,28 @@ padding: 15rpx 20rpx; border-radius: 10rpx; max-width: 70%; + display: flex; + flex-direction: column; + gap: 8rpx; + + .message-header { + display: flex; + align-items: center; + gap: 10rpx; + flex-shrink: 0; + + .message-time { + font-size: 18rpx; + color: #999; + white-space: nowrap; + } + } + + .message-text { + word-break: break-all; + line-height: 1.4; + margin-top: 4rpx; + } } .message-image { max-width: 400rpx; diff --git a/pages/index/index.vue b/pages/index/index.vue index 1c901bb..091c112 100644 --- a/pages/index/index.vue +++ b/pages/index/index.vue @@ -13,10 +13,6 @@ - @@ -32,9 +28,7 @@ } }, onLoad(option) { - console.log(option, uni.getStorageSync('type')); if (uni.getStorageSync('type')) { - console.log(1); uni.removeStorageSync('type') } else { xBlufi.initXBlufi(1) diff --git a/pages/myorder/returned/index.vue b/pages/myorder/returned/index.vue index b0200ea..77fcec7 100644 --- a/pages/myorder/returned/index.vue +++ b/pages/myorder/returned/index.vue @@ -28,14 +28,12 @@ -